每一个移动开发者都在为 Flutter 带来的“快速开发、富有表现力和灵活的 UI、原生性能”的特色和理念而痴狂,从超级 App 到独立应用,从纯 Flutter 到混合栈,开发者们在不同的场景下乐此不疲的探索和应用着 Flutter 技术,也在面临着各种各样不同的挑战。

阿里巴巴集团内也有越来越多的业务和团队开始尝试 Flutter 技术栈,从闲鱼的一支独秀引领潮流,到如今淘宝特价版、优酷、飞猪等BU业务相继入局,Flutter的业务应用在集团内也已经逐渐形成趋势。

有幸从一位朋友那里得到阿里内部出品强推的“Flutter进阶笔记”,秉承好东西的当然要共享的原则,今天就来秀一把,试试这“Flutter进阶笔记”是否也能让你事半功倍!

第一章 为什么Flutter是跨平台开发的终极之选

跨平台开发是当下最受欢迎、应用最广泛的框架之一。能实现跨平台开发的框架也五花八门,让人眼花缭乱。

最流行的跨平台框架有 Xamarin、PhoneGap、Ionic、Titanium、Monaca、Sencha、jQuery Mobile、React native、Flutter 等等。但这些工具的表现也是高低有别,各有千秋。

在这些流行的框架中,有很多也已经消失在了历史的长河中被人渐渐遗忘了。但 React native 和 Flutter 这俩框架地位依旧坚挺,备受欢迎。

因为它们俩分别由最强大的科技巨头 Facebook 和谷歌背书支持。

第二章 在Windows上搭建Flutter开发环境

  • 使用镜像
  • 系统要求
  • 获取Flutter SDK
  • 编辑器设置
  • Android设置
  • 起步: 配置编辑器
  • 起步: 体验
  • 体验热重载

第三章 编写您的第一个 Flutter App

第1步: 创建 Flutter app

