编写你的第一个 Flutter 应用,第二部分
— 焉知非鱼Flutter 是 Google 的 UI 工具包,用于从单一代码库中为手机、网页和桌面构建漂亮的、原生编译的应用程序。Flutter 可以与现有的代码一起使用,被世界各地的开发者和组织使用,并且是免费和开源的。
介绍 #
Flutter 是 Google 的 UI 工具包,用于从单一代码库中为移动、Web 和桌面构建漂亮的、原生编译的应用程序。Flutter 可以与现有的代码一起工作,被世界各地的开发者和组织使用,并且是免费和开源的。
在这个代码实验室中,您将扩展一个基本的、移动的 Flutter 应用程序,以包含交互性。您还将创建一个用户可以导航到的第二个页面(称为路由)。最后,您将修改应用程序的主题(颜色)。这个代码实验室扩展了第1部分,在这部分中,你将创建一个无限的懒惰加载的列表,但如果你想从第2部分开始,我们将提供起始代码。
你将在第二部分学到什么 #
- 如何编写一款在 iOS、Android 和 Web 上看起来很自然的 Flutter 应用?
- 如何使用热重装,加快开发周期?
- 如何为有状态的 widget 添加交互性?
- 如何创建并导航到第二个屏幕?
- 如何使用主题来改变应用程序的外观?
你将在第二部分建立什么 #
您将从一个简单的移动应用程序开始,为创业公司生成一个无尽的建议名称列表。在代码实验室结束时,您的最终用户可以选择和取消选择名称,保存最好的名称。点击应用栏右上角的列表图标可以导航到一个新的页面(称为路由),该页面只列出了最喜欢的名字。
下面的 GIF 动画显示了完成的应用程序将如何工作。
设置您的 Flutter 环境 #
如果你还没有完成第1部分,请看设置你的 Flutter 环境,在编写你的第一个Flutter应用,第1部分,设置你的 Flutter 开发环境。
获取启动应用程序 #
如果你已经完成了这个 codelab 的第一部分,你已经有了启动应用程序,startup_namer
。你可以进行下一步。
如果你没有 startup_namer
,不要害怕,你可以使用下面的说明得到它。
使用创建应用程序中的说明创建一个简单的模板化 Flutter 应用程序。将项目命名为 startup_namer
(而不是 flutter_app
)。
删除 lib/main.dart
中的所有代码。用这个文件中的代码替换,它显示了一个无限的,懒惰加载的建议启动名称列表。
更新 pubspec.yaml
,加入英文单词包。
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
english_words: ^3.1.5 // NEW
英文单词包会生成一对随机的单词,作为潜在的启动名称。
在 Android Studio 的编辑器视图中查看 pubspec 时,点击右上角的 Pub get,这将包拉到你的项目中。你应该在控制台中看到以下内容:
flutter pub get
Running "flutter pub get" in startup_namer...
Process finished with exit code 0
运行该应用。
随意滚动,查看持续供应的拟创业公司名称。
将图标添加到列表中 #
在这一步中,你将为每一行添加心形图标。在下一步中,您将使它们可点击并保存收藏夹。
在 _RandomWordsState
中添加一个 _saved
Set。这个 Set
存储了用户收藏的单词配对。Set
比 List
更受欢迎,因为一个正确实现的 Set
不允许重复的条目。
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _saved = Set<WordPair>(); // NEW
final _biggerFont = TextStyle(fontSize: 18.0);
...
}
在 _buildRow
函数中,添加一个 alreadySaved
检查,以确保一个单词配对还没有被添加到收藏夹中。
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair); // NEW
...
}
在 _buildRow()
中,你还将为 ListTile
对象添加心形图标以实现收藏夹。在下一步中,你将添加与心形图标交互的功能。
在文本之后添加图标,如下图所示。
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon( // NEW from here...
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
), // ... to here.
);
}
热重新加载应用程序。
你现在应该看到每一行都有空心,但它们还没有互动。
Android
iOS
遇到问题了? #
如果你的应用程序不能正常运行,你可以使用下面链接中的代码来回到正轨。
增加互动性 #
在这一步中,你将使心形图标可以点击。当用户点击列表中的一个条目,切换其收藏状态时,该词对就会从一组保存的收藏夹中添加或删除。
要做到这一点,你将修改 _buildRow
函数。如果一个词条已经被添加到收藏夹中,再次点击它就会将其从收藏夹中删除。当一个磁贴被点击后,函数会调用 setState()
来通知框架状态已经改变。
在 _buildRow
方法中加入 onTap
,如下图所示:
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
),
onTap: () { // NEW lines from here...
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
}, // ... to here.
);
}
提示:在 Flutter 的反应式框架中,调用 setState()
会触发对 State
对象的 build()
方法的调用,导致 UI 的更新。
热重载应用。
你应该能够点击任何磁贴来收藏或不收藏该条目。点击瓷砖会产生一个隐含的从点击点发出的泼墨动画。
Android
iOS
遇到问题了? #
如果你的应用程序不能正常运行,你可以使用下面链接中的代码来回到正轨。
导航到一个新的屏幕 #
在这一步中,您将添加一个新的页面(在 Flutter 中称为路由),显示收藏夹。您将学习如何在主页路线和新路由之间进行导航。
在 Flutter 中,Navigator
管理着一个包含应用程序路由的堆栈。将一个路由推到 Navigator
的堆栈上,会将显示更新到该路由。从 Navigator
的堆栈中弹出一条路由,会将显示返回到之前的路由。
接下来,您将在 _RandomWordsState
的 build
方法中为 AppBar
添加一个列表图标。当用户点击列表图标时,一个包含保存的收藏夹的新路由会被推送到 Navigator
,显示图标。
在 build
方法中添加图标及其对应的操作:
class _RandomWordsState extends State<RandomWords> {
...
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Startup Name Generator'),
actions: [ // NEW lines from here...
IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
], // ... to here.
),
home: RandomWords(),
);
}
...
}
提示:一些小组件属性会取一个小组件(child
),而其他属性,如 action
,会取一组小组件(children
), 如方括号([]
)所示。
在 _RandomWordsState
类中添加一个 _pushSaved()
函数。
void _pushSaved() {
}
热重新加载应用程序。列表图标出现在应用栏中。点击它还没有任何作用,因为 _pushSaved
函数是空的。
接下来,你将建立一条路由,并将其推送到 Navigator
的栈中。这个操作会改变屏幕以显示新的路由。新页面的内容是在 MaterialPageRoute
的构建器属性中以匿名函数的方式构建的。
调用 Navigator.push
,如下图所示,它将路由推送到 Navigator
的堆栈中。IDE 会抱怨无效代码,但你会在下一节中解决这个问题。
void _pushSaved() {
Navigator.of(context).push(
);
}
接下来,你将添加 MaterialPageRoute
和它的构建器。现在,添加生成 ListTile
行的代码。ListTile
的 divideTiles()
方法在每个 ListTile
之间增加了水平间距。被划分的变量持有通过方便函数 toList()
转换为列表的最终行。
添加代码,如下面的代码片段所示:
void _pushSaved() {
Navigator.of(context).push(
MaterialPageRoute<void>(
// NEW lines from here...
builder: (BuildContext context) {
final tiles = _saved.map(
(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
}, // ...to here.
),
);
}
}
builder
属性返回一个 Scaffold
,包含名为 SavedSuggestions
的新路由的应用栏。新路由的主体由一个包含 ListTiles
行的 ListView
组成。每一行都由一个分隔符隔开。
热重载应用。将一些选择收藏起来,然后点击应用栏中的列表图标。新的路由出现,包含收藏夹。请注意,Navigator 在应用栏中增加了一个"返回"按钮。你不必明确地实现 Navigator.pop
。点击"返回"按钮就可以返回到主路由。
iOS - Main route
iOS - Saved suggestions route
遇到问题了? #
如果你的应用程序没有正确运行,那么你可以使用下面链接中的代码来回到正轨。
使用主题改变用户界面 #
在这一步中,您将修改应用程序的主题。主题控制你的应用程序的外观和感觉。您可以使用默认主题,这取决于物理设备或模拟器,或者自定义主题以反映您的品牌。
您可以通过配置 ThemeData 类轻松更改应用程序的主题。应用程序使用默认主题,但你会将应用程序的主色调改为白色。
在 MyApp
类中更改颜色:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
theme: ThemeData( // Add the 3 lines from here...
primaryColor: Colors.white,
), // ... to here.
home: RandomWords(),
);
}
}
热重载应用。现在整个背景都是白色的,甚至应用栏也是白色的。
作为一个练习,使用 ThemeData
来改变 UI 的其他方面。Material 库中的 Colors 类提供了许多你可以玩的颜色常量。热重载使得对 UI 的实验变得快速而简单。
Android
iOS
遇到问题了? #
如果你已经偏离了轨道,那么使用下面链接中的代码来查看最终应用的代码。
做得很好! #
你写了一个交互式的 Flutter 应用,可以在 iOS 和 Android 上运行,具体做法如下
- 编写 Dart 代码。
- 使用热重载来加快开发周期。
- 实现一个有状态的 widget,为你的应用添加交互性。
- 创建途径并添加在原途径和新途径之间移动的逻辑。
- 学习如何使用主题改变你的应用程序的 UI 外观。
今后的步骤 #
从以下资源中了解更多关于 Flutter SDK 的信息。
- Flutter 中的布局
- 增加互动性教程
- 组件介绍
- 为 Android 开发者提供的 Flutter
- 针对 React Native 开发者的 Flutter
- Web 开发人员的 Flutter
- Flutter YouTube 频道
其他资源包括以下几点: