Flutter|解耦初實(shí)踐

本文提出了一個(gè)簡單的Flutter解耦demo

背景

社區(qū)目前同時(shí)接入了兩個(gè)游戲業(yè)務(wù),在上一篇文章社區(qū)SDK多業(yè)務(wù)適配(業(yè)務(wù)層)也介紹了一些業(yè)務(wù)層的多業(yè)務(wù)適配手段。通過模塊化去定制功能可以有效避免業(yè)務(wù)間相互干擾,但對于通用功能包括UI的區(qū)別還只是逐個(gè)判斷。

隨著版本迭代,還要支持豎屏能力(如圖),多業(yè)務(wù)x橫豎屏導(dǎo)致適配復(fù)雜度加大,不同點(diǎn)越多越難適配;而且Flutter的嵌套式寫法將UI和邏輯放在一起,哪怕只是UI小細(xì)節(jié)的不同也要用if-else區(qū)分,這種寫法使得可讀性比較差,維護(hù)成本變高,容易出錯(cuò)。

在產(chǎn)品策略和設(shè)計(jì)層雖然有盡量保持通用能力一致但還有各有風(fēng)格,細(xì)節(jié)差異也不少,所以技術(shù)上做解耦還是很有必要的。

方案

最關(guān)鍵一步就是將Flutter嵌套寫法中的UI和邏輯分離,即原來 Widget(UI、交互、數(shù)據(jù)) 變成 Cell = View(UI)+ ViewModel (交互) + 數(shù)據(jù)。這里用到了狀態(tài)管理庫Riverpod,與 Provider 類似,通過 Provider 和 Consumer 來實(shí)現(xiàn)狀態(tài)管理,通過依賴注入的方式來共享和訪問狀態(tài),相比Provider 使用上更簡單直觀、可擴(kuò)展更強(qiáng)、提供更好的異步狀態(tài)管理支持。

首先添加依賴項(xiàng):

flutter_riverpod: ^2.3.6

然后創(chuàng)建一個(gè)全局狀態(tài)容器:

void main() {
  runApp(ProviderScope(child: MyApp()));
}

接下來就是改造后的幾個(gè)主要成員,首先是Cell類,綁定了對應(yīng)的View和ViewModel,除了常用的build繪制,還支持在initStatedispose做一些初始化和銷毀的操作:

import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'base_provider.dart';
import 'base_view_model.dart';

abstract class BaseCell<VM extends BaseViewModel>
    extends ConsumerStatefulWidget {
  late BaseVMProvider<VM> provider;
  late Widget Function(BuildContext context, VM value) builder;

  BaseCell({super.key}) {
    this.provider = getVMProvider();
    this.builder = getView();
  }

  BaseVMProvider<VM> getVMProvider();

  Widget Function(BuildContext context, VM vm) getView();

  Function(VM vm)? onInitState() {
    return null;
  }

  Function(VM vm)? onDispose() {
    return null;
  }

  @override
  ConsumerState<BaseCell> createState() => BaseCellState<VM>();
}

class BaseCellState<VM extends BaseViewModel>
    extends ConsumerState<BaseCell<VM>> {
  @override
  void initState() {
    super.initState();
    widget.onInitState()?.call(ref.read(widget.provider));
  }

  @override
  void dispose() {
    widget.onDispose()?.call(ref.read(widget.provider));
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(context, ref.watch(widget.provider));
  }
}

View類主要有一個(gè)ViewModel來處理除UI部分的功能:

import 'package:flutter/material.dart';

import 'base_view_model.dart';

abstract class BaseView<VM extends BaseViewModel> extends StatelessWidget {
  final VM vm;

  BaseView(this.vm);
}

ViewModel類主要是一個(gè)通知View做UI更新的接口:

import 'package:flutter/cupertino.dart';

abstract class BaseViewModel extends ChangeNotifier {
  @protected
  void refreshUI(Function block) {
    block();
    if (hasListeners) {
      notifyListeners();
    }
  }
}

通用的狀態(tài)生成器:

import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

typedef BaseVMProvider<T extends ChangeNotifier>
= AutoDisposeChangeNotifierProvider<T>;

typedef BaseVMRef<T extends ChangeNotifier>
= AutoDisposeChangeNotifierProviderRef<T>;

class BaseVMProviderFactory {
  static BaseVMProvider<T> create<T extends ChangeNotifier>(
      T Function(BaseVMRef<T> ref) create) {
    return ChangeNotifierProvider.autoDispose<T>((ref) {
      return create(ref);
    });
  }
}

舉個(gè)簡單的使用例子,模擬請求返回?cái)?shù)據(jù)后刷新界面:

目前只是一個(gè)初步demo,還需要具體實(shí)踐下來不斷完善,這種解耦思路還是可以參考的。

總結(jié)

如此,在定制橫豎屏或不同業(yè)務(wù)的樣式只要繼承復(fù)寫View即可,無需改動(dòng)邏輯部分,使得適配過程更加高效和可靠。

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

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

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