美文网首页Flutter
Flutter——Widget系列4、widget状态分类,有状

Flutter——Widget系列4、widget状态分类,有状

作者: 阿敏其人 | 来源:发表于2019-08-27 16:31 被阅读0次
    image.png

    前言

    简单粗暴来说,一个widget,如果在运行的过程中需要变化,就是有状态的。

    Fluter的widget,安卓状态来说,可以分为:有状态(StatefulWidget)和无状态(StatelessWidget)

    无状态(StatelessWidget)

    状态组件指的就是其内部的状态是来自其父组件并使用final类型的变量来存储,当组件被build的时候它们就使用这些不可变的数据来构建自己的UI。

    无状态的组件,前面我们已经使用无数次了,重点是看StatefulWidget,有状态的组件。

    有状态(StatefulWidget)

    有状态的wdiget,其持有状态可能在Widget生命周期中发生变化。

    • Checkbox、Radio、Slider、InkWell、Form和TextField是有状态小部件的示例,它们是StatefulWidget的子类。

    • 对于有可变状态控件的管理,官方文档是写了有3种模式:控件自己管理状态交给父控件管理状态以及混合管理 (自己和父部件各管理一部分)

    实现一个StatefulWidget至少需要两个类:
    一个StatefulWidget
    一个State类。

    • StatefulWidget类本身是不变的,但是State类在Widget生命周期中始终存在,当在State内部改变任何子控件需要的变量时,都需要使用setState

    一句话来说,就是一个有状态的组件,需要两个类,分别是StatefulWidget和State,而状态组件的内容,需要用到setState方法。


    一、 有状态(StatefulWidget)

    实现StatefulWidget需要的两个类

    实现一个StatefulWidget至少需要两个类:
    一个StatefulWidget
    一个State类。

    如何改变StatefulWidget的内容

    • StatefulWidget类本身是不变的,但是State类在Widget生命周期中始终存在,当在State内部改变任何子控件需要的变量时,都需要使用setState

    例子

    例子1 控件自己管理状态

    一个计数器,点击自身,统计点击次数。

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
    //    final wordPair = new WordPair.random();
    
        return MaterialApp(
            title: 'Flutter 测试标题',
            theme: new ThemeData(
              primaryColor: Colors.red,
            ),
            home: new Scaffold(
              appBar: AppBar(
                title: Text("测试呀"),
              ),
              body: new Counter(),
            ));
      }
    }
    
    class Counter extends StatefulWidget {
      // 经典写法,继承自StatefulWidget的Widget返回一个自己的State私有类
      _CounterState createState() => new _CounterState();
    }
    
    class _CounterState extends State<Counter> {
      // State类里面定义私有变量,我们的状态变化,会通过setState方法具体操作
      int _count = 0;
    
      void _increment() {
        setState(() {
          ++_count;
        });
      }
    
      Widget build(BuildContext context) {
        return new Container(
            decoration: new BoxDecoration(color: Colors.grey[100]),
            child: new Center(
                child: new RaisedButton(
                    // 按下时的事件
                    onPressed: _increment,
                    // 内容不写死,数据动态显示,一切都是因为_increment里面的setState方法
                    child: new Text('click count : ${_count}'))));
      }
    }
    

    .
    .

    重要都在备注里面了,如果非要说说逻辑,大概就是:

    • 1、继承自StatefulWidget的Widget返回一个自己的State私有类
    • 2、State类里面定义私有变量,我们的状态变化,会通过setState方法具体操作
    • 3、通过一定的事件或者条件,触发setState改变数据
    • 4、数据展示的逻辑,不写死,根据变量的调整动态调整
    2019-08-27 13.57.03.gif

    .
    .

    例子2 父控件管理子控件状态

    
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
    //    final wordPair = new WordPair.random();
    
        return MaterialApp(
            title: 'Flutter 测试标题',
            theme: new ThemeData(
              primaryColor: Colors.red,
            ),
            home: new Scaffold(
              appBar: AppBar(
                title: Text("测试呀"),
              ),
              body: new ParentWidget(),
            ));
      }
    }
    
    
    //------------------------- parent widget ----------------------------------
    
    class ParentWidget extends StatefulWidget {
    
      ParentWidget() {
        print('ParentWidget init');
      }
    
      @override
      State<StatefulWidget> createState() {
        return _ParentWidgetState();
      }
    }
    
    class _ParentWidgetState extends State<ParentWidget> {
      bool _active = false;
    
      _ParentWidgetState() {
        print('ParentWidgetState init');
      }
    
      void _handleSonChanged(bool newValue) {
        print('parent _handleSonChanged is call : $newValue');
        setState(() {
          _active = newValue;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        print('ParentWidgetState build is call');
        return new Center(
          child: SonWidget(onChanged: _handleSonChanged, isShowAndroid: _active,),
        );
      }
    }
    
    //------------------------- child widget ----------------------------------
    
    class SonWidget extends StatelessWidget {
    
      SonWidget({
        Key key, this.isShowAndroid: false, 
        @required this.onChanged
      }): super(key: key) {
        print('Son SonWidget init : ${this.isShowAndroid}');
      }
    
      final bool isShowAndroid;
      final ValueChanged<bool> onChanged;
    
      void _handleSon() {
        print('child _handleSon : $isShowAndroid');
        onChanged(!isShowAndroid);
      }
    
      @override
      Widget build(BuildContext context) {
        print('Son SonWidget build method');
        return GestureDetector(
          onTap: _handleSon,
          child: Container(
            width: 200.0,
            height: 200.0,
            decoration: BoxDecoration(
                color: isShowAndroid ? Colors.lightGreen[700] : Colors.grey[600]
            ),
            child: Center(
              child: Text(isShowAndroid ? 'Android' : 'Flutter',
                style: TextStyle(fontSize: 32.0, color: Colors.white),),
            ),
          ),
        );
      }
    }
    
    

    核心:子控件的构造方法,暴露出事件参数,父控件创建子控件的时候,传入参数,进而实现父控件改变自控内容的目的。

    所以核心代码:

    image.png
    初始化顺序

    初始化输出日志:

    
    flutter: ParentWidget init
    flutter: ParentWidgetState build is call
    flutter: Son SonWidget init : false
    flutter: Son SonWidget build method
    Reloaded 1 of 432 libraries in 2,751ms.
    
    image.png
    点击回调执行顺序

    点击输出日志:

    第一次点击
    flutter: child _handleSon : false
    flutter: parent _handleSonChanged is call : true
    flutter: ParentWidgetState build is call
    flutter: Son SonWidget init : true
    flutter: Son SonWidget build method
    
    第二次点击
    flutter: child _handleSon : true
    flutter: parent _handleSonChanged is call : false
    flutter: ParentWidgetState build is call
    flutter: Son SonWidget init : false
    flutter: Son SonWidget build method
    
    第三次点击
    flutter: Son SonWidget init : false
    flutter: Son SonWidget build method
    flutter: child _handleSon : false
    flutter: parent _handleSonChanged is call : true
    flutter: ParentWidgetState build is call
    flutter: Son SonWidget init : true
    flutter: Son SonWidget build method
    
    

    一切是那么重新,在父控件控制子控件的时,刷新ui会让子控件重新初始化和和build(绘制)。

    image.png

    .
    .

    参考:为你的 Flutter APP 添加交互性

    2019-08-27 15.56.01.gif

    .
    .

    例子3 混合管理

    也就是,父控件管,子控件自己也管,一起管

    混合管理就是某些状态由自己管理,某些状态由父部件来管理。
    下面的例子就是一个混合管理状态的例子,部件 TabboxC 在被点击时有三个状态变换,背景色,文字和边框。
    示例中,背景色和文字的状态交由父部件来管理(和上一个示例类似),而边框状态由自己管理。
    既然父部件和子部件都能管理状态,那么它们都是要继承StatefulWidget类。

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
    //    final wordPair = new WordPair.random();
    
        return MaterialApp(
            title: 'Flutter 测试标题',
            theme: new ThemeData(
              primaryColor: Colors.red,
            ),
            home: new Scaffold(
              appBar: AppBar(
                title: Text("测试呀"),
              ),
              body: new ParentWidget2(),
            ));
      }
    }
    
    
    // ------------parent widget-----------
    class ParentWidget2 extends StatefulWidget {
      ParentWidget2() {
        print('Parent init');
      }
    
      @override
      State<StatefulWidget> createState() {
        return _ParentWidgetState2();
      }
    }
    
    class _ParentWidgetState2 extends State<ParentWidget2> {
    
      _ParentWidgetState2() {
        print('_Parent  State init');
      }
    
      bool _active = false;
    
      void _handleTapboxChanged(bool newValue) {
        print('_Parent _handleTapboxChanged method is called');
        setState(() {
          _active = newValue;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        print('_Parent State build is called');
        return TabboxC(onChanged: _handleTapboxChanged, active: _active,);
      }
    
    }
    
    // ------------child widget-----------
    class TabboxC extends StatefulWidget {
    //  构造方法
      TabboxC({
        Key key,
        this.active: false,
        @required this.onChanged
      }) : super(key: key) {
        print('TabboxC init');
      }
    
      final bool active;
      final ValueChanged<bool> onChanged;
    
      @override
      State<StatefulWidget> createState() {
        return _TapboxCState();
      }
    }
    
    class _TapboxCState extends State<TabboxC> {
    
      bool _highlight = false;
    
      _TapboxCState() {
        print('_TapboxC  State init');
      }
    
      void _handleTapDown(TapDownDetails details) {
        print('_TapboxC tap down');
        setState(() {
          _highlight = true;
        });
      }
    
      void _handleTapUp(TapUpDetails details) {
        print('_TapboxC tap up');
        setState(() {
          _highlight = false;
        });
      }
    
      void _handleTapCancel() {
        print('_TapboxC tap cancel');
        setState(() {
          _highlight = false;
        });
      }
    
      void _handleTap() {
        print('_TapboxC tap clicked');
        widget.onChanged(!widget.active);
      }
    
      @override
      Widget build(BuildContext context) {
        print('_TapboxCState build is called');
        return  Center(
          child: GestureDetector(
    //              down
            onTapDown: _handleTapDown,
    //              up
            onTapUp: _handleTapUp,
    //              cancel
            onTapCancel: _handleTapCancel,
    //              click
            onTap: _handleTap,
            child: Container(
              width: 200.0,
              height: 200.0,
              decoration: BoxDecoration(
    //                  Box 颜色  父控件 控制(通过回调方法)
                  color: widget.active ? Colors.lightGreen[700] : Colors
                      .grey[600],
    //                  边框颜色  自己控制
                  border: _highlight ? Border.all(
                      color: Colors.teal[700], width: 10.0) : null
              ),
              child: Center(
                child: Text(widget.active ? 'Active' : 'Inactive',
                  style: TextStyle(fontSize: 32.0, color: Colors.white),),
              ),
            ),
          ),
        );
      }
    }
    
    
    2019-08-27 16.10.52.gif

    按下释放后的一瞬间,才有边框。

    初始化

    初始化时候的顺序和上面类似,我们来看看点击事件被触发时候的执行顺序:

    flutter: _TapboxC tap down
    flutter: _TapboxCState build is call
    flutter: _TapboxC tap up
    flutter: _TapboxC tap clicked
    flutter: _Parent _handleTapboxChanged method is call
    flutter: _Parent State build is call
    flutter: TabboxC init
    flutter: _TapboxCState build is call
    
    

    执行流程:


    image.png

    大家可能会发现,子部件在 Down 事件中调用了 setState(...) 方法,然后执行了一次 build 操作;而在 Up 事件中同样也调用了 setState(...) 方法,但是为什么没有执行 build 操作,而是直接执行了 click 操作。这里面可能和 Android 里面类似,在 View 的 onTouchEvent 方法里面,onClick 方法也是在 ACTION_UP 里面执行的。

    参考:为你的 Flutter APP 添加交互性


    END

    相关文章

      网友评论

        本文标题:Flutter——Widget系列4、widget状态分类,有状

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