Skip to content

2026 生产级参考架构指南:Android (Compose/XML) 与 Flutter 最优解 ​

回到总览:状态管理、路由、架构
相关模块:Compose / Flutter / Vue 状态管理对照 · Riverpod / Provider / Bloc / GetX 对比 · ViewModel 架构、SavedStateHandle 与进程被杀恢复

一句话定义 ​

生产级参考架构(Reference Architecture)是指在大型移动端项目中,为避免“业务逻辑与 UI 耦合”、“数据源混乱”及“多端状态不一致”,综合官方规范与当前生态最优解所制定的单向数据流 (UDF)、分层解耦与不可变数据模型的标准架构范式。

代码索引 ​

计划补齐的实验:

  • labs/shared/state-routing/reference-architecture-demo/ — Flutter Riverpod+Freezed 范式与 Android Compose MVI 统一参考架构 Demo

为什么需要 ​

  • 为什么在大型 App 开发中必须建立统一的“参考架构(Reference Architecture)”?
    • 一句话答:如果没有统一的分层架构,团队成员会随意在 UI 层直接发网络请求、在 ViewModel 内部操作数据库,导致代码难以编写单元测试、状态变化不可追踪且难以重构。
  • Flutter 2026 年最成熟落地的推荐分层架构是什么?
    • 一句话答:采用 UI 层 (ConsumerWidget) → ViewModel 层 (Riverpod AsyncNotifier) → Repository 门面层 → DataSource 层 (Dio + Isar/Hive) → 不可变 Model (freezed) 的 Clean Architecture 范式。
  • Android Compose 与传统 XML 在架构模式上有何演进差异?
    • 一句话答:传统 XML 采用 MVVM 模式(ViewModel 通过 LiveData/StateFlow 暴露独立属性,View 双向绑定);而 Compose 拥抱 MVI / UDF (单向数据流) 模式(ViewModel 暴露单一不可变 UiState 密封类,UI 发送 UiEvent Intent)。

架构对比全景 ​

1. Flutter 2026 生产级最优解分层架构 ​

图注补充:1. UI 展示层 (Views / Widgets);2. 视图模型层 (ViewModels / Controllers);Riverpod AsyncNotifierProvider / StateNotifier;3. 数据仓库层 (Repositories);Repository (数据唯入口, 协调缓存与网络);Remote DataSource (Dio / Retrofit);Local DataSource (Isar / Hive / Sqflite);freezed + json_serializable 不可变实体;1. 监听 State / 发起 Event;5. 返回 JSON 反序列化。

Flutter 官方与生态推荐技术栈表 (2026 最优解) ​

官方架构层级具体技术栈推荐 (2026 最优解)职责说明
UI 层 (Views)StatelessWidget / ConsumerWidget纯 UI 展示,无状态,不包含任何业务与网络逻辑
UI 层 (ViewModels)Riverpod (NotifierProvider / AsyncNotifier)桥接 UI 和数据,管理加载/成功/失败 AsyncValue 状态
数据层 (Repositories)Riverpod (作为依赖注入 DI 容器)协调网络和本地缓存,是数据的唯一入口,屏蔽数据源细节
数据层 (DataSources)Dio (网络) + Isar / Hive (本地持久化)纯粹的数据读写工具,无业务逻辑,支持拦截器与加密
数据模型 (Models)freezed + json_serializable生成 100% 不可变 (Immutable) 的实体类与深拷贝 copyWith

2. Android 架构演进:Compose (MVI) vs XML (MVVM) ​

图注补充:1. Intent / UiEvent;2. 单一不可变 UiState (StateFlow);1. User Action;2. 多 LiveData / DataBinding。

Android 架构对比表 ​

维度Android Compose 架构 (MVI / UDF)Android 传统 XML 架构 (MVVM)
状态流向严格单向数据流 (Unidirectional Data Flow)响应式双向/多向数据绑定
UI 状态容器StateFlow<UiState> (包装为单个不可变 Sealed Class)多个独立的 MutableLiveData<T> 字段
组件解耦State Hoisting (状态提升) 为 Stateless Composable通过 DataBinding 或 ViewBinding 绑定 XML
依赖注入Hilt (Dagger) / Koin(4.0 详解)Hilt / Dagger2
网络与数据库Retrofit / Ktor + RoomRetrofit + Room

代码范式 ​

