编写你的第一个 Flutter 应用,第一部分
— 焉知非鱼Flutter 是 Google 的 UI 工具包,用于从单一代码库中为手机、网页和桌面构建漂亮的、原生编译的应用程序。Flutter 可以与现有的代码一起使用,被世界各地的开发者和组织使用,并且是免费和开源的。
介绍 #
Flutter 是 Google 的 UI 工具包,用于从单一代码库中为手机、网页和桌面构建漂亮的、原生编译的应用程序。Flutter 可以与现有的代码一起工作,被世界各地的开发者和组织使用,并且是免费和开源的。
在这个代码实验室中,你将创建一个简单的手机 Flutter 应用。如果你熟悉面向对象的代码和基本的编程概念-如变量、循环和条件, 那么你就可以完成这个 codelab。你不需要以前有 Dart、手机或 Web 编程的经验。
你将在第1部分学到什么 #
- 如何编写一款在 iOS、Android 和 Web 上看起来很自然的 Flutter 应用?
- Flutter 应用程序的基本结构。
- 寻找和使用包来扩展功能。
- 使用热重装来加快开发周期。
- 如何实现一个有状态的小组件。
- 如何创建一个无限的、懒加载的列表。
在这个 codelab 的第2部分中,你将添加交互性,修改应用程序的主题,并添加导航到新页面的能力(在 Flutter 中称为路由)。
你将在第1部分中构建什么 #
你将实现一个移动应用,为一家创业公司生成建议的名字。用户可以选择和取消选择名字,保存最好的名字。代码一次懒惰地生成10个名字。随着用户的滚动,会生成更多的名字。用户可以滚动的范围没有限制。
下面的 GIF 动画显示了应用程序在完成部分时的工作情况。
设置你的 Flutter 环境 #
你需要两个软件来完成这个实验室-Flutter SDK和一个编辑器。(codelab 假设你使用 Android Studio,但你可以使用你的首选编辑器。)
你可以通过使用以下任何设备来运行 codelab。
- 一个物理的 Android 或 iOS 设备连接到你的计算机并设置为开发者模式。
- iOS 模拟器(需要安装 Xcode 工具)
- 安卓模拟器(需要在 Android Studio 中进行设置)
- 浏览器(调试时需要使用 Chrome 浏览器)
如果你想编译你的应用程序以在 web 上运行,你必须启用此功能(目前处于测试阶段)。要启用 web 支持,请使用以下说明。
flutter channel beta
flutter upgrade
flutter config --enable-web
你只需要运行一次 config
命令。启用 Web 支持后,你创建的每个 Flutter 应用也会为 Web 编译。在你的 IDE 的设备下拉菜单下面,或者在命令行使用 flutter devices
,你现在应该看到 Chrome 和 Web 服务器被列出。Chrome 设备会自动启动 Chrome。Web 服务器会启动一个托管应用程序的服务器,这样你就可以从任何浏览器加载它。在开发过程中使用 Chrome 设备,以便你可以使用 DevTools,而当你要在其他浏览器上进行测试时使用 Web 服务器。有关更多信息,请参阅使用 Flutter 构建 Web 应用程序和在 Web 上编写你的第一个 Flutter 应用程序。
创建 Flutter 应用程序的启动器 #
通过使用创建应用程序中的说明来创建一个简单的、模板化的 Flutter 应用程序。输入 startup_namer
(而不是 flutter_app
)作为项目名称。您将修改启动器应用程序来创建完成的应用程序。
提示:如果你在 IDE 中没有看到能够启动一个新的 Flutter 项目作为一个选项,那么请确保你已经安装了 Flutter 和 Dart 的插件。
你将主要编辑 lib/main.dart
,Dart 的代码就在这里。
替换 lib/main.dart
的内容。
删除 lib/main.dart
中的所有代码,并用下面的代码替换,在屏幕中央显示 “Hello World”。
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: const Text('Welcome to Flutter'),
),
body: const Center(
child: const Text('Hello World'),
),
),
);
}
}
提示:当把代码粘贴到你的应用程序中时,缩进会变得歪斜。你可以用以下 Flutter 工具来解决。
- Android Studio/IntelliJ IDEA: 右键点击 Dart 代码,选择用 dartfmt 重格式代码。
- VS code: 右键点击并选择格式化文档。
- 终端: 运行
flutter format <文件名>
。
运行应用程序。您应该看到 Android,iOS 或 Web 输出,取决于您的设备。
安卓系统:
iOS:
小贴士:第一次在物理设备上运行时,可能需要一段时间来加载。之后,你可以使用热重载来快速更新。在支持的 IDE 中,如果应用正在运行,Save 也会执行热重载。当使用 flutter run
直接从控制台运行应用程序时,输入 r
来执行热重载。
观察:
- 这个例子创建了一个 Material 应用。Material 是一种视觉设计语言,是移动和 Web 的标准。Flutter 提供了一套丰富的 Material 部件。
main
方法使用箭头(=>
)符号。对单行函数或方法使用箭头符号。- 应用程序扩展了
StatelessWidget
,这使得应用程序本身成为一个组件。在 Flutter 中,几乎所有的东西都是组件,包括对齐、填充和布局。 Scaffold
组件来自 Material 库,它提供了一个默认的应用栏、一个标题和一个 body 属性,其中存放着主屏幕的组件树。组件子树可以相当复杂。- 组件的主要工作是提供一个
build
方法,描述如何用其他低级组件来显示该组件。 - 本例的主体由包含
Text
子部件的Center
部件组成。Center
组件将其组件子树对齐到屏幕的中心。
使用外部软件包 #
在这一步中,您将开始使用一个名为 english_words
的开源包,它包含了几千个最常用的英语单词,还有一些实用函数。
你可以在 pub.dev 找到 english_words
包,以及许多其他开源包。
pubspec 文件管理着 Flutter 应用的资产。在 pubspec.yaml
中,附加 english_words: ^3.1.5
(english_words
3.1.5 或更高)到依赖列表中。
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
english_words: ^3.1.5 # add this line
在 Android Studio 的编辑器视图中查看 pubspec 时,点击 Packages get。这将把包拉到你的项目中。你应该在控制台中看到以下内容。
flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0
执行 Pub get
也会自动生成 “pubspec.lock” 文件,其中包含所有拉入项目的包的列表和它们的版本号。
在 lib/main.dart
中,导入新包:
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart'; // Add this line.
当你输入时,Android Studio 会给你建议导入的库。然后,它将导入的字符串渲染成灰色,让你知道导入的库是未使用的(到目前为止)。
接下来,你将使用 english_words
包来生成文本,而不是使用 “Hello World”。
做以下修改。
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordPair = WordPair.random(); // Add this line.
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
//child: Text('Hello World'), // Replace this text...
child: Text(wordPair.asPascalCase), // With this text.
),
),
);
}
}
提示: Pascal 大小写(也称为上驼形大小写)意味着字符串中的每个单词,包括第一个单词,都以大写字母开头。所以,uppercamelcase
就变成了 UpperCamelCase
。
如果应用程序正在运行,热重载来更新正在运行的应用程序。(在命令行中,你可以输入 r
来热重载。)每次点击热重载或保存项目时,你应该会在运行中的应用程序中看到一个不同的单词对,随机选择。这是因为单词对是在 build
方法里面生成的,每次 MaterialApp
需要渲染时,或者在 Flutter Inspector 中切换 Platform 时,都会运行该方法。
Android:
iOS:
有问题? #
如果您的应用程序没有正确运行,请查找错别字。如果需要,请使用以下链接中的代码来恢复正常。
添加一个有状态的组件 #
无状态组件是不可改变的,这意味着它们的属性不能改变-所有值都是最终值。
有状态组件维护的状态可能在组件的生命周期内发生变化。实现一个有状态的组件至少需要两个类。1) 一个 StatefulWidget,它可以创建一个 State 类的实例。StatefulWidget
对象本身是不可变的,可以被丢弃和再生,但 State
对象会在 widget 的生命周期内持久存在。
在这一步骤中,您将添加一个有状态的组件 RandomWords
,并创建其 State
类 _RandomWordsState
。然后,您将在现有的 MyApp 无状态组件中使用 RandomWords
作为子类。
为有状态组件创建模板代码。
它可以放在 MyApp
以外的文件中的任何位置,但解决方案将其放在文件的底部。在 lib/main.dart
中,将光标定位在所有代码之后,输入回车键几次,重新开始一行。在你的 IDE 中,开始输入 stful
。编辑器会询问你是否要创建一个 Stateful
的组件。按回车键接受。两个类的模板代码出现了,光标定位让你输入无状态组件的名称。
输入 RandomWords
作为您的小组件的名称。
正如您在下面的代码中所看到的,RandomWords
组件除了创建它的 State
类之外,几乎没有其他的功能。
一旦您输入 RandomWords
作为有状态组件的名称,IDE 会自动更新相应的 State
类,将其命名为 _RandomWordState
。默认情况下,State
类的名称是以下划线为前缀的。在标识符前加上下划线可以加强 Dart 语言的隐私性,也是 State
对象的最佳实践。
IDE 也会自动更新 State 类以扩展 State<RandomWords>
,表明你正在使用一个专门用于 RandomWords
的通用 State 类。应用程序的大部分逻辑都在这里-它为 RandomWords
组件维护状态。这个类保存了生成的词对列表,随着用户的滚动而无限增长,在本实验室的第二部分中,当用户通过切换心形图标从列表中添加或删除这些词对时,该类会对其进行收藏。
现在两个类的外观如下:
class RandomWords extends StatefulWidget {
@override
_RandomWordsState createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
return Container();
}
}
更新 _RandomWordsState
中的 build()
方法。
用以下两行替换 return Container();
:
class _RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
final wordPair = WordPair.random(); // NEW
return Text(wordPair.asPascalCase); // NEW
}
}
通过以下修改,删除 MyApp
中的文字生成代码:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final wordPair = WordPair.random(); // DELETE
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
//child: Text(wordPair.asPascalCase), // REPLACE with...
child: RandomWords(), // ...this line
),
),
);
}
}
热重载应用程序。应用程序应该像以前一样,每次热重载或保存应用程序时都会显示一个单词配对。
提示:如果您在热重载时看到警告,表明您可能需要重新启动应用程序,您应该考虑重新启动应用程序。这可能是一个假阳性,但重启可以确保您的更改反映在应用程序的 UI 中。
遇到问题了? #
如果您的应用程序没有正确运行,您可以使用以下链接中的代码来恢复正常。
创建一个无限滚动的 ListView #
在这一步中,您将展开 _RandomWordsState
来生成并显示单词配对列表。随着用户的滚动,列表(显示在 ListView 小组件中)会无限增长。ListView 中的构建器工厂构造函数允许你按需懒惰地构建一个列表视图。
在 _RandomWordState
类中添加一些状态变量。
增加一个 _suggestions
列表,用于保存建议的单词配对。另外,添加一个 _biggerFont
变量,用于使字体大小变大。
class _RandomWordsState extends State<RandomWords> {
final List<WordPair> _suggestions = <WordPair>[]; // NEW
final TextStyle _biggerFont = const TextStyle(fontSize: 18); // NEW
...
}
接下来,你将在 _RandomWordsState
类中添加一个 _buildSuggestions()
函数。这个方法可以构建显示建议词对的 ListView
。
ListView
类提供了一个构建器属性 itemBuilder
,它是一个工厂构建器和回调函数,指定为一个匿名函数。两个参数被传递给函数–BuildContext
和行迭代器 i
。迭代器从0开始,每次调用函数时递增,每一个建议的单词配对都会递增一次。这个模型允许建议列表在用户滚动时继续增长。
添加整个 _buildSuggestions
函数。
在 _RandomWordsState
类中,添加以下函数,如果你喜欢,请删除注释:
Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16),
// The itemBuilder callback is called once per suggested
// word pairing, and places each suggestion into a ListTile
// row. For even rows, the function adds a ListTile row for
// the word pairing. For odd rows, the function adds a
// Divider widget to visually separate the entries. Note that
// the divider may be difficult to see on smaller devices.
itemBuilder: (BuildContext _context, int i) {
// Add a one-pixel-high divider widget before each row
// in the ListView.
if (i.isOdd) {
return Divider();
}
// The syntax "i ~/ 2" divides i by 2 and returns an
// integer result.
// For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
// This calculates the actual number of word pairings
// in the ListView,minus the divider widgets.
final int index = i ~/ 2;
// If you've reached the end of the available word
// pairings...
if (index >= _suggestions.length) {
// ...then generate 10 more and add them to the
// suggestions list.
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
}
);
}
_buildSuggestions
函数对每个词对调用一次 _buildRow
。该函数在 ListTile
中显示每一个新的词对,这使得你可以在第2部分中使行更有吸引力。
在 _RandomWordsState
中添加一个 _buildRow
函数。
Widget _buildRow(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
更新 _RandomWordsState
的构建方法。
将其改为使用 _buildSuggestions()
,而不是直接调用单词生成库。(Scaffold
实现了基本的 Material Design 视觉布局。)
@override
Widget build(BuildContext context) {
//final wordPair = WordPair.random(); // Delete these...
//return Text(wordPair.asPascalCase); // ... two lines.
return Scaffold ( // Add from here...
appBar: AppBar(
title: Text('Startup Name Generator'),
),
body: _buildSuggestions(),
); // ... to here.
}
更新 MyApp
的构建方法,更改标题,删除 AppBar
,并将 home 属性改为 RandomWords
部件。
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
home: RandomWords(),
);
}
重新启动应用程序。无论你滚动多远,你都应该看到一个单词配对的列表。
Android:
iOS:
遇到问题了? #
如果你的应用程序不能正常运行,你可以使用下面链接中的代码来回到正轨。
今后的步骤 #
恭喜你!
你已经完成了这个代码实验室的第一部分! 如果你想扩展这款应用,请进入第二部分,你将对应用进行如下修改。
- 增加互动性
- 增加导航到新路由的功能。
- 修改主题颜色。
当第2部分完成后,应用程序将是这样的:
其他后续步骤 #
通过以下资源了解更多关于 Flutter SDK 的信息。
- Flutter 中的布局
- 增加互动性教程
- 组件介绍
- 为 Android 开发者提供的 Flutter
- 针对 React Native 开发者的 Flutter
- Web 开发人员的 Flutter
- Flutter YouTube 频道
其他资源包括以下几点: