【Flutter 基础】Hello World

645 阅读11分钟

注:本文从个人公众号(岛前屿端)中迁移重新发布

Flutter 是谷歌的移动 UI 框架,可以从单个代码库快速的为移动端(iOS & Android)、Web、桌面端、嵌入式设备上构建高质量的原生用户界面和应用程序。


在说完 环境及配置开发环境/创建虚拟机 之后那我们就可以来看看它的真正面目,是时候揭开它的神秘面纱了。

创建 flutter 项目

不知是否还记得之前的创建 flutter 项目的命令?

flutter create myapp

这里我们创建一个 名叫 myappflutter 项目。(别忘了要先进入你的工作文件夹)

image.png
(创建 flutter 项目 myapp)

稍等一下,等待项目创建完成……

这里创建项目会被分为 3部分执行:

  1. create 部分 创建所需的文件结构、文件、代码以及编辑器所需配置信息。(66个文件)
  2. 文件部分创建完成后会执行 flutter pub get 命令来更新所需依赖。
  3. 依赖更新完成后,会执行检查开发环境,检查完成后提示 cd myapp & flutter run 来运行你的应用。

image.png
(创建 flutter 项目完成)

项目结构

Ok,创建完成后,就简单说一下项目结构吧。

image.png
(flutter 项目结构)

文件夹里更具体的内容就需要自己探索了,我就不细说了。如果要全部说一遍那就太长了。

对了,部分代码里会有 Do not edit. 的注释,这就需要自己发现这个彩蛋了。

做为 flutter 开发还是要关注重点的,那就是 lib 文件夹。这里是主要的编码目录,我们编写的代码也是放在这个目录下。该目录会有默认的一个入口文件 ,main.dart 通过这个文件后缀就可以知道 flutter 主要是使用 dart 语言来进行编写。

wecom1.gif
(myapp 的默认 main.dart)

当你一打开这个 main.dart 文件就会感觉 “哎哟,这么长这么多,看不懂啊!”

那我就先把这些注释删除……

image.png
(删除注释的 main.dart)

这还是好长啊……

没关系,如果你之前看过 flutter 中文网的话,代码编写部分会有让你删除 main.dart 的内容。然后复制教程提供的代码运行得到 hello world。

从 0 开始

这时你会想,这就完事了?

当然没完!

我是来学习的,不能复制粘贴一把梭就敷衍了事了。

注意:这里以我的为例,以后均以此配置和环境为基础。

操作系统: Window 10
编辑器: Visual Studio Code
插件: Flutter(自带安装Dart SDK),yaml
选装插件: Java or C/C++
工具: Flutter 依赖 Git for Windows (Git命令行工具)
设备: 虚拟机 or 真机
项目类型: Flutter
项目名称: myapp
编程语言: Dart

main

既然是要学习,那就要拿出学习的精神来,从 0 开始!

所以…… 我们还是把代码删了吧。

image.png

大侠且慢,别打脸!且听我说……

因为默认的例子和中文网教程虽然是可以运行了,但是我觉得还是不够详细。所以既然是要学习的话,为什么不从 0 开始学习,一点一点来把他搞懂来呢?对吧?

现在开始进入正题,代码都清空了之后怎么办?

不要着急清空代码先,新建文件 lib\main1.dart 把中文网的代码复制过去再清空,这样一来有个参照理解对吧?

就像这样。

image.png
(复制参照代码)

先说第一行,有点基础的同学们都知道这是引入一个东西,但具体是什么东西呢?我就要来解释一下啦:

// 引入 Material Design 设计语言(基于 Dart 的 flutter 版本)
import 'package:flutter/material.dart';

解释:

Material Design 设计语言 (基于 Dart 的 flutter 版本),Material Design 是由 Google 推出的全新的设计语言。谷歌表示,这种设计语言旨在为手机、平板电脑、台式机和“其他平台”提供更一致、更广泛的“外观和感觉”。其他人怎么翻译我不太清楚,但是我认为中文可以翻译为 ** “质感设计”** 。

(这么长记不住怎么办?!)

