编写您的第一款 Flutter 应用(第 2 部分)

1. 简介

Flutter 是 Google 的界面工具包,用于通过单一代码库为移动、网络和桌面设备构建本机编译的精美应用。Flutter 可与现有代码一起使用,已受到全球开发者和组织的青睐,是免费的开放源代码。

在本 Codelab 中,您将扩展基本的 Flutter 移动应用以添加交互性。您还将创建用户可以导航前往的另一个页面(我们将其称为路由)。最后,您将修改应用的主题背景(颜色)。本 Codelab 是第 1 部分的后续内容, 在第 1 部分中,您创建了无限延迟加载的列表,但如果您要从第 2 部分开始,我们将提供初始代码。

您将在第 2 部分中学习的内容

  • 如何编写像 iOS、Android 和 Web 原生应用一样的 Flutter 应用
  • 如何使用热重载实现更短的开发周期
  • 如何在有状态微件中添加交互性
  • 如何创建并导航至另一个屏幕
  • 如何使用主题背景更改应用外观

您将在第 2 部分中构建的内容

您将一个简单的移动应用开始,该应用用于为初创公司生成包含建议名称的无限列表。在本 Codelab 结尾,您的最终用户可以选择和取消选择名称,从而保留最佳名称。轻按应用栏右上方的列表图标可导航至一个新页面(称为路由),其中仅会列出已收藏的名称。

下方的动画 GIF 展示的是已完成应用的工作方式。

17a70f54709401bc.gif

您想要从本 Codelab 中学到什么?

我刚接触此主题,因此需要详尽的概述。 我了解此主题的一些内容,但需要复习一下。 我希望获得示例代码,以便在我的项目中加以应用。 我在寻找对一些特定内容的说明。

2. 设置您的 Flutter 环境

如果您未完成第 1 部分,请参阅 编写您的第一个 Flutter 应用(第 1 部分)中的 设置您的 Flutter 环境,以针对 Flutter 开发设置环境。

3. 获取起始应用

如果您完成了本 Codelab 的第 1 部分,则已拥有起始应用 startup_namer。可以继续完成下一步。

如果您没有 startup_namer,也无需担心,您可以使用下面的说明获取该应用。

b2f84ff91b0e1396.png使用 创建应用中的说明来创建简单的模板化 Flutter 应用。将此项目命名为 startup_namer(而不是 flutter_app)。

b2f84ff91b0e1396.pnglib/main.dart 中删除所有代码。将代码替换为 本文件中的代码,该代码将显示包含初创公司建议名称的无限延迟加载列表。

b2f84ff91b0e1396.png通过添加英文单词软件包来更新 pubspec.yaml

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  english_words: ^3.1.5    // NEW

此英文单词软件包会生成用作潜在初创公司名称的多对随机单词。

b2f84ff91b0e1396.png在 Android Studio 的编辑器视图中查看 pubspec 时,点击右上方的 Pub get,即可将软件包提取到您的项目中。您应该能在控制台中看到以下内容:

flutter pub get
Running "flutter pub get" in startup_namer...
Process finished with exit code 0

b2f84ff91b0e1396.png运行应用。

根据您的需要向下滚动,查看接连出现的初创公司建议名称。

4. 将图标添加到列表中

在此步骤中,您需要向每一行中添加心形图标。在下一步中,将其设置为可点按并保存收藏夹。

b2f84ff91b0e1396.png_saved Set 添加到 _RandomWordsState 中。此 Set 可用于存储用户已收藏的单词对。Set 优于 List,因为正确实现的 Set 不允许出现重复条目。

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _saved = Set<WordPair>();     // NEW
  final _biggerFont = TextStyle(fontSize: 18.0);
  ...
}

b2f84ff91b0e1396.png_buildRow 函数中,添加 alreadySaved 复选框,以确保系统尚未将单词对添加到收藏夹中。

Widget _buildRow(WordPair pair) {
  final alreadySaved = _saved.contains(pair);  // NEW
  ...
}

_buildRow() 中,还需要将心形图标添加到 ListTile 对象中,以启用收藏功能。 在下一步中,您需要添加用于和心形图标交互的功能。

b2f84ff91b0e1396.png在文本后添加图标,如下方所示:

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.
  );
}

b2f84ff91b0e1396.png热重载该应用。

您现在应该可在每一行上看到空心的心形图标,但其尚不具备交互性。

Android

iOS

遇到了问题?

如果您的应用无法正常运行,则可通过使用以下链接中的代码恢复正常状态。

5. 添加交互性

在此步骤中,您需要将心形图标设置为可点按内容。当用户点按列表中的某个条目来切换其已收藏状态时,系统会在一组已保存的收藏项中添加或移除该单词配对。

