<简书 — 刘小壮> https://www.jianshu.com/p/1a90adc09e99前端
Flutter
是Google
开发的新一代跨平台方案,Flutter
能够实现写一份代码同时运行在iOS和Android设备上,而且提供很好的性能体验。Flutter
使用Dart
做为开发语言,这是一门简洁、强类型的编程语言。Flutter
对于iOS和Android设备,提供了两套视觉库,能够针对不一样的平台有不一样的展现效果。git
Flutter
本来是为了解决Web
开发中的一些问题,而开发的一套精简版Web
框架,拥有独立的渲染引擎和开发语言,但后来逐渐演变为移动端开发框架。正是因为Dart
当初的定位是为了替代JS
成为Web
框架,因此Dart
的语法更接近于JS
语法。例如定义对象构建方法,以及实例化对象的方式等。github
在Google
刚推出Flutter
时,其发展很缓慢,终于在18年发布第一个Bate版以后迎来了爆发性增加,发布第一个Release
版时增加速度更快。能够从Github上Star数据看出来这个增加的过程。在19年最新的Flutter 1.2
版本中,已经开放Web
支持的Beta版。express
目前已经有很多大型项目接入Flutter
,阿里的咸鱼、头条的抖音、腾讯的NOW直播,都将Flutter
当作应用程序的开发语言。除此以外,还有一些其余中小型公司也在作。编程
Flutter
能够理解为开发SDK或者工具包,其经过Dart
做为开发语言,而且提供Material
和Cupertino
两套视觉控件,视图或其余和视图相关的类,都以Widget
的形式表现。Flutter
有本身的渲染引擎,并不依赖原平生台的渲染。Flutter
还包含一个用C++
实现的Engine
,渲染也是包含在其中的。json
Flutter
是一套全新的跨平台方案,Flutter
并不像React Native
那样,依赖原生应用的渲染,而是本身有本身的渲染引擎,并使用Dart
当作Flutter
的开发语言。Flutter
总体框架分为两层,底层是经过C++
实现的引擎部分,Skia
是Flutter
的渲染引擎,负责跨平台的图形渲染。Dart
做为Flutter
的开发语言,在C++
引擎上层是Dart
的Framework
。数组
Flutter
不只仅提供了一套视觉库,在Flutter
总体框架中包含各个层级阶段的库。例如实现一个游戏功能,上面一些游戏控件能够用上层视觉库,底层游戏能够直接基于Flutter
的底层库进行开发,而不须要调用原生应用的底层库。Flutter
的底层库是基于Open GL
实现的,因此Open GL
能够作的Flutter
均可以。服务器
在上层Framework
中包含两套视觉库,符合Android
风格的Material
,和符合iOS风格的Cupertino
。也能够在此基础上,封装本身风格的系统组件。Cupertino
是一套iOS风格的视觉库,包含iOS的导航栏、button
、alertView
等。网络
Flutter
对不一样硬件平台有不一样的兼容,例如一样的Material
代码运行在iOS和Android不一样平台上,有一些平台特有的显示和交互,Flutter
依然对其进行了区分适配。例如滑动ScrollView
时,iOS平台是有回弹效果的,而Android平台则是阻尼效果。例如iOS的导航栏标题是居中的,Android导航栏标题是向左的,等等。这些Flutter
都作了区分兼容。架构
除了Flutter
为咱们作的一些适配外,有一些控件是须要咱们本身作适配的,例如AlertView
,在Android和iOS两个平台下的表现就是不一样的。这些iOS特性的控件都定义在Cupertino
中,因此建议在进行App开发时,对一些控件进行上层封装。
例如AlertView
则对其进行一个二次封装,控件内部进行设备判断并选择不一样的视觉库,这样能够保证各个平台的效果。
虽然Flutter
对于iOS和Android两个平台,开发有cupertino
和material
两个视觉库,但实际开发过程当中的选择,应该使用material
当作视觉库。由于Flutter
对iOS的支持并非很好,主要对Android平台支持比较好,material
中的UI控件要比cupertino
多好几倍。
Dart是Google
在2011年推出的一款应用于Web
开发的编程语言,Dart
刚推出的时候,定位是替代JS
作前端开发,后来逐步扩展到移动端和服务端。
Dart
是Flutter
的开发语言,Flutter
必须遵循Dart
的语言特性。在此基础上,也会有本身的东西,例如Flutter
的上层Framework
,本身的渲染引擎等。能够说,Dart
只是Flutter
的一部分。
Dart
是强类型的,对定义的变量不须要声明其类型,Flutter
会对其进行类型推导。若是不想使用类型推导,也能够本身声明指定的类型。
Flutter
支持亚秒级热重载,Android Studio
和VSCode
都支持Hot Reload
的特性。但须要区分的是,热重载和热更新是不一样的两个概念,热重载是在运行调试状态下,将新代码直接更新到执行中的二进制。而热更新是在上线后,经过Runtime
或其余方式,改变现有执行逻辑。
Flutter
支持AOT
(Ahead of time
)和JIT
(Just in time
)两种编译模式,JIT
模式支持在运行过程当中进行Hot Reload
。刷新过程是一个增量的过程,由系统对本次和上次的代码作一次snapshot
,将新的代码注入到DartVM
中进行刷新。但有时会不能进行Hot Reload
,此时进行一次全量的Hot Reload
便可。
而AOT
模式则是在运行前预先编译好,这样在每次运行过程当中就不须要进行分析、编译,此模式的运行速度是最快的。Flutter
同时采用了两种方案,在开发阶段采用JIT
模式进行开发,在release
阶段采用AOT
模式,将代码打包为二进制进行发布。
在开发原生应用时,每次修改代码后都须要从新编译,而且运行到硬件设备上。因为Flutter
支持Hot Reload
,能够进行热重载,对项目的开发效率有很大的提高。
因为Flutter
实现机制支持JIT
的缘由,理论上来讲是支持热更新以及服务器下发代码的。能够从服务器。可是因为这样会使性能变差,并且还有审核的问题,因此Flutter
并无采用这种方案。
Flutter
的热重载是基于State
的,也就是咱们在代码中常常出现的setState
方法,经过这个来修改后,会执行相应的build
方法,这就是热重载的基本过程。
Flutter
的hot reload
的实现源码在下面路径中,在此路径中包含run_cold.dart
和run_hot.dart
两个文件,前者负责冷启动,后者负责热重载。
~/flutter/packages/flutter_tools/lib/src/run_hot.dart
热重载的代码实如今run_hot.dart
文件中,有HotRunner
来负责具体代码执行。当Flutter
进行热重载时,会调用restart
函数,函数内部会传入一个fullRestart
的bool
类型变量。热重载分为全量和非全量,fullRestart
参数就是表示是否全量。以非全量热重载为例,函数的fullRestart
会传入false
,根据传入false
参数,下面是部分核心代码。
Future<OperationResult> restart({ bool fullRestart = false, bool pauseAfterRestart = false, String reason }) async { if (fullRestart) { // ..... } else { final bool reloadOnTopOfSnapshot = _runningFromSnapshot; final String progressPrefix = reloadOnTopOfSnapshot ? 'Initializing' : 'Performing'; final Status status = logger.startProgress( '$progressPrefix hot reload...', progressId: 'hot.reload' ); OperationResult result; try { result = await _reloadSources(pause: pauseAfterRestart, reason: reason); } finally { status.cancel(); } } }
调用restart
函数后,内部会调用_reloadSources
函数,去执行内部逻辑。下面是大概逻辑执行流程。
在_reloadSources
函数内部,会调用_updateDevFS
函数,函数内部会扫描修改的文件,并将文件修改先后进行对比,随后会将被改动的代码生成一个kernel files
文件。
随后会经过HTTP Server
将生成的kernel files
文件发送给Dart VM
虚拟机,虚拟机拿到kernel
文件后会调用_reloadSources
函数进行资源重载,将kernel
文件注入正在运行的Dart VM
中。当资源重载完成后,会调用RPC接口触发Widgets
的重绘。
如今市面上RN、Weex的技术方案基本同样,因此这里就以RN来表明相似的跨平台方案。Flutter
是基于GPU
进行渲染的,而RN则将渲染交给原平生台,而本身只是负责经过JSCore
将视图组织起来,并处理业务逻辑。因此在渲染效果和性能这块,Flutter
的性能比RN要强不少。
跨平台方案通常都须要对各个平台进行平台适配,也就是建立各自平台的适配层,RN的平台适配层要比Flutter
要大不少。由于从技术实现来讲,RN是经过JSCore
引擎进行原生代码调用的,和原生代码交互不少,因此须要更多的适配。而Flutter
则只须要对各自平台独有的特性进行适配便可,例如调用系统相册、粘贴板等。
Flutter
技术实现是基于更底层实现的,对平台依赖度不是很高,相对来讲,RN对平台的依赖度是很高的。因此RN将来的技术升级,包括扩展之类的,都会受到很大的限制。而Flutter
将来的潜力将会很大,能够作不少技术改进。
在Flutter
中将显示以及和显示相关的部分,都统必定义为widget
,下面列举一些widget
包含的类型:
ListView
、Text
、Container
等。Transform
等动画相关。Center
、Expanded
、Column
等。在Flutter
中,全部的视图都是由Widget
组成,Label
、AppBar
、ViewController
等。在Flutter
的设计中,组合的优先级要大于继承,总体视图类结构继承层级很浅但单层不少类。若是想定制或封装一些控件,也应该以组合为主,而不是继承。
在iOS开发中,我也常常采用这种设计方案,组合大于继承。由于若是继承层级过多的话,一个是不便于阅读代码,还有就是很差维护代码。例如底层须要改一个通用的样式,但这个类的继承层级比较复杂,这样改动的话影响范围就比较大,会将一些不须要改的也改掉,这时候就会发现继承很鸡肋。但在iOS中有Category
的概念,这也是一种组合的方式,能够经过将一些公共的东西放在Category
中,使继承的方便性和组合的灵活性达到一个平衡。
Flutter
中并无单独的布局文件,例如iOS的XIB这种,代码都在Widget
中定义。和UIView
的区别在于,Widget
只是负责描述视图,并不参与视图的渲染。UIView
也是负责描述视图,而UIView
的layer
则负责渲染操做,这是两者的区别。
在应用程序启动时,main
方法接收一个Widget
当作主页面,因此任何一个Widget
均可以当作根视图。通常都是传一个MaterialApp
,也能够传一个Container
当作根视图,这都是被容许的。
在Flutter
应用中,和界面显示及用户交互的对象都是由Widget
构成的,例如视图、动画、手势等。Widget
分为StatelessWidget
和StatefulWidget
两种,分别是无状态和有状态的Widget
。
StatefulWidget
本质上也是无状态的,其经过State
来处理Widget
的状态,以达到有状态,State
出如今整个StatefulWidget
的生命周期中。
当构建一个Widget
时,能够经过其build
得到构建流程,在构建流程中能够加入本身的定制操做,例如对其设置title或视图等。
return Scaffold( appBar: AppBar( title: Text('ListView Demo'), ), body: ListView.builder( itemCount: dataList.length, itemBuilder: (BuildContext context, int index) { return Text(dataList[index]); }, ), );
有些Widget
在构建时,也提供一些参数来帮助构建,例如构建一个ListView
时,会将index
返回给build
方法,来区别构建的Cell,以及构建的上下文context
。
itemBuilder: (BuildContext context, int index) { return Text(dataList[index]); }
StatelessWidget
是一种静态Widget
,即建立后自身就不能再进行改变。在建立一个StatelessWidget
后,须要重写build
函数。每一个静态Widget
都会有一个build
函数,在建立视图对象时会调用此方法。一样的,此函数也接收一个Widget
类型的返回值。
class RectangleWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Center ( // UI Code ); } }
Widget
本质上是不可被改变的,但StatefulWidget
将状态拆分到State
中去管理,当数据发生改变时由State
去处理视图的改变。
下面是建立一个动态Widget
,当建立一个动态Widget
须要配合一个State
,而且须要重写createState
方法。重写此函数后,指定一个Widget
对应的State
并初始化。
下面例子中,在StatefulWidget
的父类中包含一个Key类型的key
变量,这是不管静态Widget
仍是动态Widget
都具有的参数。在动态Widget
中定义了本身的成员变量title,并在自定义的初始化方法中传入,经过下面DynamicWidget
类的构造方法,并不须要在内部手动进行title的赋值,title即为传入的值,是由系统完成的。
class DynamicWidget extends StatefulWidget { DynamicWidget({Key key, this.title}) : super (key : key); final String title; @override DynamicWidgetState createState() => new DynamicWidgetState(); }
因为上面动态Widget
定义了初始化方法,在调用动态Widget
时能够直接用自定义初始化方法便可。
DynamicWidget(key: 'key', title: 'title');
StatefulWidget
的改变是由State
来完成的,State
中须要重写build
方法,在build
中进行视图组织。StatefulWidget
是一种响应式视图改变的方式,数据源和视图产生绑定关系,由数据源驱动视图的改变。
改变StatefulWidget
的数据源时,须要调用setState
方法,并将数据源改变的操做写在里面。使用动态Widget
后,是不须要咱们手动去刷新视图的。系统在setState
方法调用后,会从新调用对应Widget
的build
方法,从新绘制某个Widget
。
下面的代码示例中添加了一个float按钮,并给按钮设置了一个回调函数_onPressAction
,这样在每次触发按钮事件时都会调用此函数。counter
是一个整型变量并和Text
相关联,当counter
的值在setState
方法中改变时,Text Widget
也会跟着变化。
class DynamicWidgetState extends State<DynamicWidget> { int counter = 0; void _onPressAction() { setState(() { counter++; }); } @override Widget build(BuildContext context) { return new Scaffold( body: Center( child: Text('Button tapped $_counter.') ), floatingActionButton: FloatingActionButton( onPressed: _onPressAction, tooltip: 'Increment', child: Icon(Icons.add) ) ); } }
在iOS中有UINavigationController
的概念,其并不负责显示,而是负责控制各个页面的跳转操做。在Flutter
中能够将MaterialApp
理解为iOS的导航控制器,其包含一个navigationBar
以及导航栈,这和iOS是同样的。
在iOS中除了用来显示的视图外,视图还有对应的UIViewController
。在Flutter
中并无专门用来管理视图而且和View
一对一的类,但从显示的角度来讲,有相似的类Scaffold
,其包含控制器的appBar
,也能够经过body
设置一个widget
当作其视图。
theme
是Flutter
提供的界面风格API,MaterialApp
提供有theme
属性,能够在MaterialApp
中设置全局样式,这样能够统一整个应用的风格。
new MaterialApp( title: title, theme: new ThemeData( brightness: Brightness.dark, primaryColor: Colors.lightBlue[800], accentColor: Colors.cyan[600], ) );
若是不想使用系统默认主题,能够将对应的控件或试图用Theme
包起来,并将Theme
当作Widget
赋值给其余Widget
。
new Theme( data: new ThemeData( accentColor: Colors.yellow, ), child: new FloatingActionButton( onPressed: () {}, child: new Icon(Icons.add), ), );
有时MaterialApp
设定的统一风格,并不能知足某个Widget
的要求,可能还须要有其余的外观变化,能够经过Theme.of
传入当前的BuildContext
,来对theme
进行扩展。
Flutter
会根据传入的context
,顺着Widget
树查找最近的Theme
,并对Theme
复制一份防止影响原有的Theme
,并对其进行扩展。
new Theme( data: Theme.of(context).copyWith(accentColor: Colors.yellow), child: new FloatingActionButton( onPressed: null, child: new Icon(Icons.add), ), );
Flutter
中能够经过async
、await
组合使用,进行网络请求。Flutter
中的网络请求大致有三种:
HttpClient
网络请求,缺点是代码量相对而言比较多,并且对post请求支持不是很好。http.dart
,请求简单。dio
,请求简单。http
网络库定义在http.dart
中,内部代码定义很全,包括HttpStatus
、HttpHeaders
、Cookie
等不少基础信息,有助于咱们了解http
请求协议。
由于是三方库,因此须要在pubspec.yaml
中加入下面的引用。
http: '>=0.11.3+12'
下面是http.dart
的请求示例代码,能够看到请求很简单,真正的请求代码其实就两行。生成一个Client
请求对象,调用client
实例的get方法(若是是post则调用post方法),并用Response
对象去接收请求结果便可。
经过async
修饰发起请求的方法,表示这是一个异步操做,并在请求代码的前面加入await
,修饰这里的代码须要等待数据返回,须要过一段时间后再处理。
请求回来的数据默认是json
字符串,须要对其进行decode
并解析为数据对象才可使用,这里使用系统自带的convert
库进行解析,并解析为数组。
import 'package:http/http.dart' as http; class RequestDemoState extends State<MyHomePage> { List dataList = []; @override void initState() { super.initState(); loadData(); } // 发起网络请求 loadData() async{ String requestURL = 'https://jsonplaceholder.typicode.com/posts'; Client client = Client(); Response response = await client.get(requestURL); String jsonString = response.body; setState(() { // 数据解析 dataList = json.decode(jsonString); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title) ), body: ListView.builder( itemCount: dataList.length, itemBuilder: (BuildContext context, int index) { return Text(dataList[index]['title']); }, ), ); } }
在调用Client
进行post数据请求时,须要传入一个字典进去,Client
会经过将字典当作post的from
表单。
void requestData() async { var params = Map<String, String>(); params["username"] = "lxz"; params["password"] = "123456"; var client = http.Client(); var response = await client.post(url_post, body: params); _content = response.body; }
dio
库的调用方式和http
库相似,这里不过多介绍。dio
库相对于http
库强大的在于,dio
库提供了更好的Cookie
管理、文件的上传下载、fromData
表单等处理。因此,若是对网络库需求比较复杂的话,仍是建议使用dio
。
// 引入外部依赖 dio: ^1.0.9
系统自带有convert
解析库,在使用时直接import
便可。convert
相似于iOS自带的JSON解析类NSJSONSerialization
,能够直接将json字符串解析为字典或数组。
import 'dart:convert'; // 解析代码 dataList = json.decode(jsonString);
可是,咱们在项目中使用时,通常都不会直接使用字典取值,这是一种很很差的作法。通常都会将字典或数组转换为模型对象,在项目中使用模型对象。能够定义相似Model.dart
这样的模型类,并在模型类中进行数据解析,对外直接暴露公共变量来让外界获取值。
但若是定义模型类的话,一个是要在代码内部写取值和赋值代码,这些都须要手动完成。另外若是当服务端字段发生改变后,客户端也须要跟着进行改变,因此这种方式并非很灵活。
能够采用json序列化的三方库json_serializable
,此库能够将一个类标示为自动JSON序列化的类,并对类提供JSON和对象相互转换的能力。也能够经过命令行开启一个watch
,当类中的变量定义发生改变时,相关代码自动发生改变。
首先引入下面的三个库,其中包括依赖库一个,以及调试库两个。
dependencies: json_annotation: ^2.0.0 dev_dependencies: build_runner: ^1.0.0 json_serializable: ^2.0.0
定义一个模型文件,例如这里叫作User.dart
文件,并在内部定义一个User
的模型类。随后引入json_annotation
的依赖,经过@JsonSerializable()
标示此类须要被json_serializable
进行合成。
定义的User
类包含两部分,实例变量和两个转换函数。在下面定义json转换函数时,须要注意函数命名必定要按照下面格式命名,不然不能正常生成user.g.dart
文件。
import 'package:json_annotation/json_annotation.dart'; // 定义合成后的新文件为user.g.dart part 'user.g.dart'; @JsonSerializable() class User { String name; int age; String email; factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); }
下面就是user.dart
指定生成的user.g.dart
文件,其中包含JSON和对象相互转换的代码。
part of 'user.dart'; User _$UserFromJson(Map<String, dynamic> json) { return User( json['name'] as String, json['age'] as int, json['email'] as String); } Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{ 'name': instance.name, 'age': instance.age, 'email': instance.email };
有的时候服务端返回的参数名和本地的关键字冲突,或者命名不规范,致使本地定义和服务器字段不一样的状况。这种状况能够经过@JsonKey
关键字,来修饰json字段匹配新的本地变量。除此以外,也能够作其余修饰,例如变量不能为空等。
@JsonKey(name: 'id') final int user_id;
如今项目中依然是报错的,随后咱们在flutter
工程的根目录文件夹下,运行下面命令。
flutter packages pub run build_runner watch
此命令的好处在于,其会在后台监听模型类的定义,当模型类定义发生改变后,会自动修改本地源码以适配新的定义。以文中User
类为例,当User.dart
文件发生改变后,使用Cmd+s
保存文件,随后VSCode会将自定改变user.g.dart
文件的定义,以适配新的变量定义。
这是一个系统文件,Flutter
经过.packages
文件来管理一些系统依赖库,例如material
、cupertino
、widgets
、animation
、gesture
等系统库就在里面,这些主要的系统库由.packages
下的flutter
统一管理,源码都在flutter/lib/scr
目录下。除此以外,还有一些其余的系统库或系统资源都在.packages
中。
在Flutter
中经过pubspec.yaml
文件来管理外部引用,包含本地资源文件、字体文件、依赖库等依赖,以及应用的一些配置信息。这些配置在项目中时,须要注意代码对其的问题,不然会致使加载失败。
当修改yaml
文件的依赖信息后,须要执行flutter get packages
命令更新本地文件。但并不须要这么麻烦,能够直接Cmd+s
保存文件,VSCode编译器会自动更新依赖。
// 项目配置信息 name: WeChat description: Tencent WeChat App. version: 1.0.0+1 // 常规依赖 dependencies: flutter:125864 sdk: flutter cupertino_icons: ^0.1.2 english_words: ^3.1.0 // 开发依赖 dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true // 图片依赖 assets: - assets/images/ic_file_transfer.png - assets/images/ic_fengchao.png // 字体依赖 fonts: - family: appIconFont fonts: - asset: assets/fonts/iconfont.ttf
和大多数编程语言同样,dart
也包含一个main
方法,是Flutter
程序执行的主入口,在main
方法中写的代码就是在程序启动时执行的代码。main
方法中会执行runApp
方法,runApp
方法相似于iOS的UIApplicationMain
方法,runApp
函数接收一个Widget
用来作应用程序的显示。
void main() { runApp() // code }
在iOS中经过AppDelegate
能够获取应用程序的生命周期回调,在Flutter
中也能够获取到。能够经过向Binding
添加一个Observer
,并实现didChangeAppLifecycleState
方法,来监听指定事件的到来。
可是因为Flutter
提供的状态有限,在iOS平台只能监听三种状态,下面是示例代码。
class LifeCycleDemoState extends State<MyHomePage> with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); switch (state) { case AppLifecycleState.inactive: print('Application Lifecycle inactive'); break; case AppLifecycleState.paused: print('Application Lifecycle paused'); break; case AppLifecycleState.resumed: print('Application Lifecycle resumed'); break; default: print('Application Lifecycle other'); } } }
在Flutter
中是支持矩阵变化的,例如rotate
、scale
等方式。Flutter
的矩阵变换由Widget
完成,须要进行矩阵变换的视图,在外面包一层Transform Widget
便可,内部能够设置其变换方式。
child: Container( child: Transform( child: Container( child: Text( "Lorem ipsum", style: TextStyle(color: Colors.orange[300], fontSize: 12.0), textAlign: TextAlign.center, ), decoration: BoxDecoration( color: Colors.red[400], ), padding: EdgeInsets.all(16.0), ), alignment: Alignment.center, transform: Matrix4.identity() ..rotateZ(15 * 3.1415927 / 180), ), width: 320.0, height: 240.0, color: Colors.grey[300], )
在Transform
中能够经过transform
指定其矩阵变换方式,经过alignment
指定变换的锚点。
在iOS中能够经过UINavigationController
对页面进行管理,控制页面间的push、pop跳转。Flutter
中使用Navigator
和Routers
来实现相似UINavigationController
的功能,Navigator
负责管理导航栈,包含push、pop的操做,能够把UIViewController
看作一个Routers
,Routers
被Navigator
管理着。
Navigator
的跳转方式分为两种,一种是直接跳转到某个Widget
页面,另外一种是为MaterialApp
构建一个map
,经过key
来跳转对应的Widget
页面。map
的格式是key : context
的形式。
void main() { runApp(MaterialApp( home: MyAppHome(), // becomes the route named '/' routes: <String, WidgetBuilder> { '/a': (BuildContext context) => MyPage(title: 'page A'), '/b': (BuildContext context) => MyPage(title: 'page B'), '/c': (BuildContext context) => MyPage(title: 'page C'), }, )); }
跳转时经过pushNamed
指定map
中的key
,便可跳转到对应的Widget
。若是须要从push出来的页面获取参数,能够经过await
修饰push操做,这样便可在新页面pop的时候将参数返回到当前页面。
Navigator.of(context).pushNamed('/b'); Map coordinates = await Navigator.of(context).pushNamed('/location'); Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
VSCode有很好的语法检查,若是有命名不规范等问题,都会以警告的形式表现出来。
Flutter
中建立Widget
对象,能够用new
修饰,也能够不用。child: new Container( child: Text( 'Hello World', style: TextStyle(color: Colors.orange, fontSize: 15.0) ) )
下面是一个函数定义,这里定义了一个必要参数url
,以及一个Map
类型的可选参数headers
。
Future<Response> get(url, {Map<String, String> headers});
Dart
中在函数定义前加下划线,则表示是私有方法或变量。Dart
经过import
引入外部引用,除此以外也能够经过下面的语法单独引入文件中的某部分。import "dart:collection" show HashMap, IterableBase;
在Dart
中常常能够看到=>
的调用方式,这种调用方式相似于一种语法糖,下面是一些经常使用的调用方式。
当进行函数调用时,能够将普通函数调用转换为=>
的调用方式,例以下面第一个示例。在此基础上,若是调用函数只有一个参数,能够将其改成第二个示例的方式,也就是能够省略调用的括号,直接写参数名。
(单一参数) => {函数声明} elements.map((element) => { return element.length; }); 单一参数 => {函数声明} elements.map(element => { return element.length; });
当只有一个返回值,而且没有逻辑处理时,能够直接省略return
,返回数值。
(参数1, 参数2, …, 参数N) => 表达式 elements.map(element => element.length);
当调用的函数中没有参数时,能够直接省略参数,写一对空括号便可。
() => {函数实现}
VSCode支持对Dart
语言进行重构,通常做用范围都是在函数内小范围的。
例如在建立Widget
对象的地方,将鼠标焦点放在这里,当前行的最前面会有提示。点击提示后会有下面两个选项:
Extract Local Variable
将当前Widget
及其子Widget
建立的代码,剥离到一个变量中,并在当前位置使用这个变量。
Extract Method
将当前Widget
及其子Widget
建立的代码,封装到一个函数中,并在当前位置调用此函数。
除此以外,将鼠标焦点放在方法的一行,点击最前面的提示,会出现下面两个选项:
Convert to expression body
将当前函数转换为一个表达式。
Convert to async function body
将当前函数转换为一个异步线程中执行的代码。
在Dart
中添加任何附加效果,例如动画效果或矩阵转换,除了直接给Widget
子类的属性赋值外,就是在被当前Widget
外面包一层,就可使当前Widget
拥有对应的效果。
// 动画效果 floatingActionButton: FloatingActionButton( tooltip: 'Fade', child: Icon(Icons.brush), onPressed: () { controller.forward(); }, ), // 矩阵转换 Transform( child: Container( child: Text( "Lorem ipsum", style: TextStyle(color: Colors.orange[300], fontSize: 12.0), textAlign: TextAlign.center, ) ), alignment: Alignment.center, transform: Matrix4.identity() ..rotateZ(15 * 3.1415927 / 180), ),
Cmd + Shift + p
:能够进行快速搜索。须要注意的是,默认是带有一个>
的,这样搜索结果主要是dart
代码。若是想搜索其余配置文件,或者安装插件等操做,须要把>
去掉。Cmd + Shift + o
:能够在某个文件中搜索某个类,但前提是须要提早进入这个文件。例如进入framework.dart
,搜索StatefulWidget
类。Flutter
要注意代码缩进,若是缩进有问题可能会影响最后的结果,尤为是在.yaml
中写配置文件的时候。Flutter
是开源的,因此遇到问题后能够进入源码中,找解决方案。Stack
的代码,若是上面是以逗号结尾,则后面的建立会失败,若是上面是以分号结尾则没问题。Widget unreadMsgText = Container( width: Constants.UnreadMsgNotifyDotSize, height: Constants.UnreadMsgNotifyDotSize, child: Text( conversation.unreadMsgCount.toString(), style: TextStyle( color: Color(AppColors.UnreadMsgNotifyTextColor), fontSize: 12.0 ), ), ); avatarContainer = Stack( overflow: Overflow.visible, children: <Widget>[ avatar ], );
简书因为排版的问题,阅读体验并很差,布局、图片显示、代码等不少问题。因此建议到我Github
上,下载Flutter编程指南 PDF
合集。把全部Flutter
文章总计三篇,都写在这个PDF
中,并且左侧有目录,方便阅读。
下载地址:Flutter编程指南 PDF麻烦各位大佬点个赞,谢谢!😁