Flutter及Dart入门

作者: 直男程序员 | 来源:发表于2020-02-27 14:45 被阅读0次

    目录

    • 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 类型分为 intdouble ,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 不需要给变量设置 settergetter 方法,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 ,没有关键词 publicprivate 等修饰符,==_==下横向直接代表 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"));
        
    
    • ColumnRow 绝对是必备布局, 横竖布局也是日常中最常见的场景。如下方所示,它们常用的有这些属性配置:主轴方向是 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 中 是通过实现 StatesetState 来渲染和改变 StatefulWidget 的,如果使用了flutter_redux 会有怎样的效果?

    比如把用户信息存储在 reduxstore 中, 好处在于: 比如某个页面修改了当前用户信息,所有绑定的该 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("点击变换数据")
                        )
                      ],
                    )
                ),
              );
            }
        );
      }
    

    最终效果:


    image

    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 需要两个泛型

    1. 一个是我们创建的 State(ReduxState)
    2. 一个是 ViewModel

    StoreConnector 要定义两个函数

    1. 一个是 converter,转化函数,从 Store 中拿出修改的数据 store.state.name
    2. 一个是 builder,将 converter 返回的 name 进一步转化为界面:Text(name)。

    ViewModel决定了converter(转换函数)那边的返回值类型,这边我们将它定义为String,因为转换函数里返回的是ReduxState里的name字段。

    这里就是对flutter 入门的一些简单知识分享,一些概念性的东西这里不一一分享,大家看官网就可以看到,这里主要针对一些重要概念和一些坑进行分析,希望对大家有所帮助。

    相关文章

      网友评论

        本文标题:Flutter及Dart入门

        本文链接:https://www.haomeiwen.com/subject/sowthhtx.html