1. 首页
  2. 未分类

web前端工程师必学框架:Flutter详细教程

“u003Cp class=””u003E最爱折腾的就是前端工程师了,从 jQuery 折腾到 AngularJs,再折腾到 Vue、React。最爱跨屏的也是前端工程师,从 phonegap,折腾到 React Native,这不又折腾到了 Flutter。u003Cu002Fpu003Eu003Cpu003E图啥?u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E低成本地为用户带来更优秀的用户体验。u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E目前来说Flutter可能是其中最优秀的一种方案了。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EFlutter 是什么?u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EFlutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.u003Cu002Fpu003Eu003Cpu003EFlutter是由原 Google Chrome 团队成员,利用 Chrome 2D 渲染引擎,然后精简 CSS 布局演变而来。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Fc5a1ee9c2dc2487392befaf174466d1a” img_width=”651″ img_height=”337″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E或者更详细的版本u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F8e3fc482e8324dcca6ca5ef328af6c9a” img_width=”600″ img_height=”315″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cul class=””u003Eu003Cliu003EFlutter 在各个原生的平台中,使用自己的 C++的引擎渲染界面,没有使用 webview,也不像 RN、NativeScript 一样使用系统的组件。简单来说平台只是给 Flutter 提供一个画布。u003Cu002Fliu003Eu003Cliu003E界面使用 Dart 语言开发,貌似唯一支持 JIT,和 AOT 模式的强类型语言。u003Cu002Fliu003Eu003Cliu003E写法非常的现代,声明式,组件化,Composition > inheritance,响应式……就是现在前端流行的这一套 u003Cu002Fliu003Eu003Cliu003E一套代码搞定所有平台。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrongu003EFlutter 为什么快?Flutter 相比 RN 的优势在哪里?u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E从架构中实际上已经能看出 Flutter 为什么快,至少相比之前的当红炸子鸡 React Native 快的原因了。u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003ESkia 引擎,Chrome, Chrome OS,Android,Firefox,Firefox OS 都以此作为渲染引擎。u003Cu002Fliu003Eu003Cliu003EDart 语言可以 AOT 编译成 ARM Code,让布局以及业务代码运行的最快,而且 Dart 的 GC 针对 Flutter 频繁销毁创建 Widget 做了专门的优化。u003Cu002Fliu003Eu003Cliu003ECSS 的的子集 Flex like 的布局方式,保留强大表现能力的同时,也保留了性能。u003Cu002Fliu003Eu003Cliu003EFlutter 业务书写的 Widget 在渲染之前 diff 转化成 Render Object,对,就像 React 中的 Virtual DOM,以此来确保开发体验和性能。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E而相比 React Native:u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003ERN 使用 JavaScript 来运行业务代码,然后 JS Bridge 的方式调用平台相关组件,性能比有损失,甚至平台不同 js 引擎都不一样。u003Cu002Fliu003Eu003Cliu003ERN 使用平台组件,行为一致性会有打折,或者说,开发者需要处理更多平台相关的问题。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E而具体两者的性能测试,可以看这里,结论是 Flutter,在 CPU,FPS,内存稳定上均优于 ReactNative。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EDart 语言u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E在开始 Flutter 之前,我们需要先了解下 Dart 语言……u003Cu002Fpu003Eu003Cpu003EDart 是由 Google 开发,最初是想作为 JavaScript 替代语言,但是失败沉寂之后,作为 Flutter 独有开发语言又焕发了第二春 。u003Cu002Fpu003Eu003Cpu003E实际上即使到了 2.0,Dart 语法和 JavaScriptFlutter非常的相像。单线程,Event Loop……u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F06de35de242d4ef6b322610cd2dc602c” img_width=”600″ img_height=”593″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E当然作为一篇写给前端工程师的教程,我在这里只想写写 JavaScript 中暂时没有的,Dart 中更为省心,也更“甜”的东西。u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003E不会飘的thisu003Cu002Fliu003Eu003Cliu003E强类型,当然前端现在有了 TypeScript u003Cu002Fliu003Eu003Cliu003E强大方便的操作符号:u003Cu002Fliu003Eu003Cliu003E?. 方便安全的foo?.bar取值,如果 foo 为null,那么取值为nullu003Cu002Fliu003Eu003Cliu003E?? condition ? expr1 : expr2 可以简写为expr1 ?? expr2u003Cu002Fliu003Eu003Cliu003E=和其他符号的组合: *=、~u002F=、&=、|= ……u003Cu002Fliu003Eu003Cliu003E级联操作符(Cascade notation ..)u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpreu003Eu002Fu002F 想想这样省了多少变量声明 u003Cbru003EquerySelect('#button') u003Cbru003E ..text ="Confirm" u003Cbru003E ..classes.add('important') u003Cbru003E ..onClick.listen((e) => window.alert('Confirmed')) u003Cu002Fpreu003Eu003Cpu003E甚至可以重写操作符u003Cu002Fpu003Eu003Cpreu003Eclass Vector { u003Cbru003E final int x, y; u003Cbru003E Vector(this.x, this.y); u003Cbru003E u003Cbru003E Vector operator +(Vector v) => Vector(x + v.x, y + v.y); u003Cbru003E Vector operator -(Vector v) => Vector(x – v.x, y – v.y); u003Cbru003E u003Cbru003E u002Fu002F Operator == and hashCode not shown. For details, see note below. u003Cbru003E u002Fu002F ··· u003Cbru003E} u003Cbru003E u003Cbru003Evoid main() { u003Cbru003E final v = Vector(2, 3); u003Cbru003E final w = Vector(2, 2); u003Cbru003E u003Cbru003E assert(v + w == Vector(4, 5)); u003Cbru003E assert(v – w == Vector(0, 1)); u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003E注:重写==,也需要重写 Object hashCodegetteru003Cu002Fpu003Eu003Cpreu003Eclass Person { u003Cbru003E final String firstName, lastName; u003Cbru003E Person(this.firstName, this.lastName); u003Cbru003E u003Cbru003E u002Fu002F Override hashCode using strategy from Effective Java, u003Cbru003E u002Fu002F Chapter 11. u003Cbru003E @override u003Cbru003E int get hashCode { u003Cbru003E int result = 17; u003Cbru003E result = 37 * result + firstName.hashCode; u003Cbru003E result = 37 * result + lastName.hashCode; u003Cbru003E return result; u003Cbru003E } u003Cbru003E u003Cbru003E u002Fu002F You should generally implement operator == if you u003Cbru003E u002Fu002F override hashCode. u003Cbru003E @override u003Cbru003E bool operator ==(dynamic other) { u003Cbru003E if (other is! Person) return false; u003Cbru003E Person person = other; u003Cbru003E return (person.firstName == firstName && u003Cbru003E person.lastName == lastName); u003Cbru003E } u003Cbru003E} u003Cbru003E u003Cbru003Evoid main() { u003Cbru003E var p1 = Person('Bob', 'Smith'); u003Cbru003E var p2 = Person('Bob', 'Smith'); u003Cbru003E var p3 = 'not a person'; u003Cbru003E assert(p1.hashCode == p2.hashCode); u003Cbru003E assert(p1 == p2); u003Cbru003E assert(p1 != p3); u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003E这点在 diff 对象的时候尤其有用。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003Elsolateu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EDart 运行在独立隔离的 iSolate 中就类似 JavaScript 一样,单线程事件驱动,但是 Dart 也开放了创建其他 isolate,充分利用 CPU 的多和能力。u003Cu002Fpu003Eu003Cpreu003EloadData() async { u003Cbru003E u002Fu002F 通过spawn新建一个isolate,并绑定静态方法 u003Cbru003E ReceivePort receivePort =ReceivePort(); u003Cbru003E await Isolate.spawn(dataLoader, receivePort.sendPort); u003Cbru003E u002Fu002F 获取新isolate的监听port u003Cbru003E SendPort sendPort = await receivePort.first; u003Cbru003E u002Fu002F 调用sendReceive自定义方法 u003Cbru003E List dataList = await sendReceive(sendPort, 'https:u002Fu002Fhicc.meu002Fposts'); u003Cbru003E print('dataList $dataList'); u003Cbru003E} u003Cbru003E u003Cbru003Eu002Fu002F isolate的绑定方法 u003Cbru003Estatic dataLoader(SendPort sendPort) async{ u003Cbru003E u002Fu002F 创建监听port,并将sendPort传给外界用来调用 u003Cbru003E ReceivePort receivePort =ReceivePort(); u003Cbru003E sendPort.send(receivePort.sendPort); u003Cbru003E u003Cbru003E u002Fu002F 监听外界调用 u003Cbru003E await for (var msg in receivePort) { u003Cbru003E String requestURL =msg[0]; u003Cbru003E SendPort callbackPort =msg[1]; u003Cbru003E u003Cbru003E Client client = Client(); u003Cbru003E Response response = await client.get(requestURL); u003Cbru003E List dataList = json.decode(response.body); u003Cbru003E u002Fu002F 回调返回值给调用者 u003Cbru003E callbackPort.send(dataList); u003Cbru003E } u003Cbru003E} u003Cbru003E u003Cbru003Eu002Fu002F 创建自己的监听port,并且向新isolate发送消息 u003Cbru003EFuture sendReceive(SendPort sendPort, String url) { u003Cbru003E ReceivePort receivePort =ReceivePort(); u003Cbru003E sendPort.send([url, receivePort.sendPort]); u003Cbru003E u002Fu002F 接收到返回值,返回给调用者 u003Cbru003E return receivePort.first; u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003E当然 Flutter 中封装了compute,可以方便的使用,譬如在其它 isolate 中解析大的 json。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EDart UI as Codeu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E在这里单独提出来的意义在于,从 React 开始,到 Flutter,到最近的 Apple SwiftUI,Android Jetpack Compose 声明式组件写法越发流行,Web 前端使用 JSX 来让开发者更方便的书写,而 Flutter,SwiftUI 则直接从优化语言本身着手。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E函数类的命名参数u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpreu003Evoid test({@required int age,String name}) { u003Cbru003E print(name); u003Cbru003E print(age); u003Cbru003E} u003Cbru003Eu002Fu002F 解决函数调用时候,参数不明确的问题 u003Cbru003Etest(name:"hicc",age: 30) u003Cbru003Eu002Fu002F 这样对于组件的使用尤为方便 u003Cbru003Eclass MyApp extends StatelessWidget { u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E return Scaffold( u003Cbru003E appBar: AppBar(), u003Cbru003E body: Container(), u003Cbru003E floatingActionButton:FloatingActionButton() u003Cbru003E ); u003Cbru003E } u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E大杀器:Collection If 和 Collection Foru003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpreu003Eu002Fu002F collection If u003Cbru003EWidget build(BuildContext context) { u003Cbru003E return Row( u003Cbru003E children: [ u003Cbru003E IconButton(icon: Icon(Icons.menu)), u003Cbru003E Expanded(child: title), u003Cbru003E if (!isAndroid) u003Cbru003E IconButton(icon: Icon(Icons.search)), u003Cbru003E ], u003Cbru003E ); u003Cbru003E} u003Cbru003Eu002Fu002F Collect For u003Cbru003Evar command = [ u003Cbru003E engineDartPath, u003Cbru003E frontendServer, u003Cbru003E for (var root in fileSystemRoots) "–filesystem-root=$root", u003Cbru003E for (var entryPoint in entryPoints) u003Cbru003E if (fileExists("libu002F$entryPoint.json")) "libu002F$entryPoint", u003Cbru003E mainPath u003Cbru003E]; u003Cu002Fpreu003Eu003Cpu003E更多 Dart 2.3 对此的优化看这里。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EFlutter 怎么写u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E到这里终于到正题了,如果熟悉 web 前端,熟悉 React 的话,你会对下面要讲的异常的熟悉。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002F3f671d16d6e04144b66d0de751f555c1″ img_width=”600″ img_height=”217″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003EFlutter App 的一切从libu002Fmain.dart文件的 main 函数开始:u003Cu002Fpu003Eu003Cpreu003Eimport 'package:flutteru002Fmaterial.dart'; u003Cbru003Evoid main() => runApp(MyApp()); u003Cbru003E u003Cbru003Eclass MyApp extends StatelessWidget { u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E return MaterialApp( u003Cbru003E title: 'Welcome to Flutter', u003Cbru003E home: Scaffold( u003Cbru003E appBar: AppBar( u003Cbru003E title: Text('Welcome to Flutter'), u003Cbru003E ), u003Cbru003E body: Center( u003Cbru003E child: Text('Hello World'), u003Cbru003E ), u003Cbru003E ), u003Cbru003E ); u003Cbru003E } u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003EDart 类 build 方法返回的便是 Widget,在 Flutter 中一切都是 Widget,包括但不限于u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003E结构性元素,menu,button 等u003Cu002Fliu003Eu003Cliu003E样式类元素,font,color 等u003Cu002Fliu003Eu003Cliu003E布局类元素,padding,margin 等u003Cu002Fliu003Eu003Cliu003E导航u003Cu002Fliu003Eu003Cliu003E手势u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003EWidget 是 Dart 中特殊的类,通过实例化(Dart 中new 是可选的)相互嵌套,你的这个 App 就是形如下图的一颗组件树(Dart 入口函数的概念,main.dart -> main())。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F88a32c9030a74b2c985965d7898c1eef” img_width=”600″ img_height=”337″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003Eu003Cstrongu003EWidget 布局u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E上说过 Flutter 布局思路来自 CSS,而 Flutter 中一切皆 Widget,因此整体布局也很简单:u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003E容器组件 Containeru003Cu002Fliu003Eu003Cliu003Edecoration 装饰属性,设置背景色,背景图,边框,圆角,阴影和渐变等u003Cu002Fliu003Eu003Cliu003Emarginu003Cu002Fliu003Eu003Cliu003Epaddingu003Cu002Fliu003Eu003Cliu003Ealignmentu003Cu002Fliu003Eu003Cliu003Ewidthu003Cu002Fliu003Eu003Cliu003Eheightu003Cu002Fliu003Eu003Cliu003EPadding,Centeru003Cu002Fliu003Eu003Cliu003ERow,Column,Flexu003Cu002Fliu003Eu003Cliu003EWrap, Flow 流式布局u003Cu002Fliu003Eu003Cliu003EStack, Z 轴布局u003Cu002Fliu003Eu003Cliu003E……u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003EFlutter 中 Widget 可以分为三类,形如 React 中“展示组件”、“容器组件”,“context”。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EStatelessWidgetu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E这个就是 Flutter 中的“展示组件”,自身不保存状态,外部参数变化就销毁重新创建。Flutter 建议尽量使用无状态的组件。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EStatefulWidgetu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E状态组件就是类似于 React 中的“容器组件”了,Flutter 中状态组件写法会稍微不一样。u003Cu002Fpu003Eu003Cpreu003Eclass Counter extends StatefulWidget { u003Cbru003E u002Fu002F This class is the configuration for the state. It holds the u003Cbru003E u002Fu002F values (in this case nothing) provided by the parent and used by the build u003Cbru003E u002Fu002F method of the State. Fields in a Widget subclass are always marked "final". u003Cbru003E @override u003Cbru003E _CounterState createState() => _CounterState(); u003Cbru003E} u003Cbru003E u003Cbru003Eclass _CounterState extends State<Counter> { u003Cbru003E int _counter = 0; u003Cbru003E u003Cbru003E void _increment() { u003Cbru003E setState(() { u003Cbru003E u002Fu002F This call to setState tells the Flutter framework that u003Cbru003E u002Fu002F something has changed in this State, which causes it to rerun u003Cbru003E u002Fu002F the build method below so that the display can reflect the u003Cbru003E u002Fu002F updated values. If you change _counter without calling u003Cbru003E u002Fu002F setState(), then the build method won't be called again, u003Cbru003E u002Fu002F and so nothing would appear to happen. u003Cbru003E _counter++; u003Cbru003E }); u003Cbru003E } u003Cbru003E u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E u002Fu002F This method is rerun every time setState is called, for instance u003Cbru003E u002Fu002F as done by the _increment method above. u003Cbru003E u002Fu002F The Flutter framework has been optimized to make rerunning u003Cbru003E u002Fu002F build methods fast, so that you can just rebuild anything that u003Cbru003E u002Fu002F needs updating rather than having to individually change u003Cbru003E u002Fu002F instances of widgets. u003Cbru003E return Row( u003Cbru003E children: <Widget>[ u003Cbru003E RaisedButton( u003Cbru003E onPressed: _increment, u003Cbru003E child: Text('Increment'), u003Cbru003E ), u003Cbru003E Text('Count: $_counter'), u003Cbru003E ], u003Cbru003E ); u003Cbru003E } u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003E可以看到 Flutter 中直接使用了和 React 中同名的setState方法,不过不会有变量合并的东西,当然也有生命周期。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F818aa40ef7604d6486f61bb9b9713eec” img_width=”499″ img_height=”712″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E可以看到一个有状态的组件需要两个 Class,这样写的原因在于,Flutter 中 Widget 都是 immmutable 的,状态组件的状态保存在 State 中,组件仍然每次重新创建,Widget 在这里只是一种对组件的描述,Flutter 会 diff 转换成 Element,然后转换成 RenderObject 才渲染。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002F10d41d13e437466faf59fb16119e33d8″ img_width=”600″ img_height=”348″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003EFlutter Widget 更多的渲染流程可以看这里。u003Cu002Fpu003Eu003Cpu003E实际上 Widget 只是作为组件结构一种描述,还可以带来的好处是,你可以更方便的做一些主题性的组件, Flutter 官方提供的Material Components widgets和Cupertino (iOS-style) widgets质量就相当高,再配合 Flutter 亚秒级的Hot Reload,开发体验可以说挺不错的。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EState Managementu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EsetState()可以很方便的管理组件内的数据,但是 Flutter 中状态同样是从上往下流转的,因此也会遇到和 React 中同样的问题,如果组件树太深,逐层状态创建就显得很麻烦了,更不要说代码的易读和易维护性了。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EInheritedWidgetu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E同样 Flutter 也有个context一样的东西,那就是InheritedWidget,使用起来也很简单。u003Cu002Fpu003Eu003Cpreu003Eclass GlobalData extends InheritedWidget { u003Cbru003E final int count; u003Cbru003E GlobalData({Key key, this.count,Widget child}):super(key:key,child:child); u003Cbru003E @override u003Cbru003E bool updateShouldNotify(GlobalData oldWidget) { u003Cbru003E return oldWidget.count != count; u003Cbru003E } u003Cbru003E u003Cbru003E static GlobalData of(BuildContext context) => context.inheritFromWidgetOfExactType(GlobalData); u003Cbru003E} u003Cbru003E u003Cbru003Eclass MyApp extends StatelessWidget { u003Cbru003E u002Fu002F This widget is the root of your application. u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E return MaterialApp( u003Cbru003E title: 'Flutter Demo', u003Cbru003E theme: ThemeData( u003Cbru003E primarySwatch: Colors.blue, u003Cbru003E ), u003Cbru003E home: MyHomePage(title: 'Flutter Demo Home Page'), u003Cbru003E ); u003Cbru003E } u003Cbru003E} u003Cbru003E u003Cbru003Eclass MyHomePage extends StatefulWidget { u003Cbru003E MyHomePage({Key key, this.title}) : super(key: key); u003Cbru003E u003Cbru003E final String title; u003Cbru003E u003Cbru003E @override u003Cbru003E _MyHomePageState createState() => _MyHomePageState(); u003Cbru003E} u003Cbru003E u003Cbru003Eclass _MyHomePageState extends State<MyHomePage> { u003Cbru003E int _counter = 0; u003Cbru003E u003Cbru003E void _incrementCounter() { u003Cbru003E _counter++; u003Cbru003E }); u003Cbru003E } u003Cbru003E u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E return Scaffold( u003Cbru003E appBar: AppBar( u003Cbru003E title: Text(widget.title), u003Cbru003E ), u003Cbru003E body: GlobalData( u003Cbru003E count: _counter, u003Cbru003E child: Center( u003Cbru003E child: Column( u003Cbru003E mainAxisAlignment: MainAxisAlignment.center, u003Cbru003E children: <Widget>[ u003Cbru003E Text( u003Cbru003E 'You have pushed the button this many times:', u003Cbru003E ), u003Cbru003E Text( u003Cbru003E '$_counter', u003Cbru003E style: Theme.of(context).textTheme.display1, u003Cbru003E ), u003Cbru003E Body(), u003Cbru003E Body2() u003Cbru003E ], u003Cbru003E ), u003Cbru003E ), u003Cbru003E ), u003Cbru003E floatingActionButton: FloatingActionButton( u003Cbru003E onPressed: _incrementCounter, u003Cbru003E tooltip: 'Increment', u003Cbru003E child: Icon(Icons.add), u003Cbru003E ), u003Cbru003E ); u003Cbru003E } u003Cbru003E} u003Cbru003E u003Cbru003Eclass Body extends StatelessWidget { u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E GlobalData globalData = GlobalData.of(context); u003Cbru003E return Text(globalData.count.toString()); u003Cbru003E } u003Cbru003E} u003Cbru003E u003Cbru003Eclass Body2 extends StatelessWidget { u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E u002Fu002F TODO: implement build u003Cbru003E GlobalData globalData = GlobalData.of(context); u003Cbru003E return Text(globalData.count.toString()); u003Cbru003E } u003Cu002Fpreu003Eu003Cpu003E具体实现原理可以参考这里,不过 Google 封装了一个更为上层的库provider,具体使用可以看这里。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EBlOCu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EBlOC是 Flutter team 提出建议的另一种更高级的数据组织方式,也是我最中意的方式。简单来说:u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EBloc = InheritedWidget + RxDart(Stream)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EDart 语言中内置了 Steam,Stream ~= Observable,配合RxDart, 然后加上StreamBuilder会是一种异常强大和自由的模式。u003Cu002Fpu003Eu003Cpreu003Eclass GlobalData extends InheritedWidget { u003Cbru003E final int count; u003Cbru003E final Stream<String> timeInterval$ = new Stream.periodic(Duration(seconds: 10)).map((time) => new DateTime.now().toString()); u003Cbru003E GlobalData({Key key, this.count,Widget child}):super(key:key,child:child); u003Cbru003E @override u003Cbru003E bool updateShouldNotify(GlobalData oldWidget) { u003Cbru003E return oldWidget.count != count; u003Cbru003E } u003Cbru003E u003Cbru003E static GlobalData of(BuildContext context) => context.inheritFromWidgetOfExactType(GlobalData); u003Cbru003E u003Cbru003E} u003Cbru003E u003Cbru003Eclass TimerView extends StatelessWidget { u003Cbru003E u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E GlobalData globalData = GlobalData.of(context); u003Cbru003E return StreamBuilder( u003Cbru003E stream: globalData.timeInterval$, u003Cbru003E builder: (context, snapshot) { u003Cbru003E return Text(snapshot?.data ?? ''); u003Cbru003E } u003Cbru003E ); u003Cbru003E } u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003E当然 Bloc 的问题在于u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003E学习成本略高,Rx 的概念要吃透,不然你会抓狂u003Cu002Fliu003Eu003Cliu003E自由带来的问题是,可能代码不如 Redux 类的规整。u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E顺便,今年 Apple 也拥抱了响应式,Combine(Rx like) + SwiftUI 也基本等于 Bloc 了。u003Cu002Fpu003Eu003Cpu003E所以,Rx 还是要赶紧学起来 u003Cu002Fpu003Eu003Cpu003E除去 Bloc,Flutter 中还是可以使用其他的方案,譬如:u003Cu002Fpu003Eu003Cul class=””u003Eu003Cliu003EFlutter Reduxu003Cu002Fliu003Eu003Cliu003E阿里闲鱼的Fish Redux,据说性能很好。u003Cu002Fliu003Eu003Cliu003EMobxu003Cu002Fliu003Eu003Cliu003E……u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E展开来说现在的前端开发使用强大的框架页面组装已经不是难点了。开发的难点在于如何组合富交互所需的数据,也就是上面图中的state部分。u003Cu002Fpu003Eu003Cpu003E更具体来说,是怎么优雅,高效,易维护地处理短暂数据(ephemeral state)setState()和需要共享的 App State 的问题,这是个工程性的问题,但往往也是日常开发最难的事情了,引用 Redux 作者 Dan 的一句:u003Cu002Fpu003Eu003Cpu003E“The rule of thumb is:Do whatever is less awkward.”u003Cu002Fpu003Eu003Cpu003E到这里,主要的部分已经讲完了,有这些已经可以开发出一个不错的 App 了。剩下的就当成一个 bonus 吧。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E测试u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EFlutter debugger,测试都是出场自带,用起来也不难。u003Cu002Fpu003Eu003Cpreu003Eu002Fu002F 测试在u002Ftestu002F目录下面 u003Cbru003Evoid main() { u003Cbru003E testWidgets('Counter increments smoke test', (WidgetTester tester) async { u003Cbru003E u002Fu002F Build our app and trigger a frame. u003Cbru003E await tester.pumpWidget(MyApp()); u003Cbru003E u003Cbru003E u002Fu002F Verify that our counter starts at 0. u003Cbru003E expect(find.text('0'), findsOneWidget); u003Cbru003E expect(find.text('1'), findsNothing); u003Cbru003E u003Cbru003E u002Fu002F Tap the '+' icon and trigger a frame. u003Cbru003E await tester.tap(find.byIcon(Icons.add)); u003Cbru003E await tester.pump(); u003Cbru003E u003Cbru003E u002Fu002F Verify that our counter has incremented. u003Cbru003E expect(find.text('0'), findsNothing); u003Cbru003E expect(find.text('1'), findsOneWidget); u003Cbru003E }); u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E包管理,资源管理u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E类似与 JavaScript 的 npm,Flutter,也就是 Dart 也有自己的包仓库。不过项目包的依赖使用 yaml 文件来描述:u003Cu002Fpu003Eu003Cpreu003Ename: app u003Cbru003Edescription: A new Flutter project. u003Cbru003Eversion: 1.0.0+1 u003Cbru003Eenvironment: u003Cbru003E sdk: ">=2.1.0 <3.0.0" u003Cbru003E u003Cbru003Edependencies: u003Cbru003E flutter: u003Cbru003E sdk: flutter u003Cbru003E u003Cbru003E cupertino_icons: ^0.1.2 u003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E生命周期u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E移动应用总归需要应用级别的生命周期,flutter 中使用生命周期钩子,也非常的简单:u003Cu002Fpu003Eu003Cpreu003Eclass MyApp extends StatefulWidget { u003Cbru003E @override u003Cbru003E _MyAppState createState() => new _MyAppState(); u003Cbru003E} u003Cbru003Eclass _MyAppState extends State<MyApp> with WidgetsBindingObserver { u003Cbru003E @override u003Cbru003E void initState() { u003Cbru003E super.initState(); u003Cbru003E WidgetsBinding.instance.addObserver(this); u003Cbru003E } u003Cbru003E u003Cbru003E @override u003Cbru003E void dispose() { u003Cbru003E WidgetsBinding.instance.removeObserver(this); u003Cbru003E super.dispose(); u003Cbru003E } u003Cbru003E u003Cbru003E @override u003Cbru003E void didChangeAppLifecycleState(AppLifecycleState state) { u003Cbru003E switch (state) { u003Cbru003E case AppLifecycleState.inactive: u003Cbru003E print('AppLifecycleState.inactive'); u003Cbru003E break; u003Cbru003E case AppLifecycleState.paused: u003Cbru003E print('AppLifecycleState.paused'); u003Cbru003E break; u003Cbru003E case AppLifecycleState.resumed: u003Cbru003E print('AppLifecycleState.resumed'); u003Cbru003E break; u003Cbru003E case AppLifecycleState.suspending: u003Cbru003E print('AppLifecycleState.suspending'); u003Cbru003E break; u003Cbru003E } u003Cbru003E super.didChangeAppLifecycleState(state); u003Cbru003E } u003Cbru003E u003Cbru003E @override u003Cbru003E Widget build(BuildContext context) { u003Cbru003E return Container(); u003Cbru003E } u003Cbru003E} u003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E使用原生能力u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E和 ReactNative 类似,Flutter 也是使用类似事件的机制来使用平台相关能力。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F60f75bd628d74c40bf7914d32ec37da8″ img_width=”580″ img_height=”647″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003EFlutter Web, Flutter Desktopu003Cu002Fpu003Eu003Cpu003E这些还在开发当中,鉴于对 Dart 喜欢,以及对 Flutter 性能的乐观,这些倒是很值得期待。u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F3901e2aabb2547eeb52d968ba2e8ddc6″ img_width=”600″ img_height=”246″ alt=”web前端工程师必学框架:Flutter详细教程” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:web前端工程师必学框架:Flutter详细教程

主题测试文章,只做测试使用。发布者:战鹰,转转请注明出处:http://www.cxybcw.com/26180.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code