1. Flutter 2026 推荐代码规范 (Riverpod + Freezed) ​

dart
// 1. Model 层:使用 freezed 生成不可变数据模型
@freezed
class UserProfile with _$UserProfile {
  const factory UserProfile({
    required String id,
    required String name,
    required String avatarUrl,
  }) = _UserProfile;

  factory UserProfile.fromJson(Map<String, dynamic> json) => _$UserProfileFromJson(json);
}

// 2. ViewModel 层:使用 Riverpod AsyncNotifier 管理异步状态
@riverpod
class UserProfileNotifier extends _$UserProfileNotifier {
  @override
  Future<UserProfile> build(String userId) async {
    // 自动处理 Loading / Data / Error 状态
    final repository = ref.watch(userRepositoryProvider);
    return repository.getUserProfile(userId);
  }

  Future<void> updateName(String newName) async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(() async {
      final repository = ref.read(userRepositoryProvider);
      return repository.updateName(newName);
    });
  }
}

// 3. UI 层:ConsumerWidget 响应式渲染
class UserProfileScreen extends ConsumerWidget {
  final String userId;
  const UserProfileScreen({super.key, required this.userId});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final profileAsync = ref.watch(userProfileNotifierProvider(userId));

    return profileAsync.when(
      data: (profile) => Text("Hello, ${profile.name}"),
      loading: () => const CircularProgressIndicator(),
      error: (err, stack) => Text("Error: $err"),
    );
  }
}

2. Android Compose 推荐代码规范 (MVI + StateFlow) ​

kotlin
// 1. 单一不可变 UiState 声明
sealed interface UserUiState {
    object Loading : UserUiState
    data class Success(val user: User) : UserUiState
    data class Error(val message: String) : UserUiState
}

// 2. ViewModel 暴露单向数据流
@HiltViewModel
class UserViewModel @Inject constructor(
    private val repository: UserRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
    val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()

    fun loadUser(userId: String) {
        viewModelScope.launch {
            _uiState.value = UserUiState.Loading
            try {
                val user = repository.getUser(userId)
                _uiState.value = UserUiState.Success(user)
            } catch (e: Exception) {
                _uiState.value = UserUiState.Error(e.message ?: "Unknown Error")
            }
        }
    }
}

常见误配、事故后果与排障 ​

1. 事故:在 UI 层直接通过 Dio / Retrofit 发网络请求 ​

  • 误配原因:为了图方便,直接在 onClick 或 build() 中调用 Dio().get(...)。
  • 后果:破坏了分层架构;当网络失败或返回数据格式改变时,需要修改 UI 代码,且无法对 UI 进行自动化单元测试!
  • 排障与修法:所有网络请求必须收口至 DataSource 与 Repository,UI 层只能通过 ViewModel 的方法间接调用。

复习检查题 ​

  1. 在 Flutter 2026 最成熟的推荐架构中,使用 freezed 生成不可变数据模型 (Immutable Model) 有什么好处?

    答:使用 freezed 生成的不可变模型天然支持 == 值的相等性比较与深拷贝 copyWith。它能保证数据在传递过程中不会被任何组件意外修改(Side Effects),极大地提升了状态变更的可追溯性,并使得 Riverpod 等状态管理能够精准识别数据是否真的发生改变,从而避免无效刷新。

  2. Android Compose 推荐的 MVI (UDF) 模式与传统 XML 的 MVVM 模式最核心的区别是什么?

    答:最核心的区别在于状态的集中性与单向流向。传统 MVVM 的 ViewModel 通常暴露多个独立的 LiveData 变量,容易导致状态组合混乱;而 MVI 模式将页面的所有状态收口包装为一个单一的不可变 UiState(通常是 Sealed Class 密封类),UI 只能通过发送 UiEvent 触发 ViewModel 产生新的 UiState,实现了绝对单向的数据流动。


速记 ​

  • Flutter 分层:Widget 看界面,Notifier 管状态,Repo 作门面,Dio/Isar 做读写,Freezed 锁数据。
  • Android 架构:Compose 选 MVI 结合 StateFlow 密封类,XML 选 MVVM 配合 LiveData/StateFlow。
  • 红线底线:UI 绝不直接查 DB/网络,所有数据流动必须经过 Repository 与 ViewModel。

站点构建时间:2026/8/24 23:43:17