Flutter
Table of Contents
1. Flutter 简介
Flutter 是 Google 开发的一套全新的跨平台、开源 UI 框架,支持 Android、iOS 系统开发。
参考:
Flutter widget: Flutter widget index
Flutter API:Flutter API reference documentation
Flutter Samples:https://flutter.github.io/samples
在已有项目上进行混合开发,可参考:Add Flutter to existing app
1.1. 架构简介
1.2. 环境安装
下面以 Mac 系统为例,介绍一下 Flutter 环境的安装。
下载 Flutter SDK:
$ curl -O https://storage.googleapis.com/flutter_infra/releases/stable/macos/flutter_macos_v1.7.8+hotfix.4-stable.zip
解压到 ~/development 目录中:
$ cd ~/development $ unzip ~/Downloads/flutter_macos_v1.7.8+hotfix.4-stable.zip
设置 PATH 环境变量:
$ export PATH="$PATH:${HOME}/development/flutter/bin"
提前下载开发过程中可能用到的二进制文件:
$ flutter precache --verbose
检测是否存在问题:
$ flutter doctor --verbose
注意观察上面命令输出的问题,进行下一步操作前先解决它们。
1.3. 第一个 Flutter 项目
创建 Flutter 项目:
$ flutter create myapp # 会创建样板程序,主要源码在 lib/main.dart 中 $ cd myapp
先确保你的模拟器已经启动(注:终端中执行 open -a Simulator 可以启动 iOS 模拟器)。可以通过下面命令检测模拟器是否启动(输出显示已经启动了 Android 模拟器):
$ flutter devices # 列出目前的设备 1 connected device: Android SDK built for x86 • emulator-5554 • android-x86 • Android 9 (API 28) (emulator)
然后执行下面命令即可在模拟器中运行项目:
$ flutter run # 运行项目,如果有多个可用设备, 可以使用 -d 指定设备
项目成功运行后,会显示如图 2 所示的界面。每点击一下右下角的按钮,屏幕中间的数字就会加一,这个项目的分析可以参考节 3.1。

Figure 2: 第一个 Flutter 项目
参考:https://flutter.dev/docs/get-started/test-drive?tab=terminal
1.4. 生成 Android Apk
执行 flutter build apk 可以生成 Android apk,第一次时间可以比较长,请启用 --verbose 选项以查看进度:
$ flutter build apk --verbose # 生成 Android apk
1.4.1. 不依赖 Android Studio 生成 Apk
要在服务器上自动打包 flutter 项目为 Android Apk,但服务器没有安装 X 环境,无法使用 Android Studio。下面介绍不依赖 Android Studio 情况下,编译 flutter 项目的步骤。
第一步,安装 Android SDK。
先安装“Command line tools”,再使用其中包含的工具 sdkmanager 来安装 Android SDK。
下面步骤将安装“Command line tools”,并将其解压到目录 ${HOME}/android-sdk/ 中。
$ mkdir ${HOME}/android-sdk/
$ cd ${HOME}/android-sdk/
$ curl -O https://dl.google.com/android/repository/commandlinetools-linux-5842447_latest.zip
$ unzip ~/commandlinetools-linux-5842447_latest.zip
$ ls -F ${HOME}/android-sdk/
tools/ commandlinetools-linux-5842447_latest.zip # 可以看到 zip 解压后的内容都在 tools 目录中
安装完成后,设置下面环境变量:
# Android HOME
export ANDROID_HOME=${HOME}/android-sdk
export PATH=$ANDROID_HOME/tools:$PATH
export PATH=$ANDROID_HOME/tools/bin:$PATH
export PATH=$ANDROID_HOME/platform-tools:$PATH
再通过 sdkmanager 安装 Android SDK(下面安装的是 Android-29,如果有更新的版本,你可以安装更新的版本):
$ sdkmanager --sdk_root=${HOME}/android-sdk "platforms;android-29"
$ sdkmanager --sdk_root=${HOME}/android-sdk "platform-tools"
$ sdkmanager --sdk_root=${HOME}/android-sdk "patcher;v4"
$ sdkmanager --sdk_root=${HOME}/android-sdk "build-tools;29.0.2"
安装完成后,执行下面命令以接受所有 Licenses:
$ sdkmanager --licenses
第二步,安装 Flutter SDK。
前面已经介绍过,这里省略,假定 flutter 命令已经可用。
执行下面命令在 Flutter 中配置 Android SDK 的位置:
$ flutter config --android-sdk ${HOME}/android-sdk
现在环境都已经准备好,进入到 Flutter 项目根目录中,运行 flutter build apk --verbose 即可打包项目为 Android Apk 了。
参考:https://dev.to/jay_js/setting-up-flutter-without-android-studio-olo
2. Hello World 程序分析
下面是 Flutter 版本的 Hello World 程序(把 flutter create myapp 生成的 lib/main.dart 改为下面内容即可):
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}
说明:
- 上面例子中,创建了一个 MaterialApp ,Material 是 Google 推出的应用程序视觉设计语言,Flutter 中提供了大量的 Material Widget。
- 这个应用程序扩展自 StatelessWidget, 在 Flutter 中,几乎所有东西都是 Widget,如 alignment, padding, and layout 都是 Widget。
- Scaffold Widget 来自于 Material 库,可以认为是一个脚手架,它提供了侧边栏,title,还有底部的 sheets 等等。
- Widget 的主要工作都在其
build()方法中。 - 例子中的 body 包含了一个 Center Widget(用于居中布局),而 Center Widget 包含了一个 Text Widget(用于显示文本)。
上面代码中,语法 MaterialApp(title: xx, home: yy) 是调用 MaterialApp 类的构造函数,这个构造函数采用了 Named parameters ,所以调用时要指定参数名称(如 title,home 等等)。在 Dart 2.0 中,调用构造函数时关键字 new 可以省略,如果不省略(即例子中 MaterialApp/Scaffold/AppBar/Text/Center 前面都增加一个 new 关键字),则看起来很繁琐。
MaterialApp 的构造函数还有很多其它参数,通过设置它们,可以改变其行为。例如指定参数 theme 可改变 MaterialApp 的主题颜色(如红色):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello World'),
),
),
theme: ThemeData( // 和前面相比,增加了从本行开始往下的三行代码
primaryColor: Colors.red,
),
);
}
}
执行上面代码,得到图 3 所示程序,它的主题颜色改为了红色(不再是默认的蓝色)。