如要执行此操作,您需要修改 _buildRow 函数。如果某个单词条目已添加到收藏夹中,则再次点按该条目,系统会将其从收藏夹中移除。当用户点按某个卡片时,该函数会调用 setState() 告知框架状态已更改。

b2f84ff91b0e1396.pngonTap 添加到 _buildRow 方法中,如下所示:

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.
  );
}

b2f84ff91b0e1396.png 热重载该应用。

您应该能够点按任何卡片对条目进行收藏或取消收藏。点按卡片会从触碰点处生成隐式溅墨动画。

Android

iOS

遇到了问题?

如果您的应用无法正常运行,则可通过使用以下链接中的代码恢复正常状态。

6. 导航至新屏幕

在此步骤中,您需要添加一个用于显示收藏夹的新页面(在 Flutter 中称为路由)。您将学习如何在主路由和新路由之间进行导航。

在 Flutter 中,Navigator 可用于管理包含应用路由的堆栈。将路由推送到 Navigator 的堆栈中会将显示内容更新为该路由。从 Navigator 的堆栈中弹出路由会令显示内容变为上一个路由。

接下来,将列表图标添加到 _RandomWordsStatebuild 方法的 AppBar 中。当用户点击列表图标时,系统会将包含已保存的收藏项的新路由推送到 Navigator,以显示该图标。

b2f84ff91b0e1396.png将图标及其相应的操作添加到 build 方法中:

class _RandomWordsState extends State<RandomWords> {
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
        actions: [
          IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),
      body: _buildSuggestions(),
    );
  }
  ...
}

b2f84ff91b0e1396.png_pushSaved() 函数添加到 _RandomWordsState 类中。

  void _pushSaved() {
  }

b2f84ff91b0e1396.png热重载该应用。列表图标 a114478ae13b853.png 将出现在应用栏中。由于 _pushSaved 函数为空,所以点按该图标暂时不会执行任何操作。

接下来,将构建路由并将其推送到 Navigator 的堆栈中。该操作可将显示内容更改为显示新路由。新页面的内容由系统在匿名函数中,使用 MaterialPageRoutebuilder 属性构建而成。

b2f84ff91b0e1396.png调用 Navigator.push(如下所示),这会将路由推送到 Navigator 的堆栈。IDE 将向您告知存在无效代码,但您将在下一节中解决此问题。

void _pushSaved() {
  Navigator.of(context).push(
  );
}

接下来,您需要添加 MaterialPageRoute 及其构建器。现在,添加用于生成 ListTile 行的代码。ListTiledivideTiles() 方法会在每个 ListTile 之间添加水平间距。divided 变量可负责保存由便利函数 toList() 转换为列表的最终行。

b2f84ff91b0e1396.png添加节点,如以下代码段中所示:

  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 组成。系统会在每行间,使用一个分隔线进行分隔。

b2f84ff91b0e1396.png热重载该应用。将一些所选项添加到收藏夹并点按应用栏中的列表图标。系统将显示包含收藏夹的新路由。请注意,Navigator 会将"返回"按钮添加到应用栏中。您不必显式实现 Navigator.pop。点按"back"(返回)按钮即可返回到主路由。

iOS - 主路由

iOS - 已保存的建议路由

遇到了问题?

如果您的应用无法正常运行,则可通过使用以下链接中的代码恢复正常状态。

7. 使用主题背景更改界面

在此步骤中,您需要修改应用的主题背景。主题背景会负责控制应用的外观。您可以使用默认主题背景(视实体设备或模拟器而定),也可以自定义主题背景以反映您的品牌。

您可通过配置 ThemeData 类来轻松更改应用的主题背景。应用会使用默认主题背景,但您需将该应用的主色更改为白色。

b2f84ff91b0e1396.pngMyApp 类中更改颜色:

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(),
    );
  }
}

b2f84ff91b0e1396.png热重载该应用。包括应用栏在内的整个背景现在均为白色。

作为练习,请使用 ThemeData 更改界面的其他方面。您可从 Material 库的 Colors 类中, 获取可供试用的多种颜色常量。通过热重载,您可以快速、轻松地尝试使用界面。

Android

iOS

遇到了问题?

如果您的应用出现了差错,则可使用以下链接中的代码查看最终应用的代码。

8. 做得好!

通过执行下列操作,您编写了一个可在 iOS 和 Android 上运行的交互式 Flutter 应用:

  • 编写 Dart 代码
  • 使用热重载缩短开发周期
  • 使用有状态微件,并向应用中添加交互性
  • 创建路由并添加在主路由与新路由之间移动的逻辑
  • 了解如何使用主题背景更改应用界面的外观

9. 后续步骤

通过下列资源详细了解 Flutter SDK:

其他资源包括:

此外,您还可 联系 Flutter 社区