基於Flutter2.0,語法空安全,Provider MVVM的最佳實踐,可以用於線上應用的優秀架構

陳平大將發表於2021-05-19

前言

BaseFlutter開源專案基於Flutter2.0,語法上空安全,是Provider MVVM的最佳實踐,可以用於線上應用的優秀架構,該專案使用了很多實際專案開發中需要用到了技術和第三方框架,而且做了很多基礎封裝,可以直接拿到實際專案中使用,而且學習這個專案還可以幫助新手減降低學習難度,提供學習方向,實現flutter快速入門,github連結

程式碼結構

image.png

image.png

部分程式碼示例

abstract class BaseState<W extends StatefulWidget, VM extends BaseViewModel>
    extends State<W>
    with
        BaseStateInterface,
        NavigatorMixin,
        ToastMixin,
        EventBusMixin,
        SharePreferenceMixin,
        ScreenAdapterMixin{
  late VM viewModel;//這裡需要改成late,不能用?,不然Provider會報錯
  EventBus? eventBus;
  LoadingDialog? loadingDialog;

  late bool isBuildFinish;

  @override
  void initState() {
    super.initState();
    isBuildFinish = false;
    WidgetsBinding widgetsBinding = WidgetsBinding.instance!;
    widgetsBinding.addPostFrameCallback((callback) {
      //說明build結束了
      print("====>build結束了");
      isBuildFinish = true;
      onBuildFinish();
    });

    setContext(context);
    setIsDispose(false);
    viewModel = getIt.get<VM>();
    viewModel.context = context;
    viewModel.init();
    viewModel.showLoadingFun = () {
      showLoading();
    };
    viewModel.dismissLoadingFun = () {
      loadingDialog?.dismissDialog();
    };
    initEventBus();
  }

  @override
  void onBuildFinish() {}

  @override
  void initEventBus() {
    if (eventBus == null) {
      eventBus = EventBus.get();
    }
  }

  void showLoading() async {
    if (isBuildFinish) {
      //必須等到父元件build結束後才能構建自己,https://blog.csdn.net/qq_39493848/article/details/108514136
      showDialog(
          context: context,
          builder: (_) {
            if (loadingDialog == null) {
              loadingDialog = LoadingDialog();
            }
            return loadingDialog!;
          });
    } else {
      await Future.delayed(Duration(milliseconds: 10));
      showLoading();
    }
  }

  @override
  void dispose() {
    super.dispose();
    setIsDispose(true);
    viewModel.showLoadingFun = null;
    viewModel.dismissLoadingFun = null;
  }
}

複製程式碼
abstract class BaseViewModel<M extends Object> extends ChangeNotifier
    with
        BaseViewModelInterface,
        NavigatorMixin,
        ToastMixin,
        SharePreferenceMixin,
        EventBusMixin,
        DataBaseMixin {
  int _loadNum = 0;
  int _minLoadNum = 1;
  late BuildContext context;
  late M model;
  bool _isDispose = false;

  bool get isDispose => _isDispose;

  int needLoadingRequestCount = 0;

  bool isLoading = false;

  Function()? showLoadingFun;

  Function? dismissLoadingFun;

  static bool isNeedCatchError = false;

  set minLoadNum(int value) {
    _minLoadNum = value;
  }

  set loadNum(int value) {
    _loadNum = value;
  }

  int get loadNum {
    return _loadNum;
  }

  void notifyPage() {
    if (!_isDispose) {
      loadNum++;
      print("====>loadNum:$loadNum");
      if (_loadNum >= _minLoadNum) {
        print("====>notifyListeners");
        notifyListeners();
      }
    }
  }

  @override
  void init() {
    model = getIt.get<M>();
    setContext(context);
    setIsDispose(false);
  }

  void showLoading(bool isNeedLoading) {
    if (isNeedLoading) {
      needLoadingRequestCount++;
      if (!isLoading) {
        isLoading = true;
        if (showLoadingFun != null) {
          showLoadingFun!.call();
        }
        showLoadingFun?.call();
      }
    }
  }

  void dismissLoading(bool isNeedLoading) {
    if (isNeedLoading) {
      needLoadingRequestCount--;
      if (needLoadingRequestCount == 0) {
        isLoading = false;
        if (dismissLoadingFun != null) {
          dismissLoadingFun!.call();
        }
        dismissLoadingFun?.call();
      }
    }
  }

  /// 發起網路請求,同時處理異常,loading
  void sendRequest<T>(Future<T> future, FutureOr<dynamic> onValue(T value),
      {Function(Exception e)? error, bool isNeedLoading = false}) {
    showLoading(isNeedLoading);
    future.then((t) {
      dismissLoading(isNeedLoading);
      onValue(t);
    });
    if (isNeedCatchError) {
      future.catchError((e) {
        dismissLoading(isNeedLoading);
        print("====>error:$e");
        if (error != null) {
          error(e);
        }
      });
    }
  }

  @override
  void dispose() {
    super.dispose();
    _isDispose = true;
    setIsDispose(_isDispose);
  }
}
複製程式碼
@injectable
class LoginViewModel extends BaseViewModel<LoginModel> {
  @factoryMethod
  LoginViewModel();

