跟我學(xué)企業(yè)級(jí)flutter項(xiàng)目:手把手教你制作一款低耦合空頁(yè)面widget

前言

如何開(kāi)發(fā)一款易用的,并且可以擴(kuò)展的空頁(yè)面呢?那么今天我將帶領(lǐng)大家手把手開(kāi)發(fā)一款可擴(kuò)展的空頁(yè)面。

開(kāi)發(fā)前注意事項(xiàng)

1、定義好空頁(yè)面狀態(tài)
2、可擴(kuò)展思想(用抽象或基類(lèi)替代實(shí)體)
3、抽離出空頁(yè)面的結(jié)構(gòu)

空頁(yè)面展示

在這里插入圖片描述

開(kāi)始搭建

一、頁(yè)面分析

空頁(yè)面需要元素有:

  1. 展示圖片
  2. 展示文案
  3. 展示刷新按鈕

頁(yè)面功能點(diǎn):

  1. 文案可自定義
  2. 圖片可自定義
  3. 按鈕可隱藏

wiget作用范圍:

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

二、定義狀態(tài)

2.1 幾種狀態(tài)

enum EmptyStatus {
  fail, //失敗視圖
  loading, //加載視圖
  nodata, //沒(méi)有數(shù)據(jù)視圖
  none //沒(méi)有狀態(tài)
}

沒(méi)有狀態(tài)該空頁(yè)面就隱藏掉

2.2 空頁(yè)面刷新回調(diào)

abstract class IEmptyRefresh{

  void pressedReload();

}

2.3 定義copy類(lèi)(復(fù)用做準(zhǔn)備)&定義空接口(抽離要擴(kuò)展的方法)

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 空頁(yè)面實(shí)現(xiàn)類(lèi)

默認(rèn)加載中頁(yè)面

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;
  }


}

默認(rèn)空頁(yè)面

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;;
  }


}
默認(rèn)網(wǎng)絡(luò)失效頁(yè)

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 空頁(yè)面管理類(lèi)

可進(jìn)行外部配置


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 核心邏輯

判斷狀態(tài),并進(jìn)行類(lèi)型拷貝,并增加自定義參數(shù)

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
        ;
    }

如果是包裹類(lèi)型需要stack進(jìn)行包裝

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),
            ),
          ),),
      ],
    );

判斷是否有網(wǎng),有網(wǎng)的話,就刷新,沒(méi)網(wǎng)的話,就提示


  @override
  void pressedReload() async{
    bool isConnectNetWork = await isConnected();
    if(isConnectNetWork){
      widget.refresh.call();
    }else{
      TipToast.instance.tip(LibLocalizations.getLibString().libNetWorkNoConnect!,tipType: TipType.error);
    }
  }

  // 是否有網(wǎng)
  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;
}

三、空頁(yè)面widget實(shí)現(xiàn)完整代碼

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);
    }
  }


  // 是否有網(wǎng)
  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;
}

四、空頁(yè)面widget使用代碼

包裹使用 (代碼中的webview封裝參見(jiàn):跟我學(xué)企業(yè)級(jí)flutter項(xiàng)目:如何封裝一套易用,可擴(kuò)展的Hybrid混合開(kāi)發(fā)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;
      }
    }

感謝大家閱讀我的文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容