记不住没关系,这时候我们就要善用 VS Code 或其他编辑器工具的提示功能了。只要输入如下内容就会有提示了,这时候只要选第一行就Ok啦!

import 'pfm';

image.png
(引入包提示)

接下来会看到这一句:

// 每个应用都需要有个顶级的 main() 入口函数才能执行。
// main() 函数的返回值为 void
void main() => runApp(new MyApp());

main 是 dart 的顶级函数,也是入口。

箭头函数?这个箭头函数和 JavaScript 的箭头函数不太一样。在还没深入理解Dart语法的时候我们尽量不要使用较为高级的用法。还原如下:

void main() {
  runApp(new MyApp());
}

这样看起来是不是就比较熟悉了?

这里还有调用了一个 runApp 的函数,这是什么东西?

runApp

runApp 是 Flutter 的入口函数,所以如果要使用的话那就必须调用 runApp 才能启动 flutter 项目,不然的话就会报错了。

image.png
(runApp 提示)

这里代码提示还告诉我们 runApp 函数仅接受一个叫 Widget 的参数,这个又是什么东西?

Flutter 中文网 - Widget 框架概述

Flutter Widget 采用现代响应式框架构建,这是从 React 中获得的灵感,中心思想是用 widget 构建你的UI。Widget描述了他们的视图在给定其当前配置和状态时应该看起来像什么。当 widget 的状态发生变化时,widget 会重新构建 UI,Flutter 会对比前后变化的不同, 以确定底层渲染树从一个状态转换到下一个状态所需的最小更改(译者语:类似于React/Vue中虚拟DOM的diff算法)。

那现在应该就能理解 Widget 了,Widget 是一个用来构建UI的框架,则 runApp 函数接受给定的 Widget 并使其成为 Widget 树的根。

关于 Widget 框架的内容:Widgets 目录

了解了 main、runApp、Widget 之后我们就可以再次改写代码:

void main() {
  runApp(
    Center( // 居中
      child: Text( // 文字对象
        'Hello World!', // 文字内容
        textDirection: TextDirection.ltr, // 文字输出方向
        // textDirection 在使用虚拟设备时需要写明,不然无法编译通过。
        // 使用真实设备 或 Material 时无需写明文本方向,均会自动处理。
      ), 
    ),
  );
}

启动项目后就可以看到一个无 Material 的 hello world

image.png
(无 Material 的 hello world)

恭喜你,你已经可以对外宣称 “精通 flutter hello world 的编写”* 。

接下来我们继续往下看,代码中定义了一个叫 MyApp 的类。

这时候你以为我的标题要写 MyApp

State x Widget

不好意思,你猜错了!因吹斯汀!

MyApp 它继承自 StatelessWidget 。所以我要说的是关于继承的这个东西。

Flutter 中文网 - Widget 框架概述

在编写应用程序时,通常会创建新的 widget,这些 widget 是无状态的 StatelessWidget 或者是有状态的 StatefulWidget, 具体的选择取决于您的 widget 是否需要管理一些状态。

widget 的主要工作是实现一个 build 函数,用以构建自身。

换句话说 StatelessWidgetStatefulWidget 都是 Widget 的抽象类。唯一不同的是:

  • StatelessWidget 是无状态的,意味着无法通过数据变更而更新。
  • StatefulWidget 是有状态的,意味着可以通过数据变更而更新,需要通过setState 来管理状态。

由于 StatelessWidget 和 StatefulWidget 都是 Widget 的抽象类。

所以我们在使用的时候就需要重写 Widget 类来实现具体的代码和逻辑。重写 Widget 时我们就需要用到 @override 来装饰需要重写的部分。(@override 装饰关键字,如果对于后端语言不熟悉的少侠们那就需要动手查找资料啦,不然说起来就太长了)

参照右边代码的话这时我们就可以正式使用 MaterialApp 了。

// 继承自 StatelessWidget
class MyApp extends StatelessWidget {
  // 重写 Widget 类实现
  @override
  // Widget 实现构建 build 函数
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp();
  }
}

保存之后,在控制台中按下 r 命令 键就能看到效果了!!!

一个红彤彤的的错误警告信息。

什么?你居然不知道 r 命令 键什么意思?那你现在还不立刻马上赶紧回去复习?!!复习传送门

