Flutter自定义加载圈

刚起步学习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 _notifier,让offstage属性指向这个值。当这个值改变的时候,运用VoidCallback刷新页面

  VoidCallback listener;
  Future _onWillPop() => new Future.value(false); //禁掉返回按钮和右滑关闭

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    widget.operation._notifier = ValueNotifier(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 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 createState() {
    // TODO: implement createState
    return new LoadingState();
  }
}

class LoadingState extends State {
  VoidCallback listener;
  Future _onWillPop() => new Future.value(false); //禁掉返回按钮和右滑关闭

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    widget.operation._notifier = ValueNotifier(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: [
        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: [
                  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 Function(BackPressType); //按返回键时触发

class Operation {
  ValueNotifier _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: [
                 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自定义加载圈)