美文网首页Flutter圈子FlutterFlutter中文社区
跟我学企业级flutter项目:手把手教你制作一款低耦合空页面w

跟我学企业级flutter项目:手把手教你制作一款低耦合空页面w

作者: 王二蛋和他的狗 | 来源:发表于2022-03-21 17:40 被阅读0次

    前言

    如何开发一款易用的,并且可以扩展的空页面呢?那么今天我将带领大家手把手开发一款可扩展的空页面。

    开发前注意事项

    1、定义好空页面状态
    2、可扩展思想(用抽象或基类替代实体)
    3、抽离出空页面的结构

    空页面展示

    在这里插入图片描述

    开始搭建

    一、页面分析

    空页面需要元素有:

    1. 展示图片
    2. 展示文案
    3. 展示刷新按钮

    页面功能点:

    1. 文案可自定义
    2. 图片可自定义
    3. 按钮可隐藏

    wiget作用范围:

    1. 可包裹其他widget
    2. 不包裹其他widget

    二、定义状态

    2.1 几种状态

    enum EmptyStatus {
      fail, //失败视图
      loading, //加载视图
      nodata, //没有数据视图
      none //没有状态
    }
    

    没有状态该空页面就隐藏掉

    2.2 空页面刷新回调

    abstract class IEmptyRefresh{
    
      void pressedReload();
    
    }
    
    

    2.3 定义copy类(复用做准备)&定义空接口(抽离要扩展的方法)

    abstract class Copyable<T> {
      T copy();
    }
    
    abstract class IEmpty implements Copyable<IEmpty>{
      IEmptyRefresh? iEmptyRefresh;
      Widget? diyImage; // 自定义图片替换
      Widget? diyText;// 自定义文案替换
      Widget? image();
    
      Widget? text();
    
      Widget? refresh();
    }
    

    2.4 空页面实现类

    默认加载中页面
    
    class DefaultLodingPage extends IEmpty{
    
      @override
      Widget? text() {
        return diyText??Text(
          LibEmptyManager.instance.libEmptyPageLoding,
          style: TextStyle(fontSize: LibEmptyManager.instance.textSize, color: AppTheme.instance.textColor()),
        );
      }
    
      @override
      Widget? image() {
        return null;
      }
    
      @override
      Widget? refresh() => null;
    
      @override
      IEmpty copy() {
        return DefaultLodingPage()
          ..diyImage = diyImage
          ..diyText = diyText
          ..iEmptyRefresh=iEmptyRefresh;
      }
    
    
    }
    
    
    默认空页面
    
    class DefaultEmptyPage extends IEmpty{
    
      @override
      Widget? text() {
        return diyText??Text(
          LibEmptyManager.instance.libEmptyPageNoData,
          style: TextStyle(fontSize: LibEmptyManager.instance.textSize, color: AppTheme.instance.textColor()),
        );
      }
    
      @override
      Widget? image() {
        return Padding(
          padding: const EdgeInsets.only(bottom: 20),
          child: diyImage??Icon(LibEmptyManager.instance.imageNoData,color: AppTheme.instance.imageColor(),size: LibEmptyManager.instance.imageSize,),
        );
      }
    
      @override
      Widget? refresh() => null;
    
      @override
      IEmpty copy() {
        return DefaultEmptyPage()
          ..diyImage = diyImage
          ..diyText = diyText
          ..iEmptyRefresh=iEmptyRefresh;;
      }
    
    
    }
    
    默认网络失效页
    
    class DefaultNetWorkError extends IEmpty {
      @override
      Widget? text() {
        return diyText??Text(
          LibEmptyManager.instance.libEmptyPageNetError,
          style: TextStyle(fontSize: LibEmptyManager.instance.textSize, color: AppTheme.instance.textColor()),
        );
      }
    
      @override
      Widget? image() {
        return Padding(
          padding: const EdgeInsets.only(bottom: 20),
          child: diyImage??Icon(LibEmptyManager.instance.imageNetWork,color: AppTheme.instance.imageColor(),size: LibEmptyManager.instance.imageSize,),
        );
      }
    
      @override
      Widget? refresh() {
        return Padding(
          padding: const EdgeInsets.only(top: 20),
          child: Padding(
            padding: const EdgeInsets.only(left: 20,right: 20),
            child: ElevatedButton(onPressed: () => iEmptyRefresh?.pressedReload(),
              style:  ButtonStyle(
                backgroundColor: MaterialStateProperty.all(AppTheme.instance.btnBackColor()),
                shape: MaterialStateProperty.all(const StadiumBorder()),
              )
              , child: Text(LibEmptyManager.instance.libRefresh,style: TextStyle(fontSize: LibEmptyManager.instance.libRefreshSize,color: AppTheme.instance.btnTextColor())),),
          ),
        );
      }
    
      @override
      IEmpty copy() {
        return DefaultNetWorkError()
          ..diyImage = diyImage
          ..diyText = diyText
          ..iEmptyRefresh=iEmptyRefresh;;
      }
    }
    

    2.5 空页面管理类

    可进行外部配置

    
    class LibEmptyManager{
      IEmpty emptyPage = DefaultEmptyPage();
      IEmpty loadingPage = DefaultLodingPage();
      IEmpty netWorkError = DefaultNetWorkError();
    
      late LibEmptyConfig libEmptyConfig;
    
      LibEmptyManager._();
    
      static final LibEmptyManager _instance = LibEmptyManager._();
    
      static LibEmptyManager get instance {
        return _instance;
      }
    

    2.6 核心逻辑

    判断状态,并进行类型拷贝,并增加自定义参数

    switch(widget.layoutType){
          case EmptyStatus.none:
            visable = true;
            break;
        // return widget.child;
          case EmptyStatus.fail:
            iEmpty = LibEmptyManager.instance.netWorkError.copy()
              ..diyText = widget.networkText
              ..diyImage = widget.networkImage
            ;
            break;
          case EmptyStatus.nodata:
            iEmpty = LibEmptyManager.instance.emptyPage.copy()
              ..diyText = widget.emptyText
              ..diyImage = widget.emptyImage
            ;
            break;
          case EmptyStatus.loading:
            iEmpty = LibEmptyManager.instance.loadingPage;
            break;
          default:
            iEmpty = LibEmptyManager.instance.emptyPage.copy()
              ..diyText = widget.emptyText
              ..diyImage = widget.emptyImage
            ;
        }
    

    如果是包裹类型需要stack进行包装

    return Stack(
          children: [
            Offstage(
              offstage: !visable,
              child: widget.child,
            ),
            Offstage(
              offstage: visable,
              child: Container(
                width: double.infinity,
                color: AppTheme.instance.backgroundColor(),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: _listEmpty(iEmpty),
                ),
              ),),
          ],
        );
    

    判断是否有网,有网的话,就刷新,没网的话,就提示

    
      @override
      void pressedReload() async{
        bool isConnectNetWork = await isConnected();
        if(isConnectNetWork){
          widget.refresh.call();
        }else{
          TipToast.instance.tip(LibLocalizations.getLibString().libNetWorkNoConnect!,tipType: TipType.error);
        }
      }
    
      // 是否有网
      Future<bool> isConnected() async {
        var connectivityResult = await (Connectivity().checkConnectivity());
        return connectivityResult != ConnectivityResult.none;
      }
    

    组装empty

    
    List<Widget> _listEmpty(IEmpty? iEmpty) {
      List<Widget> tempEmpty = [];
      if(iEmpty!=null){
        Widget? image = iEmpty.image();
        Widget? text = iEmpty.text();
        Widget? refresh = iEmpty.refresh();
        if(image!=null){
          tempEmpty.add(image);
        }
        if(text!=null){
          tempEmpty.add(text);
        }
        if(refresh!=null){
          tempEmpty.add(refresh);
        }
    
      }
      return tempEmpty;
    }
    
    

    三、空页面widget实现完整代码

    class LibEmptyView extends StatefulWidget{
      Widget? child;
      EmptyStatus layoutType;
      VoidCallback refresh;
    
    
      Widget? networkImage;Widget? networkText;
      Widget? emptyImage;Widget? emptyText;
    
      LibEmptyView({Key? key, this.child,required this.refresh,required this.layoutType,this.networkImage,this.networkText, this.emptyImage,this.emptyText}) : super(key: key);
    
      @override
      State<StatefulWidget> createState() => _LibEmptyViewState();
    
    }
    
    class _LibEmptyViewState extends State<LibEmptyView> implements IEmptyRefresh{
      //控制器
    
      @override
      Widget build(BuildContext context) {
        IEmpty? iEmpty;
        bool visable = false;
        switch(widget.layoutType){
          case EmptyStatus.none:
            visable = true;
            break;
          case EmptyStatus.fail:
            iEmpty = LibEmptyManager.instance.netWorkError.copy()
              ..diyText = widget.networkText
              ..diyImage = widget.networkImage
            ;
            break;
          case EmptyStatus.nodata:
            iEmpty = LibEmptyManager.instance.emptyPage.copy()
              ..diyText = widget.emptyText
              ..diyImage = widget.emptyImage
            ;
            break;
          case EmptyStatus.loading:
            iEmpty = LibEmptyManager.instance.loadingPage;
            break;
          default:
            iEmpty = LibEmptyManager.instance.emptyPage.copy()
              ..diyText = widget.emptyText
              ..diyImage = widget.emptyImage
            ;
        }
        iEmpty?.iEmptyRefresh = this;
    
        
    
        return Stack(
          children: [
            Offstage(
              offstage: !visable,
              child: widget.child,
            ),
            Offstage(
              offstage: visable,
              child: Container(
                width: double.infinity,
                color: AppTheme.instance.backgroundColor(),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: _listEmpty(iEmpty),
                ),
              ),),
          ],
        );
      }
    
      @override
      void pressedReload() async{
        bool isConnectNetWork = await isConnected();
        if(isConnectNetWork){
          widget.refresh.call();
        }else{
          TipToast.instance.tip(LibLocalizations.getLibString().libNetWorkNoConnect!,tipType: TipType.error);
        }
      }
    
    
      // 是否有网
      Future<bool> isConnected() async {
        var connectivityResult = await (Connectivity().checkConnectivity());
        return connectivityResult != ConnectivityResult.none;
      }
    }
    
    List<Widget> _listEmpty(IEmpty? iEmpty) {
      List<Widget> tempEmpty = [];
      if(iEmpty!=null){
        Widget? image = iEmpty.image();
        Widget? text = iEmpty.text();
        Widget? refresh = iEmpty.refresh();
        if(image!=null){
          tempEmpty.add(image);
        }
        if(text!=null){
          tempEmpty.add(text);
        }
        if(refresh!=null){
          tempEmpty.add(refresh);
        }
    
      }
      return tempEmpty;
    }
    

    四、空页面widget使用代码

    包裹使用 (代码中的webview封装参见:跟我学企业级flutter项目:如何封装一套易用,可扩展的Hybrid混合开发webview

    LibEmptyView(
            layoutType: status,
            refresh: () {
             
              status = EmptyStatus.none;
              _innerWebPageController.reload();
             
            },
            
            child: InnerWebPage(widget.url,titleCallBack: (title){
              setState(() {
                urlTitle = title;
              });
            },javascriptChannels: widget._javascriptChannels,urlIntercept: widget._urlIntercept,onInnerWebPageCreated: (innerWebPageController){
              _innerWebPageController = innerWebPageController;
              widget._javascriptChannels?.webPageCallBack = webPageCallBack;
              widget._urlIntercept?.webPageCallBack = webPageCallBack;
            },onWebResourceError: (error){
              setState(() {
                status = EmptyStatus.fail;
              });
            },),
          ),
        ));
    

    非包裹使用

    if(_status == EmptyStatus.none){
          return _listViewUi.call(_allReportItems);
        }else{
          var empty = LibEmptyView(
            layoutType: _status,
            refresh: () {
              _status = EmptyStatus.loading;
              LibLoading.show();
              _refreshCenter.refreshData();
            },networkImage: networkImage,networkText: networkText,emptyImage: emptyImage,emptyText: emptyText,);
          if(builder!=null){
            return builder.call(context,empty);
          }else{
            return empty;
          }
        }
    

    感谢大家阅读我的文章

    相关文章

      网友评论

        本文标题:跟我学企业级flutter项目:手把手教你制作一款低耦合空页面w

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