  String loginName = "";
  String psw = "";

  ///登入
  void login() {
    if (loginName.isEmpty) {
      showToast("登入賬號不可為空");
    } else if (psw.isEmpty) {
      showToast("登入密碼不可為空");
    } else {
      sendRequest<LoginResult>(model.login(loginName, psw), (value) {
        if (value.errorCode == 0) {
          UserInfoSp.getInstance().uid = value.data!.id!;
          UserInfoSp.getInstance().token = value.data!.token!;
          UserInfoSp.getInstance().userName = value.data!.username!;
          pop();
          push(MainPage());
        } else {
          showToast(value.errorMsg!);
        }
      }, isNeedLoading: true);
    }
  }
}
複製程式碼

使用的第三方框架

  • 1. injectable
    配合get_it框架,在編譯時生成程式碼,實現依賴注入
  • 2.dio
    實現網路請求
  • 3.get_it
    實現依賴注入
  • 4.retrofit
    結合dio實現網路請求,編譯時生成網路請求的程式碼
  • 5.logger
    日誌列印
  • 6.toast
    吐司
  • 7.event_bus
    實現不同頁面和元件的通訊
  • 8.json_serializable
    結合json_annotation實現json資料序列化
  • 9.extended_image
    實現網路圖片的載入,強大的官方 Image 擴充套件元件, 支援載入以及失敗顯示,快取網路圖片,縮放拖拽圖片,繪製自定義效果等功能
  • 10.webview_flutter
    實現網頁的載入
  • 11.shared_preferences
    簡單的資料持久儲存
  • 12.pull_to_refresh
    實現下拉重新整理和分頁載入
  • 13.floor
    資料庫,使用類似於retrofit
  • 14.flutter_swiper
    圖片輪播

使用的架構和基礎封裝

  • 基於Flutter2.0,語法空安全
  • 結合Provider實現MVVM架構,封裝了BaseState,BaseStatefulWidget,BaseViewModel
  • 結合模板方法模式,泛型,Mixin,依賴注入等方式,封裝了大量重複的邏輯,簡化了開發難度
  • Mixin類的封裝:目前包含NavigatorMixin,ToastMixin,SharePreferenceMixin,EventBusMixin,DataBaseMixin
  • 基礎Widget的封裝:例如BottomDialog,CenterDialog,EnsureAndCancelDialog,LoadingDialog,PopupWindow,CommonWrap,LazyIndexedStack等等
  • BaseViewModel統一網路請求,統一發起網路請求,同時處理異常,loading
  • 使用擴充套件函式:擴充套件自Object,List,int,Widget,讓程式碼更加簡潔優雅

後期規劃

1.路由,實現各模組,各業務的解耦
2.元件化
3.記憶體洩漏檢測
4.埋點框架
5.各種炫酷動畫
6.效能優化

QQ交流群

群號碼:770892444

相關文章