Figure 3: Hello World(主题颜色改为了红色)
3. Widgets
Flutter 中,几乎一切都是 Widget,Widget 采用现代“响应式”框架构建,借鉴于前端框架 React。其中心思想是用 Widget 构建你的 UI。 Widget 描述了他们的视图在给定其当前配置和状态时应该看起来像什么,当 Widget 的状态发生变化时,Widget 会重新构建 UI,Flutter 会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改(注:类似于 React/Vue 中虚拟 DOM 的 diff 算法)。
当我们编写应用程序时,往往是创建新的 Widget,它继承于 StatelessWidget(无状态组件)或者 StatefulWidget(有状态组件,即可以包含变化的数据)。Widget 的主要工作都在其 build() 函数中,在这个函数中定义其所包含的子组件。
StatelessWidget 的使用实例可以参考前面介绍的 Hello World,下面将介绍 StatefulWidget 的使用实例。
3.1. StatefulWidget
Flutter 中用 StatefulWidget 来表示“有状态组件”。下面就是使用命令 flutter create myapp 创建的项目的主要源码:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
MyHomePage 有状态,它继承于 StatefulWidget。你可能会奇怪,为什么 StatefulWidget (MyHomePage) 和 State (_MyHomePageState) 被分为两个组件。 在 Flutter 中, StatefulWidget 和 State 具有不同的生命周期, Widget 是临时对象,用于构建应用程序的当前展示形式;而 State 对象是持久的,其数据在其 build() 方法多次调用之间不会丢失。
对状态的修改必须在 setState() 中进行,随后 build() 会自动执行。Flutter 中的状态控制如图 4 所示。

Figure 4: State Lifecycle
4. Layouts
前面提到过,在 Flutter 中,几乎一切都是 Widget,布局类也是 Widget。

Figure 5: 3 icons with a label under each one

Figure 6: 3 icons with a label under each one
其相应的 Widget 树如图 7 所示。

Figure 7: Diagram of the widget tree
Container 是一个重要的布局类,支持 padding, margins, borders, or background color 等等。
