Flutter系列笔记 - 基础页面的搭建

328 阅读3分钟
  • 在Flutter开发中,页面的概念相较于原生来说被模糊了,Flutter没有页面类的概念,相对于的是一切皆Widget,不管一个页面还是一个组件,我们都称它为Widget,对于一些重复性基础的功能,我们希望可以像原生一样有一个base类来统一管理,基于实战项目,对基础页面的搭建进行下复盘整理。

基础页面需要的功能:1、生命周期管理。2、网络状态管理。3加载dialog统一管理。我们希望这些功能可以在基础页面进行统一管理。

1、基础StatefulWidget--> BaseStatefulPage 和 BaseStatefulPageState

  • 1. 生命周期管理
    基于原生生命周期封装。

代码:

abstract class BaseStatefulPage extends StatefulWidget {
  const BaseStatefulPage({Key? key}) : super(key: key);

  @override
  BaseStatefulPageState<BaseStatefulPage> createState();
}
// AutomaticKeepAliveClientMixin 页面状态保持
// WidgetsBindingObserver 生命周期相关
abstract class BaseStatefulPageState<T extends BaseStatefulPage>
    extends State<T>
    with AutomaticKeepAliveClientMixin, WidgetsBindingObserver {
    

/// 生命周期
/// [initState] widget创建执行 且只执行一次
/// [build] 状态更新就会执行
/// [didUpdateWidget] 页面重新build是调用
/// [onResumed] 界面可见 从后台切换到前台触发
/// [onPause] 应用不可见 切换到后台触发
/// [onInactive] 应用不可见或者弹出框覆盖本页面 一般不咋用
/// [dispose] 销毁widget 在此回调释放资源等
@override
void initState() {
  super.initState();
// 注册
WidgetsBinding.instance?.addObserver(this);
} 

  @override
  void dispose() {
    super.dispose();
    // 释放
    WidgetsBinding.instance?.removeObserver(this);
  }
    

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  LogUtil.i(state, tag: "state");
  switch (state) {
    case AppLifecycleState.resumed:
      onResumed();
      break;
    case AppLifecycleState.inactive:
      onInactive();
      break;
    case AppLifecycleState.paused:
      onPause();
      break;
    case AppLifecycleState.detached:
      break;
  }
}
void onResumed() {} 

void onInactive() {}

void onPause() {}


  • 2. 网络状态管理
    我希望在有网络请求的页面可以统一管理网络加载成功、失败等各种状态,首先我们需要一个加载基类我这边一共有:加载中、加载成功、加载失败、空数据四种状态,加载失败可以统一管理重新加载。

    代码:

/// 网络加载根布局
class LoadingLayout extends StatelessWidget {
 /// 加载成功
 static const int success = 0;

 /// 加载中
 static const int loading = 1;

 /// 加载错误
 static const int error = 2;

 /// 数据为空
 static const int empty = 3;

 final Widget child; // 子布局
 final Function? onReload; //重新加载方法
 final int state; //页面标记
 final ErrorRes? errorRes;

 const LoadingLayout(
     {Key? key,
     this.onReload,
     this.state = loading,
     this.errorRes,
     required this.child})
     : super(key: key);

 @override
 Widget build(BuildContext context) {
   // 点击其他部位关闭键盘
   return KeyboardDismissOnTap(
       child: Material(
           color: Theme.of(context).backgroundColor,
           child: Stack(
             children: [child, _page(context)],
           )));
 }

 Widget _page(BuildContext context) {
   Widget widget;
   switch (state) {
     case loading:
       widget = const LoadingPage();
       break;
     case success:
       widget = const SizedBox();
       break;
     case empty:
       widget = EmptyPage(onTab: onReload,);
       break;
     case error:
       widget = ErrorPage(onReload, errorRes: errorRes);
       break;
     default:
       widget = EmptyPage(onTab: onReload,);
       break;
   }
   return widget;
 }
}

在基础页面类里加入:


  /// 构建具体业务页面
  Widget buildPage(BuildContext context); 
  
@override
Widget build(BuildContext context) {
  super.build(context);
  return LoadingLayout(
          child: buildPage(context),
          state: loadingState,
          onReload: onReload,
          errorRes: errorRes,
        );
}
 /// 初始化网络请求数据、 重新加载调用的方法
  void initHttpData();
  
  void onReload() {
    setState(() {
      loadingState = LoadingLayout.loading;
    });
    initHttpData();
  }

  /// state 请求状态
  /// result 成功之后可以操作业务代码
  setLoadingState(int state, {Function? result, ErrorRes? errorRes}) {
    setState(() {
      loadingState = state;
      if (state == LoadingLayout.error) {
        this.errorRes = errorRes;
      }
      if (result != null) result();
    });
  }

  setLoading() {
    setLoadingState(LoadingLayout.loading);
  }

  setSuccess({Function? result}) {
    setLoadingState(LoadingLayout.success, result: result);
  }

  setError(ErrorRes res) {
    setLoadingState(LoadingLayout.error, errorRes: res);
  }

  setEmpty() {
    setLoadingState(LoadingLayout.empty);
  }
  • 3. 加载Dialog统一管理
///加载loading
showLoadingDialog(BuildContext context, {String? text}) {
  showDialog(
      context: context,
      builder: (context) {
        return LoadingDialog(
          text: text,
        );
      });
}

/// 关闭loading
dismissLoadingDialog(BuildContext context) {
  Navigator.pop(context);
}

具体的状态页面根据自己UI设计具体编写即可,这里只整理下实现的具体思路 --End