目录
- Dart语言下的Flutter
- Flutter Widget
- Flutter 布局
- Flutter 页面
- 路由跳转
- 网络请求
- Json序列号
- Redux
一、Dart语言下的Flutter
这里详细的不做详解,只简单讲述下 Dart 的一些特性,主要涉及的是 Flutter下使用。
1.1、基本类型
- var 可以定义变量,如
var tag = "666
,这和 Swift、JS 、 Kotlin 等语言类似,同时支持闭包。
Dart
属于是强类型语言 ,但可以用var
来声明变量,Dart
会自推导出数据类型,所以 var 实际上是编译期的“语法糖”。dynamic
表示动态类型, 被编译后,实际是一个 object 类型,在编译期间不进行任何的类型检查,而是在运行期进行类型检查。Dart 中 number 类型分为
int
和double
,Dart 中没有float
类型。
- Dart 下只有
bool
型可以用于 if 等判断 。
- Dart 中,switch 支持 String 类型
类型对照表
Dart | Android | ios |
---|---|---|
null | null | nil |
bool | java.lang.Boolean | Bool |
int | java.lang.Integer | Int、Int64等 |
List | java.util.ArrayList | Array |
Map | java.util.HashMap | Dictionary |
1.2、变量
- Dart 不需要给变量设置
setter
、getter
方法,Dart 中所有的基础类型、类等都继承 Object ,默认值是 NULL, 自带 getter 和 setter ,而如果是final
或者const
的话,那么它只有一个 getter 方法。
- Dart 中 final 和 const 表示常量,比如
final name = 'GSY'; const value= 1000000;
同时 static const 组合代表了静态常量,其中 const 的值在编译期确定,final 的值要到运行时才确定。
- Dart 下的数值,在作为字符串使用时,是需要
显式指定
的。比如:int i = 0; print("aaaa" + i); 这样并不支持,需要 print("aaaa" + i.toString()); 这样使用,这和 Java 与 JS 存在差异,所以在使用动态类型时,需要注意不要把 number 类型当做 String 使用。
- Dart 中数组等于列表,所以
var list = []; 和 List list = new List()
可以简单看做一样。
1.3、方法
- Dart 下
??
属于操作符,如:AA ?? "999"
表示如果 AA 为空,返回999;AA ??= "999"
表示如果 AA 为空,给 AA 设置成 999。
- Dart 方法可以设置 参数默认值 和 指定名称 。比如:
getDetail(Sting userName, reposName, {branch = "master"}){}
方法,这里 branch 不设置的话,默认是 “master” 。参数类型 可以指定或者不指定。调用效果:getRepositoryDetailDao(“aaa", "bbbb", branch: "dev");
- Dart 不像 Java ,没有关键词
public
、private
等修饰符,==_==下横向直接代表 private 。
- Dart 中
多构造函数
,可以通过如下代码实现的。默认构造方法只能有一个,而通过Model.empty() 方法可以创建一个空参数的类,而变量初始化值时,只需要通过 this.name 在构造方法中指定即可:
class ModelA {
String name;
String tag;
//默认构造方法,赋值给name和tag
ModelA(this.name, this.tag);
//返回一个空的ModelA
ModelA.empty();
//返回一个设置了name的ModelA
ModelA.forName(this.name);
}
1.4、Flutter
Flutter 中支持
async/await
,如下代码所示, async/await 其实只是语法糖,最终会编译为 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 一样的模式。
二、Flutter Widget
在
Flutter 中一切皆 Widget
,Widget 是一切的基础,利用响应式模式进行渲染。我们可以通过修改数据,再用
setState
设置数据,Flutter 会自动通过绑定的数据更新 Widget , 所以你需要做的就是实现 Widget 界面,并且和数据绑定起来。Widget 分为
有状态
和无状态
两种,在 Flutter 中每个页面都是一帧,无状态就是保持在那一帧,而有状态的 Widget 当数据更新时,其实是创建了新的 Widget,只是 State 实现了跨帧的数据同步保存。
这里有个小 Tip ,当代码框里输入
stl
的时候,可以自动弹出创建无状态控件的模板选项,而输入 stf 的时,就会弹出创建有状态 Widget 的模板选项。
3.1 无状态StatelessWidget
直接进入主题,如下下代码所示是无状态 Widget 的简单实现。继承 StatelessWidget,通过
build
方法返回一个布局好的控件。
Widget 和 Widget 之间通过
child:
进行嵌套。其中有的 Widget 只能有一个 child,比如下方的Container
;有的 Widget 可以多个 child ,也就是children
,比如Column` 布局,下方代码便是 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 中,你可以动态改变数据,在 setState
之后,改变的数据会触发 Widget 重新构建刷新,而下方代码中,是通过延两秒之后,让文本显示为 "这就变了数值"。
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<DemoStateWidget> {
String text;
_DemoStateWidgetState(this.text);
@override
void initState() {
///初始化,这个函数在生命周期中只调用一次
super.initState();
///定时1秒
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"),
);
}
}
三、Flutter布局
Flutter 中拥有需要将近30种内置的
布局Widget
,其中常用有 Container、Padding、Center、Flex、Stack、Row、Column、ListView 等,下面简单讲解它们的特性和使用,大家了解知道有这些即可,使用时候具体属性可以再查。
-
Container
:最常用的默认控件,但是实际上它是由多个内置控件组成的模版,只能包含一个child,支持 padding,margin,color,宽高,decoration(一般配置边框和阴影)等配置,在 Flutter 中,不是所有的控件都有 宽高、padding、margin、color 等属性,所以才会有 Padding、Center 等 Widget 的存在。
///四周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"));
-
Column
、Row
绝对是必备布局, 横竖布局也是日常中最常见的场景。如下方所示,它们常用的有这些属性配置:主轴方向是 start 或 center 等;副轴方向方向是 start 或 center 等;mainAxisSize 是充满最大尺寸,或者只根据子 Widget 显示最小尺寸。
//主轴方向,Column的竖向、Row我的横向
mainAxisAlignment: MainAxisAlignment.start,
//默认是最大充满、还是根据child显示最小大小
mainAxisSize: MainAxisSize.max,
//副轴方向,Column的横向、Row我的竖向
crossAxisAlignment :CrossAxisAlignment.center,
-
Expanded
在 Column 和 Row 中代表着平均充满的作用,当有两个存在的时候默认均分充满。同时页可以设置 flex 属性决定比例。
new Column(
///主轴居中,即是竖直向居中
mainAxisAlignment: MainAxisAlignment.center,
///大小按照最小显示
mainAxisSize : MainAxisSize.min,
///横向也居中
crossAxisAlignment : CrossAxisAlignment.center,
children: <Widget>[
///flex默认为1
new Expanded(child: new Text("1111"), flex: 2,),
new Expanded(child: new Text("2222")),
],
);
Flutter 中,布局很多时候一层一层嵌套出来的,当然还有其他更高级的布局方式,这里就先不展开了。
四、Flutter 页面
Flutter 中除了布局的 Widget,还有交互显示的 Widget 和完整页面呈现的Widget,其中常见的有 MaterialApp、Scaffold、Appbar、Text、Image、FlatButton等,下面简单介绍这些 Wdiget。
类型 | 使用 |
---|---|
MaterialApp | 一般作为APP顶层的主页入口,可配置主题,多语言,路由等 |
Scaffold | 一般用户页面的承载Widget,包含appbar、snackbar、drawer等material design的设定。 |
Appbar | 一般用于Scaffold的appbar ,内有标题,二级页面返回按键等,当然不止这些,tabbar等也会需要它 。 |
Text | 显示文本,几乎都会用到,主要是通过style设置TextStyle来设置字体样式等。 |
RichText | 富文本,通过设置TextSpan,可以拼接出富文本场景。 |
TextField | 文本输入框 :new TextField(controller: //文本控制器, obscureText: "hint文本"); |
Image | 图片加载: new FadeInImage.assetNetwork( placeholder: "预览图", fit: BoxFit.fitWidth, image: "url"); |
FlatButton | 按键点击: new FlatButton(onPressed: () {},child: new Container()); |
实现一个简单完整的页面试试。直接上代码:
- 首先我们创建一个StatefulWidget:DemoPage。
- 然后在_DemoPageState 中,通过build创建了一个Scaffold。
- Scaffold内包含了一个AppBar和一个ListView。
- AppBar类似标题了区域,其中设置了 title为 Text Widget。
- body是ListView, 返回自定义Widget。
import 'package:flutter/material.dart';
import 'package:gsy_github_app_flutter/test/DemoItem.dart';
class DemoPage extends StatefulWidget {
@override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
@override
Widget build(BuildContext context) {
///一个页面的开始
///如果是新页面,会自带返回按键
return new Scaffold(
///背景样式
backgroundColor: Colors.blue,
///标题栏,当然不仅仅是标题栏
appBar: new AppBar(
///这个title是一个Widget
title: new Text("Title"),
),
///正式的页面开始
///一个ListView,20个Item
body: new ListView.builder(
itemBuilder: (context, index) {
return new CustomItem(); //类似于ios的cell
},
itemCount: 20,
),
);
}
}
这里主要讲解都是一些入坑
常用的东西,方便大家首先对一些基础知识有个大概了解。
五、路由
Flutter 中的页面跳转一般是通过
Navigator
实现的,路由跳转又分为:带参数跳转和不带参数跳转。不带参数跳转比较简单,默认可以通过 MaterialApp 的路由表跳转,也可以使用PageRouteBuider
;而带参数的跳转自定义跳转动画,参数通过跳转页面的构造方法传递。常用的跳转有如下几种使用:
///不带参数的路由表跳转
Navigator.pushNamed(context, routeName);
///跳转新页面并且替换,比如登录页跳转主页
Navigator.pushReplacementNamed(context, routeName);
///跳转到新的路由,并且关闭给定路由的之前的所有页面
Navigator.pushNamedAndRemoveUntil(context, '/calendar', ModalRoute.withName('/'));
///带参数的路由跳转,并且监听返回
Navigator.push(context, new MaterialPageRoute(builder: (context) => new NotifyPage())).then((res) {
///获取返回处理
});
///带参数的路由跳转,自定义跳转动画
Navigator.push(context, new PageRouteBuider(pageBuilde: (context, anination, secondAnimation) => new NotifyPage())).then((res) {
///获取返回处理
});
同时我们可以看到,Navigator 的 push 返回的是一个 Future,这个Future 的作用是在页面返回时被调用的。也就是你可以通过 Navigator 的 pop 时返回参数,之后在 Future 中可以的监听中处理页面的返回结果。
六、网络请求
当前 Flutter 网络请求封装中,国内最受欢迎的就是 Dio 或者 http 了,这两个都是对dart请求进行了二次封装,使用起来非常方便。
这里看下一个Http网络请求的例子
/// 请求列表数据,返回model
static Future<FYContractListModelEntity> getSelectContractList(Map params,{String pre ="t8"})
async {
/// 获取请求url
String url = EnvirmentUtils.getBaseUrlByPre(pre,FYNetUrl.selectContractList);
final response = await http.post(url,body: params);
if(response.statusCode == 200){
/// 请求成功后对结果进行处理
final result = json.decode(Utf8Decoder().convert(response.bodyBytes));
return JsonConvert.fromJsonAsT<FYContractListModelEntity>(result);
}else{
/// 抛出异常
throw Exception('Failed to load ContractList json');
}
}
七、Json序列化
项目json解析是不可避免的操作,Dart的解析如果手动解析的话是非常复杂的,可以看下代码:
class OrderListItem {
List<Data> data;
Property property;
Page page;
Status status;
OrderListItem({this.data, this.property, this.page, this.status});
OrderListItem.fromJson(Map<String, dynamic> json) {
if (json['data'] != null) {
data = new List<Data>();
(json['data'] as List).forEach((v) {
data.add(new Data.fromJson(v));
});
}
property = json['property'] != null
? new Property.fromJson(json['property'])
: null;
page = json['page'] != null ? new Page.fromJson(json['page']) : null;
status =
json['status'] != null ? new Status.fromJson(json['status']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.data != null) {
data['data'] = this.data.map((v) => v.toJson()).toList();
}
if (this.property != null) {
data['property'] = this.property.toJson();
}
if (this.page != null) {
data['page'] = this.page.toJson();
}
if (this.status != null) {
data['status'] = this.status.toJson();
}
return data;
}
}
class Data {
int goodsLoadDate;
String orderSn;
String unDrawMoney;
int brokerPeriod;
String startCityName;
String goodsWeight;
int checkStatus;
String payStatusEnum;
int buttonStatus;
String carLength;
String driverMobile;
double noPayMoney;
String statusDesc;
String hasPay;
int isNew;
int needReceipt;
String buttonStatusDesc;
String endCityName;
String exceptionStatusEnum;
String backStatusEnum;
int driverId;
int backStatus;
String checkStatusEnum;
String driverName;
String waitPay;
int payStatus;
String carModel;
String goodsNum;
String goodsCubage;
String locationAddress;
int driverArrangeDeadline;
int expectDelayTime;
int expectGoodsUnloadDate;
int positionStatus;
Data(
{this.goodsLoadDate,
this.orderSn,
this.unDrawMoney,
this.brokerPeriod,
this.startCityName,
this.goodsWeight,
this.checkStatus,
this.payStatusEnum,
this.buttonStatus,
this.carLength,
this.driverMobile,
this.noPayMoney,-->
this.statusDesc,
this.hasPay,
this.isNew,
this.needReceipt,
this.buttonStatusDesc,
this.endCityName,
this.exceptionStatusEnum,
this.backStatusEnum,
this.driverId,
this.backStatus,
this.checkStatusEnum,
this.driverName,
this.waitPay,
this.payStatus,
this.carModel,
this.goodsNum,
this.goodsCubage,
this.locationAddress,
this.driverArrangeDeadline,
this.expectDelayTime,
this.expectGoodsUnloadDate,
this.positionStatus});
Data.fromJson(Map<String, dynamic> json) {
goodsLoadDate = json['goodsLoadDate'];
orderSn = json['orderSn'];
unDrawMoney = json['unDrawMoney'];
brokerPeriod = json['brokerPeriod'];
startCityName = json['startCityName'];
goodsWeight = json['goodsWeight'];
checkStatus = json['checkStatus'];
payStatusEnum = json['payStatusEnum'];
buttonStatus = json['buttonStatus'];
carLength = json['carLength'];
driverMobile = json['driverMobile'];
noPayMoney = json['noPayMoney'];
statusDesc = json['statusDesc'];
hasPay = json['hasPay'];
isNew = json['isNew'];
needReceipt = json['needReceipt'];
buttonStatusDesc = json['buttonStatusDesc'];
endCityName = json['endCityName'];
exceptionStatusEnum = json['exceptionStatusEnum'];
backStatusEnum = json['backStatusEnum'];
driverId = json['driverId'];
backStatus = json['backStatus'];
checkStatusEnum = json['checkStatusEnum'];
driverName = json['driverName'];
waitPay = json['waitPay'];
payStatus = json['payStatus'];
carModel = json['carModel'];
goodsNum = json['goodsNum'];
goodsCubage = json['goodsCubage'];
locationAddress = json['locationAddress'];
driverArrangeDeadline = json['driverArrangeDeadline'];
expectDelayTime = json['expectDelayTime'];
expectGoodsUnloadDate = json['expectGoodsUnloadDate'];
positionStatus = json['positionStatus'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['goodsLoadDate'] = this.goodsLoadDate;
data['orderSn'] = this.orderSn;
data['unDrawMoney'] = this.unDrawMoney;
data['brokerPeriod'] = this.brokerPeriod;
data['startCityName'] = this.startCityName;
data['goodsWeight'] = this.goodsWeight;
data['checkStatus'] = this.checkStatus;
data['payStatusEnum'] = this.payStatusEnum;
data['buttonStatus'] = this.buttonStatus;
data['carLength'] = this.carLength;
data['driverMobile'] = this.driverMobile;
data['noPayMoney'] = this.noPayMoney;
data['statusDesc'] = this.statusDesc;
data['hasPay'] = this.hasPay;
data['isNew'] = this.isNew;
data['needReceipt'] = this.needReceipt;
data['buttonStatusDesc'] = this.buttonStatusDesc;
data['endCityName'] = this.endCityName;
data['exceptionStatusEnum'] = this.exceptionStatusEnum;
data['backStatusEnum'] = this.backStatusEnum;
data['driverId'] = this.driverId;
data['backStatus'] = this.backStatus;
data['checkStatusEnum'] = this.checkStatusEnum;
data['driverName'] = this.driverName;
data['waitPay'] = this.waitPay;
data['payStatus'] = this.payStatus;
data['carModel'] = this.carModel;
data['goodsNum'] = this.goodsNum;
data['goodsCubage'] = this.goodsCubage;
data['locationAddress'] = this.locationAddress;
data['driverArrangeDeadline'] = this.driverArrangeDeadline;
data['expectDelayTime'] = this.expectDelayTime;
data['expectGoodsUnloadDate'] = this.expectGoodsUnloadDate;
data['positionStatus'] = this.positionStatus;
return data;
}
}
class Property {
int searchType;
int systemTime;
Property({this.searchType, this.systemTime});
Property.fromJson(Map<String, dynamic> json) {
searchType = json['searchType'];
systemTime = json['systemTime'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['searchType'] = this.searchType;
data['systemTime'] = this.systemTime;
return data;
}
}
class Page {
int totalCou;
int curPage;
int totalPage;
int nextPage;
bool useCache;
int pageSize;
bool queryCount;
Page(
{this.totalCou,
this.curPage,
this.totalPage,
this.nextPage,
this.useCache,
this.pageSize,
this.queryCount});
Page.fromJson(Map<String, dynamic> json) {
totalCou = json['totalCou'];
curPage = json['curPage'];
totalPage = json['totalPage'];
nextPage = json['nextPage'];
useCache = json['useCache'];
pageSize = json['pageSize'];
queryCount = json['queryCount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['totalCou'] = this.totalCou;
data['curPage'] = this.curPage;
data['totalPage'] = this.totalPage;
data['nextPage'] = this.nextPage;
data['useCache'] = this.useCache;
data['pageSize'] = this.pageSize;
data['queryCount'] = this.queryCount;
return data;
}
}
class Status {
int code;
String desc;
Status({this.code, this.desc});
Status.fromJson(Map<String, dynamic> json) {
code = json['code'];
desc = json['desc'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['code'] = this.code;
data['desc'] = this.desc;
return data;
}
}
所以 json_annotation
插件就诞生了,只需写入类名和返回的json数据即可自动生成
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons
json_annotation: ^2.0.0
dev_dependencies:
flutter_test:
sdk: flutter
json_serializable: ^2.0.0
可以自己在项目中操作一把看下,非常方便。
八、Redux
在前端领域,Redux
并不是一个陌生的概念,作为全局状态管理机,用于 Flutter 中再合适不过,简单来说就是:它可以跨控件管理、同步State 。
前面已经讲了,在 Flutter
中 是通过实现 State
与 setState
来渲染和改变 StatefulWidget 的,如果使用了flutter_redux 会有怎样的效果?
比如把用户信息存储在 redux
的 store
中, 好处在于: 比如某个页面修改了当前用户信息,所有绑定的该 State 的控件将由 Redux 自动同步修改,State 可以跨页面共享。
redux 中主要引入了 action、reducer、store 概念:
- action 简单点就是动作,通过发起一个action来告诉Reducer该更新状态了
- reducer 用于根据 action 产生新状态
- store 位于整个APP的顶层,用于存储和管理 state
直接上代码:
8.1. 创建State
///创建State,全局Redux store 的对象,保存State数据
//定义一个state
class ReduxState {
String name;
ReduxState.initState() : name = "666";
}
8.2. 创建action
//定义action
enum Action {
Change
}
8.3. 创建reducer
//定义reducer
ReduxState getReduce(ReduxState state, action) {
if(action == Action.Change) {
String nname = "1";
state.name = state.name + nname;
}
return state;
}
8.4. 创建全局Store
在main.dart中创建一个全局的store
main() {
final store = Store<ReduxState>(
getReduce,
initialState: ReduxState.initState()
);
runApp(ReduxDemo3(store,));
}
8.5. 将Store跟根Widget关联
注意:
- 最顶层必须是 StoreProvider 开始
- StoreBuilder后要跟上我们定义的那个State类,要不会报错,
class ReduxDemo3 extends StatelessWidget {
final Store<ReduxState> store;
ReduxDemo3(this.store);
@override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: StoreBuilder<ReduxState>(builder: (BuildContext context, Store<ReduxState> store){
return MaterialApp(
title: 'ReduxDemo3',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: FirstPage(),
);
})
);
}
}
8.6. 一级子界面构建
注意:builder方法里有2个值,context和store,我们自定义的State类可以从store中获取
@override
Widget build(BuildContext context) {
return StoreBuilder<ReduxState>(
builder: (BuildContext context, Store<ReduxState> store){
return Scaffold(
appBar: AppBar(
title: Text("ReduxDemo3"),
),
body: Center(
child: Column(
children: <Widget>[
Text(store.state.name),
SizedBox(height: 100,),
FlatButton(
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context){
return NextPage();
}));
},
child: Text("下一页")
)
],
)
),
);
}
);
}
8.7. 二级子界面构建
State数据的修改是由Store来发起Action,通知Reducer修改数据,方法为store.dispatch(定义的action)
@override
Widget build(BuildContext context) {
return StoreBuilder<ReduxState>(
builder: (BuildContext context, Store<ReduxState> store){
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
Text(store.state.name),
SizedBox(height: 100,),
FlatButton(
onPressed: (){
store.dispatch(Action.Change);
},
child: Text("点击变换数据")
)
],
)
),
);
}
);
}
最终效果:
8.8. StoreBuilder和StoreConnector
例子里的界面构建使用的是StoreBuilder来构建,也可以使用 ==StoreConnector== 来构建,两者就差一个参数。
StoreConnector主要是有个数据转化的作用,可以对数据先做一些转化操作再赋值到组件上
/// 多一个ViewModel
class StoreConnector<S, ViewModel> extends StatelessWidget
class StoreBuilder<S> extends StatelessWidget
可以修改第二个界面里的body的代码,将显示的Text换成用StoreConnector来包装,效果是一样的
body: Center(
child: Column(
children: <Widget>[
StoreConnector<ReduxState, String>(
///转换为要使用的 name 字符串
converter: (store) => store.state.name,
builder: (BuildContext context, String name) {
return Text(name);
},
),
SizedBox(height: 100,),
StoreBuilder<ReduxState>(
builder: (context, store) {
return FlatButton(
onPressed: (){
store.dispatch(Action.Change);
},
child: Text("点击变换数据")
);
}
),
],
)
),
StoreConnector 需要两个泛型
- 一个是我们创建的 State(ReduxState)
- 一个是 ViewModel
StoreConnector 要定义两个函数
- 一个是 converter,转化函数,从 Store 中拿出修改的数据 store.state.name
- 一个是 builder,将 converter 返回的 name 进一步转化为界面:Text(name)。
ViewModel决定了converter(转换函数)那边的返回值类型,这边我们将它定义为String,因为转换函数里返回的是ReduxState里的name字段。
这里就是对
flutter
入门的一些简单知识分享,一些概念性的东西这里不一一分享,大家看官网就可以看到,这里主要针对一些重要概念和一些坑进行分析,希望对大家有所帮助。