image.png
(错误警告信息 - MaterialApp)

MaterialApp

少侠莫慌,不要害怕!我来告诉你这是怎么回事。让我们来先看一下 MaterialApp 的源码部分。

image.png
(MaterialApp 部分源码)

唉?这个1234 是不是很熟悉啊?是不是好像哪里见过?

唉~没错!,就是刚刚错误警告信息里的1234,只不过屏幕太小没有显示完全。这里我们看到最后一行的说明:

If [home], [routes], [onGenerateRoute], and [onUnknownRoute] are all null, and [builder] is not null, then no [Navigator] is created.

翻译:

如果 [home], [routes], [onGenerateRoute][onUnknownRoute] 都为 null 并且 [builder] 不为 null 那么将不会创建 [Navigator]

这里还是没说为什么啊。那我们继续往下看……

image.png
(MaterialApp 部分源码)

我们来看,注意这一句话:

Creates a MaterialApp.
At least one of [home], [routes], [onGenerateRoute], or [builder] must be non-null.

翻译:

创建 MaterialApp
[home][routes][onGenerateRoute][builder] 中的至少一个必须为非 null。

那现在应该了解了。我们缺少了必要的代码实现,那就先来个最简单的 [home],所以可以将代码修改为:

// 继承自 StatelessWidget
class MyApp extends StatelessWidget {
  // 重写 Widget 类实现
  @override
  // Widget 实现构建 build 函数
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      // 实现 home 函数
      home: Center( // 居中
        child: Text('hello world'), // 文字内容
      ),
    );
  }
}

保存代码,然后 R 命令 一下。 (注意:我没有打错字,因为对代码的渲染结构进行了更改,所以需要使用 R)

什么?你居然不知道 R 键什么意思?那你现在还不立刻马上赶紧回去复习?!!

什么?你居然还分不清 rR 的区别?!!你取关吧!

image.png
(使用 material 的 hello world)

哇!怎么这么丑?谁家APP界面会长这样啊?

使用主题

少侠不要着急嘛,再说了 “罗马都不是一天建成的” 要打好基础循序渐进。

参照右边的代码会发现这里 用 Scaffold 来实现了 home 函数。我们在来看看源码,看看这到底是个什么东西:

image.png
(Scaffold 部分源码)

我们来看到这一句:

Creates a visual scaffold for material design widgets.
为“质感设计”部件创建一个可视化的支架。

噢(恍然大悟)~原来这才是 UI 的框架啊?

也不全对,我的理解应该是这样

  • Material 是设计规范 (标准)
  • Scaffold 是实现了设计规范的可视化支架 (标准实现)
  • Widgets 是 UI 框架,但实现了更具体的一些的对象,例如文本框(Text),行(Row),列(Colum)等 (具体实现)

所以只要 Widget 提供了所需的内容给 Scaffold,那么 Scaffold 就会实现为对应的设计标准 Material

所以最终的代码和运行结果:

import 'package:flutter/material.dart';

void main() {
  runApp (MyApp());
}

// 继承自 StatelessWidget
class MyApp extends StatelessWidget {
  // 重写 Widget 类实现
  @override
  // Widget 实现构建 build 函数
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold( // 使用 Scaffold 实现 home 函数
        // 导航栏(顶部)显示文字
        appBar: AppBar( // 导航栏(顶部)
          title: Text('Welcome to Flutter'), // 文字内容
        ),
        
        // Scaffold 的容器 body
        body: Center( // 在容器中居中显示文字
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

image.png
(flutter - hello world)

完美!


总结

  • 在学习一门新的编程语言或者框架时,切勿着急囫囵吞枣,复制粘贴一把梭只能让你学会“形”而学不会“意”
  • 知道为什么,才能更好的在实践当中去运用。
  • 官方文档永远是最好的入门资料之一。官网是英文的怎么办?国内 IT 行业已经发展了多年,中文社区大概率是会有的。
  • 学好英文,太前沿的领域和内容就需要自己去探索了。

配合文章一同食用的代码已同步更新到 Github 地址:github.com/linxsbox/my…

前置阅读:

参考