开始使用-编写你的第一个Flutter应用程序 顶

这是创建您的第一个Flutter应用程序的指南。 如果您熟悉面向对象的代码和基本编程概念(如变量,循环和条件),则可以完成本教程。 您不需要以前使用Dart或移动编程的经验。

  • 第1步:创建起始Flutter应用程序
  • 第2步:使用外部包装
  • 第3步:添加一个有状态的小部件
  • 第4步:创建一个无限滚动ListView
  • 第5步:添加交互性
  • 第6步:导航到新的屏幕
  • 第7步:使用主题更改UI
  • 完成!

 你会建立什么

您将实施一个简单的移动应用程序,为一家创业公司生成建议名称。 用户可以选择和取消选择名称,保存最好的名称。 该代码一次生成十个名称。 当用户滚动时,会生成新批次的名称。 用户可以点击应用栏右上方的列表图标,以移动到仅列出收藏名称的新路由。

动画GIF显示完成的应用程序的工作方式。

你会学到什么: Flutter应用程序的基本结构。 查找和使用包来扩展功能。 使用热重载加快开发周期。 如何实现有状态的小部件。 如何创建一个无限的,延迟加载的列表。 如何创建并导航到第二个屏幕。 如何使用主题更改应用程序的外观。

你会到用什么: 您需要安装以下内容:

  • Flutter SDK
  • Flutter SDK包括Flutter的引擎,框架,小部件,工具和Dart SDK。 这个codelab需要v0.1.4或更高版本。
  • Android Studio IDE
  • 该codelab具有Android Studio IDE,但您可以使用其他IDE,或者从命令行运行。
  • 您的IDE插件
  • Flutter和Dart插件必须为您的IDE单独安装。 除了Android Studio,Flutter和Dart插件也可用于VS CodeIntelliJ IDE

有关如何设置环境的信息,请参阅Flutter安装和设置

第1步:创建起始Flutter应用程序

使用第一个Flutter应用程序入门中的说明创建一个简单的模板化Flutter应用程序。 将项目命名为startup_namer(而不是myapp)。 你将会修改这个初学者应用程序来创建完成的应用程序。

在这个codelab中,你将主要编辑Dart代码所在的lib / main.dart。

提示:将代码粘贴到应用程序中时,缩进可能会变形。 您可以使用Flutter工具自动修复此问题:

  • Android Studio / IntelliJ IDEA:右键单击飞镖代码,然后选择Reformat Code with dartfmt格式化代码。
  • VS代码:右键单击并选择Format Document
  • 终端:运行flutter格式<filename>。

1.替换lib/main.dart。 删除lib/main.dart中的所有代码。 替换为下面的代码,它在屏幕的中心显示“Hello World”。

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Welcome to Flutter',
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('Welcome to Flutter'),
        ),
        body: new Center(
          child: new Text('Hello World'),
        ),
      ),
    );
  }
}

2.运行应用程序。 你应该看到下面的屏幕。

意见

  • 本示例创建一个Material应用程序。 Material是一种视觉设计语言,在移动设备和网络上是标准的。 Flutter提供了一套丰富的Material小部件。
  • main方法指定胖箭头(=>)表示法,它是用于单行函数或方法的简写。
  • 该应用程序扩展了使应用程序本身成为小部件的StatelessWidget。 在Flutter中,大多数情况都是一个小部件,包括对齐,填充和布局。
  • Material库中的Scaffold小部件提供了默认应用程序栏,标题和控制主屏幕小部件树的body属性。 小部件子树可能相当复杂。
  • 小部件的主要工作是提供一个build()方法,该方法描述如何根据其他较低级别的小部件来显示小部件。
  • 此示例的小部件树由包含Text小部件的Center小部件组成。 Center小部件将其小部件子树对齐到屏幕中心。

第2步:使用外部包装

在这一步中,您将开始使用名为english_words的开源软件包,其中包含数千个最常用的英文单词以及一些实用功能。

您可以在pub.dartlang.org上找到english_words软件包以及其他许多开源软件包。

1.pubspec文件管理Flutter应用程序的资产。 在pubspec.yaml中,将english_words(3.1.0或更高版本)添加到依赖项列表。 新行高亮如下:

