String name; String tag; //默认构造方法,赋值给name和tag ModelA(this.name, this.tag); //返回一个空的ModelA ModelA.empty(); //返回一个设置了name的ModelA ModelA.forName(this.name); }
Flutter
Flutter 中支持 async/await 。这一点和 ES7 很像,如下代码所示,只是定义的位置不同。同时异步操作也和 ES6 中的Promise 很像,只是 Flutter 中返回的是 Future 对象,通过 then 可以执行下一步。如果返回的还是 Future 便可以 then().then.() 的流式操作了 。
///模拟等待两秒,返回OK request() async { await Future.delayed(Duration(seconds: 1)); return “ok!”; } ///得到"ok!"后,将"ok!“修改为"ok from request” doSomeThing() async { String data = await request(); data = “ok from request”; return data; } ///打印结果 renderSome() { doSomeThing().then((value) { print(value); ///输出ok from request }); }
Flutter 中 setState 很有 React Native 的既视感,Flutter 中也是通过 state 跨帧实现管理数据状态的,这个后面会详细讲到。
Flutter 中一切皆 Widget 呈现,通过 build方法返回 Widget,这也是和 React Native 中,通过 render 函数返回需要渲染的 component 一样的模式。
###3、Flutter Widget
在 Flutter 中,一切的显示都是 Widget 。Widget 是一切的基础,作为响应式的渲染,属于 MVVM 的实现机制。我们可以通过修改数据,再用setState 设置数据,Flutter 会自动通过绑定的数据更新 Widget 。 所以你需要做的就是实现 Widget 界面,并且和数据绑定起来。
Widget 分为 有状态 和 无状态 两种,在 Flutter 中每个页面都是一帧。无状态就是保持在那一帧。而有状态的 Widget 当数据更新时,其实是绘制了新的 Widget,只是 State 实现了跨帧的数据同步保存。
这里有个小 Tip ,当代码框里输入 stl 的时候,可以自动弹出创建无状态控件的模板选项,而输入 stf 的时,就会弹出创建有状态 Widget 的模板选项。
代码格式化的时候,括号内外的逗号都会影响格式化时换行的位置。
如果觉得默认换行的线太短,可以在设置-Editor-Code Style-Dart-Wrapping and Braces-Hard wrap at 设置你接受的数值。
3.1、无状态StatelessWidget
直接进入主题,下方代码是无状态 Widget 的简单实现。
继承 StatelessWidget,通过 build 方法返回一个布局好的控件。可能现在你还对 Flutter 的内置控件不熟悉,but Don’t worry , take is easy ,后面我们就会详细介绍。这里你只需要知道,一个无状态的 Widget 就是这么简单。
Widget 和 Widget 之间通过 child: 进行嵌套。其中有的 Widget 只能有一个 child,比如下方的 Container ;有的 Widget 可以多个 child ,也就是children:,比如` Colum 布局。下方代码便是 Container Widget 嵌套了 Text Widget。
import ‘package:flutter/material.dart’; class DEMOWidget extends StatelessWidget { final String text; //数据可以通过构造方法传递进来 DEMOWidget(this.text); @override Widget build(BuildContext context) { //这里返回你需要的控件 //这里末尾有没有的逗号,对于格式化代码而已是不一样的。 return Container( //白色背景 color: Colors.white, //Dart语法中,?? 表示如果text为空,就返回尾号后的内容。 child: Text(text ?? “这就是无状态DMEO”), ); } }
3.2、有状态StatefulWidget
继续直插主题,如下代码,是有状态的widget的简单实现。
你需要创建管理的是主要是 State , 通过 State 的 build 方法去构建控件。在 State 中,你可以动态改变数据,这类似 MVVM 实现,在 setState 之后,改变的数据会触发 Widget 重新构建刷新。而下方代码中,是通过延两秒之后,让文本显示为 “这就变了数值”。
如下代码还可以看出,State 中主要的声明周期有 :
initState :初始化,理论上只有初始化一次,第二篇中会说特殊情况下。
didChangeDependencies:在 initState 之后调用,此时可以获取其他 State 。
dispose :销毁,只会调用一次。
看到没,Flutter 其实就是这么简单!你的关注点只要在:创建你的 StatelessWidget 或者 StatefulWidget 而已。你需要的就是在 build 中堆积你的布局,然后把数据添加到 Widget 中,最后通过 setState 改变数据,从而实现画面变化。
import ‘dart:async’; import ‘package:flutter/material.dart’; class DemoStateWidget extends StatefulWidget { final String text; 通过构造方法传值 DemoStateWidget(this.text); ///主要是负责创建state @override _DemoStateWidgetState createState() => _DemoStateWidgetState(text); } class _DemoStateWidgetState extends State { String text; _DemoStateWidgetState(this.text); @override void initState() { ///初始化,这个函数在生命周期中只调用一次 super.initState(); ///定时2秒 new Future.delayed(const Duration(seconds: 1), () { setState(() { text = “这就变了数值”; }); }); } @override void dispose() { ///销毁 super.dispose(); } @override void didChangeDependencies() { ///在initState之后调 Called when a dependency of this [State] object changes. super.didChangeDependencies(); } @override Widget build(BuildContext context) { return Container( child: Text(text ?? “这就是有状态DMEO”), ); } }
4、Flutter 布局
Flutter 中拥有需要将近30种内置的 布局Widget,其中常用有 Container、Padding、Center、Flex、Stack、Row、Colum、ListView 等,下面简单讲解它们的特性和使用。
- Container :最常用的默认布局!只能包含一个
child:
,支持配置 padding,margin,color,宽高,decoration(一般配置边框和阴影)等配置,在 Flutter 中,不是所有的控件都有 宽高、padding、margin、color 等属性,所以才会有 Padding、Center 等 Widget 的存在。
new Container( ///四周10大小的maring margin: EdgeInsets.all(10.0), height: 120.0, width: 500.0, ///透明黑色遮罩 decoration: new BoxDecoration( ///弧度为4.0 borderRadius: BorderRadius.all(Radius.circular(4.0)), ///设置了decoration的color,就不能设置Container的color。 color: Colors.black, ///边框 border: new Border.all(color: Color(GSYColors.subTextColor), width: 0.3)), child:new Text(“666666”));
Colum、Row 绝对是必备布局, 横竖布局也是日常中最常见的场景。如下方所示,它们常用的有这些属性配置:主轴方向是 start 或 center 等;副轴方向方向是 start 或 center 等;mainAxisSize 是充满最大尺寸,或者只根据子 Widget 显示最小尺寸。
//主轴方向,Colum的竖向、Row我的横向 mainAxisAlignment: MainAxisAlignment.start, //默认是最大充满、还是根据child显示最小大小 mainAxisSize: MainAxisSize.max, //副轴方向,Colum的横向、Row我的竖向 crossAxisAlignment :CrossAxisAlignment.center, Expanded 在 Colum 和 Row 中代表着平均充满,当有两个存在的时候默认均分充满。同时页可以设置 flex 属性决定比例。 new Column( ///主轴居中,即是竖直向居中 mainAxisAlignment: MainAxisAlignment.center, ///大小按照最小显示 mainAxisSize : MainAxisSize.min, ///横向也居中 crossAxisAlignment : CrossAxisAlignment.center, children: [ ///flex默认为1 new Expanded(child: new Text(“1111”), flex: 2,), new Expanded(child: new Text(“2222”)), ], );
接下来我们来写一个复杂一些的控件。首先我们创建一个私有方法_getBottomItem,返回一个 Expanded Widget,因为后面我们需要将这个方法返回的 Widget 在 Row 下平均充满。
如代码中注释,布局内主要是现实一个居中的Icon图标和文本,中间间隔5.0的 padding:
///返回一个居中带图标和文本的Item _getBottomItem(IconData icon, String text) { ///充满 Row 横向的布局 return new Expanded( flex: 1, ///居中显示 child: new Center( ///横向布局 child: new Row( ///主轴居中,即是横向居中