创建一个简单的、基于模板的Flutter应用程序,按照[创建您的第一个Flutter应用中的指南的步骤, 然后将项目命名为startup_namer(而不是myapp),接下来你将会修改这个应用来完成最终的APP。

在这个示例中,你将主要编辑Dart代码所在的lib/main.dart 文件,

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

Android Studio / IntelliJ IDEA: 右键单击Dart代码,然后选择Reformat Code with dartfmt.

VS Code: 右键单击并选择Format Document.

Terminal: 运行flutter format <filename>.

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

import'package:flutter/material.dart';voidmain()=>runApp(newMyApp());classMyAppextendsStatelessWidget{
@overrideWidgetbuild(BuildContextcontext){returnnewMaterialApp(title:'Welcome to Flutter',home:newScaffold(appBar:newAppBar(title:newText('Welcome to Flutter'),),body:newCenter(child:newText('Hello World'),),),);
}}

2.运行应用程序,你应该看到如下界面.

分析

本示例创建一个Material APP。Material是一种标准的移动端和web端的视觉设计语言。 Flutter提供了一套丰富的Material widgets。

main函数使用了(=>)符号, 这是Dart中单行函数或方法的简写。

该应用程序继承了 StatelessWidget,这将会使应用本身也成为一个widget。 在Flutter中,大多数东西都是widget,包括对齐(alignment)、填充(padding)和布局(layout)

Scaffold 是 Material library 中提供的一个widget, 它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。widget树可以很复杂。

widget的主要工作是提供一个build()方法来描述如何根据其他较低级别的widget来显示自己。

本示例中的body的widget树中包含了一个Center widget, Center widget又包含一个 Text 子widget。 Center widget可以将其子widget树对其到屏幕中心。

第2步: 使用外部包(package)

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

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

1.pubspec文件管理Flutter应用程序的assets(资源,如图片、package等)。 在pubspec.yaml中,将english_words(3.1.0或更高版本)添加到依赖项列表,如下面高亮显示的行:

dependencies:flutter:sdk: fluttercupertino_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.使用 English words 包生成文本来替换字符串“Hello World”.

Tip: “驼峰命名法” (称为 “upper camel case” 或 “Pascal case” ), 表示字符串中的每个单词(包括第一个单词)都以大写字母开头。所以,“uppercamelcase” 变成 “UpperCamelCase”
进行以下更改, 如高亮部分所示:

import'package:flutter/material.dart';import'package:english_words/english_words.dart';
voidmain()=>runApp(newMyApp());
classMyAppextendsStatelessWidget{
@overrideWidget build(BuildContext context){
final wordPair =new WordPair.random();
returnnewMaterialApp(title:'Welcome to Flutter',home:newScaffold(appBar:newAppBar(title:newText('Welcome to Flutter'),
),body:newCenter(
//child: new Text('Hello World'),child:newText(wordPair.asPascalCase),),),);
}}

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

遇到问题?

如果您的应用程序运行不正常,请查找是否有拼写错误。如果需要,使用下面链接中的代码来对比更正。

· pubspec.yaml (The pubspec.yaml file won’t change again.)

· lib/main.dart

第3步: 添加一个有状态的部件(Stateful widget)

Stateless widgets 是不可变的, 这意味着它们的属性不能改变 - 所有的值都是最终的.

Stateful widgets 持有的状态可能在widget生命周期中发生变化. 实现一个 stateful widget 至少需要两个类:

1.一个 StatefulWidget类。

2.一个 State类。 StatefulWidget类本身是不变的,但是 State类在widget生命周期中始终存在.
在这一步中,您将添加一个有状态的widget-RandomWords,它创建其State类RandomWordsState。State类将最终为widget维护建议的和喜欢的单词对。

1.添加有状态的 RandomWords widget 到 main.dart。 它也可以在MyApp之外的文件的任何位置使用,但是本示例将它放到了文件的底部。RandomWords widget除了创建State类之外几乎没有其他任何东西

classRandomWordsextendsStatefulWidget{@overridecreateState()=>newRandomWordsState();}

2.添加 RandomWordsState 类.该应用程序的大部分代码都在该类中, 该类持有RandomWords widget的状态。这个类将保存随着用户滚动而无限增长的生成的单词对, 以及喜欢的单词对,用户通过重复点击心形 ❤️ 图标来将它们从列表中添加或删除。

你会一步一步地建立这个类。首先,通过添加高亮显示的代码创建一个最小类

class RandomWordsState extends State<RandomWords>{}

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

将build方法添加到RandomWordState中,如下面高亮代码所示

classRandomWordsStateextendsState<RandomWords>{
@overrideWidget build(BuildContext context){final wordPair =new WordPair.random();returnnew Text(wordPair.asPascalCase);
}}

4.通过下面高亮显示的代码,将生成单词对代的码从MyApp移动到RandomWordsState中

classMyAppextendsStatelessWidget{
@overrideWidget build(BuildContext context){
final wordPair =new WordPair.random();// 删除此行returnnewMaterialApp(title:'Welcome to Flutter',home:newScaffold(appBar:newAppBar(title:newText('Welcome to Flutter'),),body:newCenter(
//child: new Text(wordPair.asPascalCase),child:newRandomWords(),),),);
}}

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

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

这可能是误报,但考虑到重新启动可以确保您的更改在应用界面中生效。
应用程序应该像之前一样运行,每次热重载或保存应用程序时都会显示一个单词对。

遇到问题?

如果您的应用程序运行不正常,可以使用下面链接中的代码来对比更正。

· lib/main.dart

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

在这一步中,您将扩展(继承)RandomWordsState类,以生成并显示单词对列表。 当用户滚动时,ListView中显示的列表将无限增长。 ListView的builder工厂构造函数允许您按需建立一个懒加载的列表视图。

1.向RandomWordsState类中添加一个suggestions列表以保存建议的单词对。 该变量以下划线()开头,在Dart语言中使用下划线前缀标识符,会强制其变成私有的。

另外,添加一个biggerFont变量来增大字体大小

classRandomWordsStateextendsState<RandomWords>{
final _suggestions =<WordPair>[];final _biggerFont =constTextStyle(fontSize:18.0);
...}

2.向RandomWordsState类添加一个 _buildSuggestions() 函数. 此方法构建显示建议单词对的ListView。
ListView类提供了一个builder属性,itemBuilder 值是一个匿名回调函数, 接受两个参数- BuildContext和行迭代器i。迭代器从0开始, 每调用一次该函数,i就会自增1,对于每个建议的单词对都会执行一次。该模型允许建议的单词对列表在用户滚动时无限增长。
添加如下高亮的行:

classRandomWordsStateextendsState<RandomWords>{
...Widget _buildSuggestions(){returnnew ListView.builder(padding:const EdgeInsets.all(16.0),// 对于每个建议的单词对都会调用一次itemBuilder,然后将单词对添加到ListTile行中// 在偶数行,该函数会为单词对添加一个ListTile row.// 在奇数行,该函数会添加一个分割线widget,来分隔相邻的词对。// 注意,在小屏幕上,分割线看起来可能比较吃力。itemBuilder:(context, i){// 在每一列之前,添加一个1像素高的分隔线widgetif(i.isOdd)returnnew Divider();// 语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i为:1, 2, 3, 4, 5// 时,结果为0, 1, 1, 2, 2, 这可以计算出ListView中减去分隔线后的实际单词对数量final index = i ~/2;// 如果是建议列表中最后一个单词对if(index >= _suggestions.length){// ...接着再生成10个单词对,然后添加到建议列表_suggestions.addAll(generateWordPairs().take(10));}return_buildRow(_suggestions[index]);});
}}

3.对于每一个单词对,_buildSuggestions函数都会调用一次_buildRow。 这个函数在ListTile中显示每个新词对,这使您在下一步中可以生成更漂亮的显示行
在RandomWordsState中添加一个_buildRow函数 :

classRandomWordsStateextendsState<RandomWords>{
...Widget _buildRow(WordPair pair){returnnew ListTile(title:new Text(pair.asPascalCase,style: _biggerFont,),);
}}

4.更新RandomWordsState的build方法以使用_buildSuggestions(),而不是直接调用单词生成库。 更改后如下面高亮部分:

classRandomWordsStateextendsState<RandomWords>{
...@overrideWidget build(BuildContext context){final wordPair =new WordPair.random();// 删除这两行returnnew Text(wordPair.asPascalCase);returnnew Scaffold (appBar:new AppBar(title:new Text('Startup Name Generator'),),body:_buildSuggestions(),);}
...
}

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

classMyAppextendsStatelessWidget{@overrideWidget build(BuildContext context){returnnew MaterialApp(title:'Startup Name Generator',home:new RandomWords(),);
}}

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

遇到问题?

如果你的应用没有正常运行,你可以使用一下链接中的代码对比更正。

· lib/main.dart

第5步: 添加交互

在这一步中,您将为每一行添加一个可点击的心形 ❤️ 图标。当用户点击列表中的条目,切换其“收藏”状态时,将该词对添加到或移除出“收藏夹”。

1.添加一个 _saved Set(集合) 到RandomWordsState。这个集合存储用户喜欢(收藏)的单词对。 在这里,Set比List更合适,因为Set中不允许重复的值。

classRandomWordsStateextendsState<RandomWords>{final _suggestions =<WordPair>[];final _saved =new Set<WordPair>();final _biggerFont =constTextStyle(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);
returnnewListTile(title:newText(pair.asPascalCase,style: _biggerFont,
),
trailing:new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red :null,
),
);}

4.重新启动应用。你现在可以在每一行看到心形❤️图标️,但它们还没有交互。
在 _buildRow中让心形❤️图标变得可以点击。如果单词条目已经添加到收藏夹中, 再次点击它将其从收藏夹中删除。当心形❤️图标被点击时,函数调用setState()通知框架状态已经改变。
添加如下高亮的行:

Widget _buildRow(WordPair pair){
final alreadySaved = _saved.contains(pair);
returnnewListTile(title:newText(pair.asPascalCase,style: _biggerFont,
),trailing:newIcon(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的更新
热重载你的应用。你就可以点击任何一行收藏或移除。请注意,点击一行时会生成从心形 ❤️ 图标发出的水波动画.

遇到了问题?

如果您的应用没有正常运行,请查看下面链接处的代码,对比更正。

· lib/main.dart

第6步: 导航到新页面

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

在Flutter中,导航器管理应用程序的路由栈。将路由推入(push)到导航器的栈中,将会显示更新为该路由页面。 从导航器的栈中弹出(pop)路由,将显示返回到前一个路由。

1.在RandomWordsState的build方法中为AppBar添加一个列表图标。当用户点击列表图标时,包含收藏夹的新路由页面入栈显示。

提示: 某些widget属性需要单个widget(child),而其它一些属性,如action,需要一组widgets(children),用方括号[]表示。

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

classRandomWordsStateextendsState<RandomWords>{
...
@overrideWidget build(BuildContext context){
returnnewScaffold(appBar:newAppBar(title:newText('Startup Name Generator'),
actions:<Widget>[
new IconButton(icon:new Icon(Icons.list), onPressed: _pushSaved),],),body:_buildSuggestions(),);}...
}

2.向RandomWordsState类添加一个 _pushSaved() 方法.

classRandomWordsStateextendsState<RandomWords>{...void_pushSaved(){
}}

热重载应用,列表图标将会出现在导航栏中。现在点击它不会有任何反应,因为 _pushSaved 函数还是空的。

3.当用户点击导航栏中的列表图标时,建立一个路由并将其推入到导航管理器栈中。此操作会切换页面以显示新路由。

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

添加Navigator.push调用,这会使路由入栈(以后路由入栈均指推入到导航管理器的栈)

void_pushSaved(){
Navigator.of(context).push(
);}

4.添加MaterialPageRoute及其builder。 现在,添加生成ListTile行的代码。ListTile的divideTiles()方法在每个ListTile之间添加1像素的分割线。 该 divided 变量持有最终的列表项。

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

5.builder返回一个Scaffold,其中包含名为“Saved Suggestions”的新路由的应用栏。 新路由的body由包含ListTiles行的ListView组成; 每行之间通过一个分隔线分隔。
添加如下高亮的代码:

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

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

遇到了问题?

如果您的应用不能正常工作,请参考下面链接处的代码,对比并更正。

· lib/main.dart

第7步:使用主题更改UI

在这最后一步中,您将会使用主题。主题控制您应用程序的外观和风格。您可以使用默认主题,该主题取决于物理设备或模拟器,也可以自定义主题以适应您的品牌。

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

通过如下高亮部分代码,将应用程序的主题更改为白色:

    classMyAppextendsStatelessWidget{@overrideWidget build(BuildContext context){returnnewMaterialApp(title:'Startup Name Generator',theme:new ThemeData(primaryColor: Colors.white,),home:newRandomWords(),);
}}

2.热重载应用。 请注意,整个背景将会变为白色,包括应用栏。

3.作为读者的一个练习,使用 ThemeData 来改变UI的其他方面。 Material library中的Colors类提供了许多可以使用的颜色常量, 你可以使用热重载来快速简单地尝试、实验。

遇到了问题?

如果你遇到了问题,请查看以下链接中应用程序的最终代码。

· lib/main.dart

待续……

作者:Android进阶架构
链接:https://www.jianshu.com/p/a3466af9473c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

ALI Flutter进阶笔记相关推荐

  1. ALI Flutter进阶笔记,移动手机app开发

    阿里巴巴集团内也有越来越多的业务和团队开始尝试 Flutter 技术栈,从闲鱼的一支独秀引领潮流,到如今淘宝特价版.优酷.飞猪等BU业务相继入局,Flutter的业务应用在集团内也已经逐渐形成趋势. ...

  2. 阿里内部总结“Flutter进阶笔记”,你收藏好了嘛?

    每一个移动开发者都在为 Flutter 带来的"快速开发.富有表现力和灵活的 UI.原生性能"的特色和理念而痴狂,从超级 App 到独立应用,从纯 Flutter 到混合栈,开发者 ...

  3. Android进阶笔记:Messenger源码详解

    Messenger可以理解为一个是用于发送消息的一个类用法也很多,这里主要分析一下再跨进程的情况下Messenger的实现流程与源码分析.相信结合前面两篇关于aidl解析文章能够更好的对aidl有一个 ...

  4. Android进阶笔记:AIDL内部实现详解 (二)

    接着上一篇分析的aidl的流程解析.知道了aidl主要就是利用Ibinder来实现跨进程通信的.既然是通过对Binder各种方法的封装,那也可以不使用aidl自己通过Binder来实现跨进程通讯.那么 ...

  5. Shell 编程进阶笔记

    这几篇博文主要记录博主的Linux 学习之路,用作以后回顾和参考.大家可以选择略过也可以作参考. (一)Linux 初步笔记 (二)Linux 进阶笔记(一) (三)Linux 进阶笔记(二) (四) ...

  6. Linux 进阶笔记(二)

    这几篇博文主要记录博主的Linux 学习之路,用作以后回顾和参考.大家可以选择略过也可以作参考. (一)Linux 初步笔记 (二)Linux 进阶笔记(一) (三)Linux 进阶笔记(二) (四) ...

  7. Linux 进阶笔记(一)

    这几篇博文主要记录博主的Linux 学习之路,用作以后回顾和参考.大家可以选择略过也可以作参考. (一)Linux 初步笔记 (二)Linux 进阶笔记(一) (三)Linux 进阶笔记(二) (四) ...

  8. ggplot2箱式图两两比较_R语言进阶笔记2 | 长数据与ggplot2

    1. 长数据是什么鬼? 之前介绍了如何将多个性状的箱线图放在一个图上,比如learnasreml包中的fm数据,它有h1~h5五年的株高数据,想对它进行作图. 「数据预览:」 > library ...

  9. Flutter学习笔记(10)--容器组件、图片组件

    如需转载,请注明出处:Flutter学习笔记(10)--容器组件.图片组件 上一篇Flutter学习笔记(9)--组件Widget我们说到了在Flutter中一个非常重要的理念"一切皆为组件 ...

最新文章

  1. 【引用】整理Sed与Awk学习笔记(一)
  2. Nginx reload的时候出现的问题/usr/local/nginx/logs/nginx.pid
  3. spring的AOP配置之XML方式
  4. ZooKeeper 集群:集群概念、选举流程、机器数量
  5. cordova 人脸识别_html5与EmguCV前后端实现——人脸识别篇(一)
  6. 失去健康代表失去一切
  7. matlab 1stopt,1stOpt或者MATLAB自定义公式曲线拟合 - 计算模拟 - 小木虫 - 学术 科研 互动社区...
  8. ETL最佳实践-NiFi
  9. 代码查重实验(深大算法实验4)报告+代码
  10. Python 实现自动刷抖音,解放双手了
  11. 上海市五险一金及税后工资计算器
  12. 多家波卡生态项目招聘开发者,高薪职位等你来 Pick
  13. Ceres的Options详解
  14. 求最大公约数的4种算法(C++)
  15. Altium Designer快捷键布线无法实现网络线自动编号
  16. MATLAB中的特殊图像显示
  17. Andriod小项目——在线音乐播放器
  18. Qt实现Qchart的打印和打印预览的几种方法
  19. 智能个性化推荐系统设计
  20. 为何要从用户角度出发来思考问题

热门文章

  1. 更改IP电话的IP地址
  2. Android资源之图像资源(图层图像资源)
  3. Latex引用bib文件的一个流程
  4. Vetur can‘t find `tsconfig.json` or `jsconfig.json` in e:\决策测试
  5. windows下读取Linux分区软件
  6. javase加强(七、 不可变集合、Stream、异常)
  7. 和“僵尸”好友说拜拜,微信如何批量删除好友?
  8. 最大长方形 (Maximum Submatrix Largest Rectangle)(涵盖各种求最大矩形题目)
  9. Windows 系统下搭建 WAMP 环境
  10. CPython入门----Fork源码到自己github并下载配置本地git