dependencies:   flutter:     sdk: flutter   cupertino_icons: ^0.1.0   english_words: ^3.1.0

2.在Android Studio编辑器视图中查看pubspec时,单击右上角的Packages get。 这将该包加入您的项目。 您应该在控制台中看到以下内容:

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

3.在lib/main.dart中,添加english_words导入语句,如突出显示的行所示:

import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart';

在您键入时,Android Studio会为您提供有关库导入的建议。 然后它将呈现灰色的导入字符串,让您知道导入的库尚未使用(到目前为止)。

4.使用英文单词包来生成文本,而不是使用字符串“Hello World”。

提示:“Pascal case”(也称为“上骆驼案例”)意味着字符串中的每个单词(包括第一个单词)都以大写字母开头。 所以,“uppercamelcase”变成“UpperCamelCase”。

进行以下更改,如下所示:

import 'package:flutter/material.dart'; import 'package:english_words/english_words.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override   Widget build(BuildContext context) { final wordPair = new WordPair.random();     return new MaterialApp(       title: 'Welcome to Flutter',       home: new Scaffold(         appBar: new AppBar(           title: new Text('Welcome to Flutter'),         ),         body: new Center(           //child: new Text('Hello World'), // Replace the highlighted text...           child: new Text(wordPair.asPascalCase),  // With this highlighted text.         ),       ),     );   } }

5.如果应用程序正在运行,请使用热重新加载按钮(闪电图标)更新正在运行的应用程序。 每次单击热重新加载或保存项目时,都会在正在运行的应用程序中随机选择不同的单词对。 这是因为配对这个词是在构建方法内部生成的,每次MaterialApp需要渲染时或者在Flutter Inspector中切换平台时都会运行。

问题?

如果您的应用程序运行不正常,请查找错别字。 如果需要,请使用以下链接中的代码重新开始正轨。

第3步:添加一个有状态的小部件

无状态小部件是不可变的,这意味着它们的属性不能改变 - 所有的值都是最终的。

有状态的小部件保持在小部件的生命周期中可能改变的状态。 实现一个有状态的小部件至少需要两个类:1)一个StatefulWidget类,它创建一个2)一个State类的实例。 StatefulWidget类本身是不可变的,但State类在整个构件的生命周期中保持不变。

在这一步中,您将添加一个有状态的小部件RandomWords,它创建其状态类RandomWordsState。 State类将最终维护小部件的建议和最喜欢的单词对。

1.将有状态的RandomWords小部件添加到main.dart。 它可以在MyApp之外的文件中的任何位置使用,但解决方案将它放在文件的底部。 RandomWords小部件除了创建State类之外几乎没有其他任何东西:

class RandomWords extends StatefulWidget {
  @override
  createState() => new RandomWordsState();
}

2.添加RandomWordsState类。 该应用的大部分代码都驻留在该类中,该类保持RandomWords小部件的状态。 这个类将保存随着用户滚动而无限增长的生成的单词对,以及最喜欢的单词对,因为用户通过切换心脏图标来将它们从列表中添加或删除。

你会一点一点地建立这个类。 首先,通过添加突出显示的文本创建一个最小类:

class RandomWordsState extends State<RandomWords> {
}

3.在添加状态类后,IDE会抱怨该类缺少构建方法。 接下来,您将添加一个基本构建方法,该方法通过将单词生成代码从MyApp移动到RandomWordsState来生成单词对。

将构建方法添加到RandomWordState中,如突出显示的文本所示:

class RandomWordsState extends State<RandomWords> { @override   Widget build(BuildContext context) {     final wordPair = new WordPair.random();     return new Text(wordPair.asPascalCase);   } }

4.通过下面突出显示的更改从MyApp中删除单词生成代码:

