美文网首页Flutter
Flutter自定义加载圈

Flutter自定义加载圈

作者: buhuiming | 来源:发表于2019-01-23 17:33 被阅读0次

    刚起步学习Flutter,还是比较容易上手的。下面介绍一下自定义的加载圈,加入了一些小特性。

    项目地址(github


    效果图

    image.png

    1、结合代码

        首先继承自StatefulWidget,声明一些属性:
        (1)isShowLoadingAtNow:是否马上显示加载圈;
        (2)backPressType:物理返回键的类,定义了三种:SBLOCK(阻止返回),CLOSE_CURRENT(关闭当前页,默认),CLOSE_PARENT(关闭当前页及当前页的父页)
        (3)backPressCallback:点击物理返回键时,会有一个回调,主要拓展于取消某些操作(如网络请求)
        (4)operation:操作对象,可以操作加载圈的状态:隐藏或显示
        (5)child:布局对象
    

    2、加载圈显示时,需要隐藏软键盘

        FocusScope.of(context).requestFocus(FocusNode());
    

    3、使用offstage控制布局的状态,false(显示),true(隐藏)

    4、定义Operation类,声明ValueNotifier<bool> _notifier,让offstage属性指向这个值。当这个值改变的时候,运用VoidCallback刷新页面

      VoidCallback listener;
      Future<bool> _onWillPop() => new Future.value(false); //禁掉返回按钮和右滑关闭
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        widget.operation._notifier = ValueNotifier<bool>(false);
        if (widget.isShowLoadingAtNow != true) {
          widget.operation._notifier.value = false;
        } else {
          widget.operation._notifier.value = true;
        }
        listener = () {
          setState(() { //刷新页面
            _hideKeyBord();
          });
        };
        widget.operation._notifier.addListener(listener);
      }
    

    5、利用WillPopScope控件,来物理返回键和侧滑关闭功能状态

    (1)加载圈显示时候,引用WillPopScope控件,否则使用Container控件
    (2)当引用WillPopScope控件时,有对应3种状态:
          if (widget.backPressType == BackPressType.SBLOCK) {
                _onWillPop();//阻止返回
          }else if (widget.backPressType == BackPressType.CLOSE_CURRENT) {
                widget.operation.setShowLoading(false);//关闭当前页
          }else if (widget.backPressType == BackPressType.CLOSE_PARENT) {
                Navigator.pop(context);//关闭当前页及当前页的父页
          }
    

    6、物理返回键监听

    (1)定义方法:typedef BackPressCallback = Future<void> Function(BackPressType);
    (2)触发回调
           onWillPop: () {
              if(null != widget.backPressCallback){
                widget.backPressCallback(widget.backPressType);
              }
           }
    

    全部代码

    import 'package:flutter/material.dart';
    import 'package:flutter_nb/resource/colors.dart';
    
    /*
    *  loading page
    */
    class LoadingScaffold extends StatefulWidget {
      final bool isShowLoadingAtNow;
      final BackPressType backPressType;
      final BackPressCallback backPressCallback;
      final Operation operation;
      final Widget child;
    
      const LoadingScaffold(
          {Key key,
          this.isShowLoadingAtNow: false,
          this.backPressType: BackPressType.CLOSE_CURRENT,
          this.backPressCallback,
          @required this.operation,
          @required this.child})
          : super(key: key);
    
      @override
      State<StatefulWidget> createState() {
        // TODO: implement createState
        return new LoadingState();
      }
    }
    
    class LoadingState extends State<LoadingScaffold> {
      VoidCallback listener;
      Future<bool> _onWillPop() => new Future.value(false); //禁掉返回按钮和右滑关闭
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        widget.operation._notifier = ValueNotifier<bool>(false);
        if (widget.isShowLoadingAtNow != true) {
          widget.operation._notifier.value = false;
        } else {
          widget.operation._notifier.value = true;
        }
        listener = () {
          setState(() {
            _hideKeyBord();
          });
        };
        widget.operation._notifier.addListener(listener);
      }
    
      void _hideKeyBord(){
        FocusScope.of(context).requestFocus(FocusNode());
      }
    
      @override
      void dispose() {
        // TODO: implement dispose
        super.dispose();
        if (null != listener) {
          widget.operation._notifier.removeListener(listener);
        }
      }
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return new Stack(
          children: <Widget>[
            new Offstage(
              offstage: false,
              child: widget.child,
            ),
            new Offstage(
              offstage: widget.operation._notifier.value != true,
              child: widget.operation._notifier.value != true
                  ? _loadingWidget()
                  : _WillPopScopeWidget(), //显示loading,则禁掉返回按钮和右滑关闭
            )
          ],
        );
      }
    
      Widget _WillPopScopeWidget() {
        return new WillPopScope(
            onWillPop: () {
              if(null != widget.backPressCallback){
                widget.backPressCallback(widget.backPressType);
              }
              if (widget.backPressType == BackPressType.SBLOCK) {
                _onWillPop();//阻止返回
              }else if (widget.backPressType == BackPressType.CLOSE_CURRENT) {
                widget.operation.setShowLoading(false);//关闭当前页
              }else if (widget.backPressType == BackPressType.CLOSE_PARENT) {
                Navigator.pop(context);//关闭当前页及当前页的父页
              }
            },
            child: _loadingWidget());
      }
    
      Widget _loadingWidget() {
        return new Container(
            alignment: Alignment.center,
            color: ColorT.transparent_50,
            width: double.infinity,
            height: double.infinity,
            child: ClipRRect(
                borderRadius: BorderRadius.circular(10.0),
                child: new Container(
                  alignment: Alignment.center,
                  color: ColorT.transparent_80,
                  width: 220.0,
                  height: 90.0,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      SizedBox(
                        width: 28.0,
                        height: 28.0,
                        child: CircularProgressIndicator(
                          valueColor: AlwaysStoppedAnimation(Colors.white),
                          strokeWidth: 2.5,
                        ),
                      ),
                      SizedBox(width: 15.0),
                      Text(
                        '玩命加载中...',
                        style: new TextStyle(
                          fontSize: 17.0,
                          color: Colors.white,
                          letterSpacing: 0.8,
                          fontWeight: FontWeight.normal,
                          decoration: TextDecoration.none,
                        ),
                      )
                    ],
                  ),
                )));
      }
    }
    
    enum BackPressType {
      SBLOCK, //阻止返回
      CLOSE_CURRENT, //关闭当前页
      CLOSE_PARENT //关闭当前页及当前页的父页
    }
    
    typedef BackPressCallback = Future<void> Function(BackPressType); //按返回键时触发
    
    class Operation {
      ValueNotifier<bool> _notifier;
    
      void setShowLoading(bool isShow) {
        _notifier.value = isShow;
      }
    
      bool get isShow => _notifier.value;
    }
    
    

    7、调用的地方

      Operation operation = new Operation();
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return new LoadingScaffold(
          //使用有Loading的widget
          operation: operation,
          isShowLoadingAtNow: false,
          backPressType: BackPressType.CLOSE_CURRENT,
          backPressCallback: (backPressType){
            print('back press and type is ' + backPressType.toString());//点击了返回键
          },
          child: new Scaffold(
            backgroundColor: Colors.white,
            primary: true,
            body: SafeArea(
              child: ListView(
                physics: AlwaysScrollableScrollPhysics(), //内容不足一屏
                padding: EdgeInsets.symmetric(horizontal: 40.0),
                children: <Widget>[
                     SizedBox(height: 50.0),
                      RaisedButton(
                          textColor: Colors.white,
                          color: Colors.blue[300],
                          padding: EdgeInsets.all(12.0),
                          shape: new StadiumBorder(
                            side: new BorderSide(
                            style: BorderStyle.solid,
                            color: Colors.blue,
                      )),
                    child: Text('显示加载圈', style: new TextStyle(fontSize: 16.0)),
                    onPressed: () {
                         operation.setShowLoading(true);
                    },
                  ),
                   SizedBox(height: 10.0),
                   RaisedButton(
                          textColor: Colors.white,
                          color: Colors.blue[300],
                          padding: EdgeInsets.all(12.0),
                          shape: new StadiumBorder(
                            side: new BorderSide(
                            style: BorderStyle.solid,
                            color: Colors.blue,
                      )),
                    child: Text('隐藏加载圈', style: new TextStyle(fontSize: 16.0)),
                    onPressed: () {
                         operation.setShowLoading(false);
                    },
                  ),
               ]
             )
           )
         )
       )
      };
    

    相关文章

      网友评论

        本文标题:Flutter自定义加载圈

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