class MyApp extends StatelessWidget { @override   Widget build(BuildContext context) { final wordPair = new WordPair.random();  // Delete this line     return new MaterialApp(       title: 'Welcome to Flutter',       home: new Scaffold(         appBar: new AppBar(           title: new Text('Welcome to Flutter'),         ),         body: new Center(           //child: new Text(wordPair.asPascalCase), // Change the highlighted text to...           child: new RandomWords(), // ... this highlighted text         ),       ),     );   } }

重新启动应用程序。 如果您尝试重新加载热点,则可能会看到一条警告:

Reloading...
Not all changed program elements ran during view reassembly; consider
restarting.

这可能是误报,但考虑重新启动以确保您的更改反映在应用的用户界面中。

应用程序应该像以前一样运行,每次热重新加载或保存应用程序时都会显示一个字对。

问题?

如果您的应用程序运行不正常,则可以使用以下链接中的代码重新进入正轨。

第4步:创建一个无限滚动ListView

在这一步中,您将展开RandomWordsState以生成并显示单词配对列表。 当用户滚动时,ListView小部件中显示的列表将无限增长。 ListView的builder工厂构造函数允许您根据需要懒惰地构建列表视图。

1.将一个_suggestions列表添加到RandomWordsState类,以保存建议的词对。 该变量以下划线(_)开头 - 在前面加上一个带有下划线的标识符可以强化Dart语言的隐私。

此外,添加一个largerFont变量来使字体变大。

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];

  final _biggerFont = const TextStyle(fontSize: 18.0);
  ...
}

2.将一个_buildSuggestions()函数添加到RandomWordsState类。 此方法构建显示建议词对的ListView。

ListView类提供了一个构建器属性itemBuilder,一个指定为匿名函数的工厂构建器和回调函数。 两个参数传递给函数 - BuildContext和行迭代器,i 迭代器从0开始,每次调用该函数时递增,每次建议的单词配对一次。 该模型允许建议的列表在用户滚动时无限增长。

添加下面突出显示的行:

class RandomWordsState extends State<RandomWords> {   ... Widget _buildSuggestions() {     return new ListView.builder(       padding: const EdgeInsets.all(16.0),       // 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: (context, i) {         // Add a one-pixel-high divider widget before each row in theListView.   if (i.isOdd) return new 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 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]);   }     );   } }

3._buildSuggestions函数每个词对调用_buildRow一次。 这个函数在ListTile中显示每个新对,这允许您在下一步中使行更具吸引力。

向RandomWordsState添加_buildRow函数:

class RandomWordsState extends State<RandomWords> {   ...   Widget _buildRow(WordPair pair) {     return new ListTile(       title: new Text(         pair.asPascalCase,         style: _biggerFont,       ),     );   } }

4.更新RandomWordsState的build 方法以使用_buildSuggestions(),而不是直接调用单词生成库。 进行突出显示的更改:

class RandomWordsState extends State<RandomWords> {   ... @override   Widget build(BuildContext context) { final wordPair = new WordPair.random(); // Delete these two lines.     return new Text(wordPair.asPascalCase); return new Scaffold (       appBar: new AppBar(         title: new Text('Startup Name Generator'),       ),       body: _buildSuggestions(),     );   }   ... }

5.更新MyApp的build方法。 从MyApp中删除Scaffold和AppBar实例。 这些将由RandomWordsState管理,这使得用户在下一步中从一个屏幕导航到另一个屏幕时,可以更轻松地更改应用栏中的路由名称。

用下面突出显示的构建方法替换原始方法:

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) {     return new MaterialApp(       title: 'Startup Name Generator',       home: new RandomWords(),     );   } }

重新启动应用程序。 你应该看到一个单词配对清单。 尽可能向下滚动,您将继续看到新的单词配对。

问题?

如果您的应用程序运行不正常,则可以使用以下链接中的代码重新进入正轨。

第5步:添加交互性

在这一步中,您将为每一行添加可点击的心脏图标。 当用户点击列表中的条目,切换其“收藏”状态时,该词语配对被添加或从一组保存的收藏夹中移除。

1.将一个_saved集添加到RandomWordsState。 这个集合存储用户最喜欢的单词配对。 Set比List更受欢迎,因为正确实施的Set不允许重复输入。

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

2.在_buildRow函数中,添加alreadySaved检查以确保单词配对尚未添加到收藏夹。

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

3.同样在_buildRow()中,将心形图标添加到ListTiles以启用收藏。 稍后,您将添加与心脏图标进行交互的功能。

添加下面突出显示的行:

Widget _buildRow(WordPair pair) {   final alreadySaved = _saved.contains(pair);   return new ListTile(     title: new Text(       pair.asPascalCase,       style: _biggerFont,     ), trailing: new Icon(       alreadySaved ? Icons.favorite : Icons.favorite_border,       color: alreadySaved ? Colors.red : null,     ),   ); }

4.重新启动应用程序。 你现在应该在每一行看到开放的心,但它们还没有互动。

5.在_buildRow函数中让心灵可点击。 如果单词条目已被添加到收藏夹中,再次点击它将其从收藏夹中删除。 当心脏被轻敲时,函数调用setState()来通知框架状态已经改变。

添加突出显示的行:

Widget _buildRow(WordPair pair) {   final alreadySaved = _saved.contains(pair);   return new ListTile(     title: new Text(       pair.asPascalCase,       style: _biggerFont,     ),     trailing: new Icon(       alreadySaved ? Icons.favorite : Icons.favorite_border,       color: alreadySaved ? Colors.red : null,     ), onTap: () {       setState(() {         if (alreadySaved) {           _saved.remove(pair);         } else {           _saved.add(pair);         }       });     },   ); }

提示:在Flutter的反应风格框架中,调用setState()会触发对State对象的build()方法的调用,从而导致UI的更新。

热重新加载应用程序。 你应该能够点击任何一行以获得最喜欢的,或不适合的入口。 请注意,点击一行会生成从心脏图标发出的隐式墨迹飞溅动画。

问题?

如果您的应用程序运行不正常,则可以使用以下链接中的代码重新进入正轨。

第6步:导航到新的屏幕

在这一步中,您将添加一个显示收藏夹的新屏幕(在Flutter中称为路由)。 您将学习如何在主路由和新路由之间导航。

在Flutter中,导航器管理包含应用程序路由的堆栈。 将路由推入导航器的堆栈,将显示更新为该路由。 从导航器的堆栈中弹出路由,将显示返回到前一个路由。

1.向RandomWordsState的构建方法中的AppBar添加列表图标。 当用户点击列表图标时,包含收藏夹项目的新路线被推送到导航器,显示该图标。

提示:某些小部件属性采用单个小部件(子级),而其他属性(如操作)则采用小部件(子级)数组,如方括号([])所示。

将该图标及其相应的操作添加到构建方法中:

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

2.将一个_pushSaved()函数添加到RandomWordsState类。

class RandomWordsState extends State<RandomWords> {   ... void _pushSaved() {   } }

热重新加载应用程序。 列表图标出现在应用程序栏中。 点击它什么也没做,因为_pushSaved函数是空的。

3.当用户点击应用栏中的列表图标时,建立一条路由并将其推送到导航器的堆栈。 此操作会更改屏幕以显示新路由。

新页面的内容是使用匿名函数在MaterialPageRoute的builder属性中构建的。

将呼叫添加到Navigator.push,如突出显示的代码所示,将路由推送到导航器的堆栈。

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

4.添加MaterialPageRoute及其构建器。 现在,添加生成ListTile行的代码。 ListTile的divideTiles()方法在每个ListTile之间添加水平间距。 变量divided保存最后的行,通过便利函数toList()转换为列表。

void _pushSaved() {   Navigator.of(context).push( new MaterialPageRoute(       builder: (context) {         final tiles = _saved.map(           (pair) {             return new ListTile(               title: new Text(                 pair.asPascalCase,                 style: _biggerFont,               ),             );           },         );         final divided = ListTile           .divideTiles(             context: context,             tiles: tiles,           )           .toList();       },     ),   ); }

5.builder属性返回一个Scaffold,其中包含名为“Saved Suggestions”的新路由的应用栏。新路由的主体由包含ListTiles行的ListView组成; 每行由一个分隔符分隔。

添加下面突出显示的代码:

void _pushSaved() {   Navigator.of(context).push(     new MaterialPageRoute(       builder: (context) {         final tiles = _saved.map(           (pair) {             return new ListTile(               title: new Text(                 pair.asPascalCase,                 style: _biggerFont,               ),             );           },         );         final divided = ListTile           .divideTiles(             context: context,             tiles: tiles,           )           .toList(); return new Scaffold(           appBar: new AppBar(             title: new Text('Saved Suggestions'),           ),           body: new ListView(children: divided),         );       },     ),   ); }

6.热重新加载应用程序。 最喜欢的一些选择,并点击应用栏中的列表图标。 新路线显示包含收藏夹。 请注意,导航器会在应用栏中添加一个“返回”按钮。 你不必显式实现Navigator.pop。 点击后退按钮返回到主页路由。

问题?

如果您的应用程序运行不正常,则可以使用以下链接中的代码重新进入正轨。

第7步:使用主题更改UI

在最后一步中,您将使用该应用的主题。 主题控制你的应用的外观和感觉。 您可以使用默认主题,该主题取决于物理设备或模拟器,也可以自定义主题以反映品牌。

1.您可以通过配置ThemeData类轻松更改应用程序的主题。 您的应用程序目前使用默认主题,但您将更改主要颜色为白色。

将突出显示的代码添加到MyApp,将应用程序的主题更改为白色:

class MyApp extends StatelessWidget {   @override   Widget build(BuildContext context) {     return new MaterialApp(       title: 'Startup Name Generator',   theme: new ThemeData(         primaryColor: Colors.white,       ),       home: new RandomWords(),     );   } }

2.热重新加载应用程序。 请注意,整个背景是白色的,甚至是应用栏。

3.作为读者的练习,使用ThemeData来改变UI的其他方面。 材质库中的Colors类提供了许多可以使用的颜色常量,而热重载使得用户界面的实验变得快速而简单。

问题?

如果您的应用程序运行不正常,则可以使用以下链接中的代码重新进入正轨。

完成!

您已经编写了一个在iOS和Android上运行的交互式Flutter应用程序。 在这个codelab中,你有:

  • 从头开始创建一个Flutter应用程序。
  • 书写Dart代码。
  • 利用外部的第三方库。
  • 使用热重载加快开发周期。
  • 实现一个有状态的小部件,为你的应用增加交互性。
  • 用ListView和ListTiles创建一个延迟加载的无限滚动列表。
  • 创建了一条路由并添加了在主路由和新路由之间移动的逻辑。
  • 了解如何使用主题更改应用UI的外观。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Flutter入门

Flutter入门三部曲(1) - 基础认识

看到整体的架构图,它是由dart完成上层的framework,然后由通过skia来完成图形的绘制。

16900
来自专栏前端知识分享

深入理解Vue的生命周期

  谈到Vue的生命周期,相信许多人并不陌生。但大部分人和我一样,只是听过而已,具体用在哪,怎么用,却不知道。我在学习vue一个多礼拜后,感觉现在还停留在初级阶...

14430
来自专栏Modeng的专栏

Vue双向绑定原理,教你一步一步实现双向绑定

当今前端天下以 Angular、React、vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋。

12210
来自专栏落影的专栏

iOS开发笔记(六)

前言 专注、坚持,是优良的品格。 正文 1、cell和cell.contentView 的区别 在给UITableViewCell添加视图的时候,我们有以下两种...

34350
来自专栏Netkiller

Shell 高级编程

Shell 高级编程 Shell 更多是被看成一种批处理命令,确实很多是是吧 Shell当成批处理去使用的。 我确不这么看,我认为要想开发程序一样去写Shel...

39760
来自专栏微信公众号:Java团长

Intellij IDEA神器那些让人爱不释手的小技巧

之前写了一篇介绍IntellIJ IDEA的文章,主要是列出一些平时大家可能没用过或者没怎么用,但是又非常好用的IntellIJ IDEA小技巧。由于篇幅原因,...

8920
来自专栏烂笔头

Django 1.10中文文档-第一个应用Part4-表单和通用视图

目录[-] 本教程接Part3开始。继续网页投票应用程序,并将重点介绍简单的表单处理和精简代码。 一个简单表单 更新一下在上一个教程中编写的投票详细页面的...

33540
来自专栏Golang语言社区

golang微信支付服务端

一般来说,使用golang主要还是写服务端。所以本文主要讲golang在处理微信移动支付的服务端时的统一下单接口和支付回调接口,以及查询接口。 微信支付流程 下...

1.6K70
来自专栏Flutter入门

Flutter入门三部曲(1) - 基础认识

image.png 看到整体的架构图,它是由dart完成上层的framework,然后由通过skia来完成图形的绘制。

1.6K60
来自专栏不止是前端

TS+React+Router+Mobx+Koa打造全栈应用

49770

扫码关注云+社区

领取腾讯云代金券