Flutter MVVM架构与状态管理插件maac_mvvm_with_riverpod的使用

Flutter MVVM架构与状态管理插件maac_mvvm_with_riverpod的使用

MVVM

maac_mvvm_with_riverpod 是一个扩展包,用于与 RiverPod 一起使用的 maac_mvvm 包。此包保留了 maac_mvvm 包中的架构和组件,如 ViewModel、StreamData 和 ViewModelWidget,并添加了支持 RiverPod 的额外组件,如 ConsumerViewModelWidget 和 RiverViewModel。

它简单、干净且易于实现。

请参阅 maac_mvvm

特性

ConsumerViewModelWidget

用于构建绑定到 ViewModel 的 UI 小部件的地方。

RiverViewModel

持有与其绑定的小部件的逻辑和生命周期,并继承自 maac_mvvm 的 ViewModel 类。

开始使用

安装

在 pubspec.yaml 文件中安装:

flutter pub add maac_mvvm_with_riverpod

使用方法

1 - 安装包

flutter pub add maac_mvvm_with_riverpod

2 - 创建你的 ViewModel

通过继承 RiverViewModel 来创建 ViewModel,以便管理小部件的逻辑和生命周期。此 ViewModel 将可以访问 RiverViewModel 中定义的状态和方法,以及 maac_mvvm 的 ViewModel 中定义的方法。以下是一个简单的 ViewModel,用于从小部件中增加计数器。

final _exampleUIStateProvider = StateProvider.autoDispose<ExamplePageUIState>((ref) {
  return ExamplePageUIState();
});

final exampleViewModelProvider = Provider.autoDispose<ExamplePageViewModel>((ref) {
  return ExamplePageViewModel(uiState: ref.watch(_exampleUIStateProvider.notifier));
});

class ExamplePageUIState {
  int counter;

  ExamplePageUIState({this.counter = 0});

  ExamplePageUIState copyWith({int? counter}) {
    return ExamplePageUIState(
      counter: counter ?? this.counter,
    );
  }
}

class ExamplePageViewModel extends RiverViewModel<ExamplePageUIState> {
  ExamplePageViewModel({required super.uiState});
}

3 - 创建绑定到 ViewModel 的小部件

ConsumerViewModelWidget 只包含两个方法:createViewModel 和 build。

  • buildWidget 方法用于构建界面。
  • viewModelProvider 方法用于提供 ViewModel 提供者。

与 maac_mvvm 类似,我们不必担心小部件的其他生命周期,因为它们会根据 ViewModel 自动调用。

class ExamplePage extends ConsumerViewModelWidget<ExamplePageViewModel> {
  const ExamplePage({super.key});
  
  [@override](/user/override)
  AutoDisposeProvider<ExamplePageViewModel> viewModelProvider() => exampleViewModelProvider;
  
  [@override](/user/override)
  Widget buildWidget(BuildContext context, WidgetRef ref, ExamplePageViewModel viewModel) {
    return BuildYourPageWidget();
  }
}

如果小部件有传递的属性并且需要传递给 ViewModel,我们可以覆盖 awake 方法。

awake 方法将在 ViewModelWidget 的 createViewModel 方法之后立即调用,并在 ViewModel 的 onInitState 方法之前调用。这将有助于设置必要的数据。

class ExamplePage extends ConsumerViewModelWidget<ExamplePageViewModel> {
  final int initValue;
  const ExamplePage({super.key, required this.initValue});

  [@override](/user/override)
  AutoDisposeProvider<ExamplePageViewModel> viewModelProvider() => exampleViewModelProvider;

  [@override](/user/override)
  void awake(WidgetRef ref, ExamplePageViewModel viewModel) => viewModel.setup(initValue);
  
  [@override](/user/override)
  Widget buildWidget(BuildContext context, WidgetRef ref, ExamplePageViewModel viewModel) {
    return BuildYourPageWidget();
  }
}

4 - 监听来自 ViewModel 的数据更改

要从 ViewModel 中监听数据更改,我们可以使用 RiverPod 提供的组件。由于 RiverPod 是一个状态管理库,我们可以使用默认的 RiverViewModel 中提供的 StateController 来更新 UI。

在以下示例中,ExamplePageViewModel 将增加计数器并通过 uiState 更新 UI。ViewModel 还将暴露 counterSelector 供 Widget 监听并更新。

class ExamplePageViewModel extends RiverViewModel<ExamplePageUIState> {
  ExamplePageViewModel({required super.uiState});

  final counterSelector = _exampleUIStateProvider.select((value) => value.counter);

  void incrementCounter() {
    uiState.update((state) => state.copyWith(counter: state.counter + 1));
  }
}

您可以使用 RiverPod 提供的组件(例如 ConsumerWidget)来更新 UI。

class ExampleCounterText extends ConsumerWidget {
  const ExampleCounterText({super.key});

  [@override](/user/override)
  Widget build(BuildContext context, WidgetRef ref) {
    final viewModel = ref.watch(exampleViewModelProvider);
    final counter = ref.watch(viewModel.counterSelector);
    return Text(
      '$counter',
      style: Theme.of(context).textTheme.headlineMedium,
    );
  }
}

完整示例

class ExamplePage extends ConsumerViewModelWidget<ExamplePageViewModel> {
  const ExamplePage({super.key});

  [@override](/user/override)
  AutoDisposeProvider<ExamplePageViewModel> viewModelProvider() => exampleViewModelProvider;

  [@override](/user/override)
  Widget buildWidget(BuildContext context, WidgetRef ref, ExamplePageViewModel viewModel) {
    final counter = ref.watch(viewModel.counterSelector);
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            // 监听 counterSelector 更新 UI
            Consumer(
              builder: (context, ref, child) {
                final counter = ref.watch(viewModel.counterSelector);
                return Text(
                  '$counter',
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: viewModel.incrementCounter, // 调用 ViewModel 中的 incrementCounter 函数
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // 这个尾随逗号使自动格式化更美观
    );
  }
}

final _exampleUIStateProvider = StateProvider.autoDispose<ExamplePageUIState>((ref) {
  return ExamplePageUIState();
});

final exampleViewModelProvider = Provider.autoDispose<ExamplePageViewModel>((ref) {
  return ExamplePageViewModel(uiState: ref.watch(_exampleUIStateProvider.notifier));
});

class ExamplePageUIState {
  int counter;

  ExamplePageUIState({this.counter = 0});

  ExamplePageUIState copyWith({int? counter}) {
    return ExamplePageUIState(
      counter: counter ?? this.counter,
    );
  }
}

class ExamplePageViewModel extends RiverViewModel<ExamplePageUIState> {
  ExamplePageViewModel({required super.uiState});
  // 暴露一个选择器供 Widget 监听和更新
  final counterSelector = _exampleUIStateProvider.select((value) => value.counter);
  
  void incrementCounter() {
    // 更新 uiState 以反映新的计数值
    uiState.update((state) => state.copyWith(counter: state.counter + 1));
  }
}

更多关于Flutter MVVM架构与状态管理插件maac_mvvm_with_riverpod的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter MVVM架构与状态管理插件maac_mvvm_with_riverpod的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,MVVM(Model-View-ViewModel)是一种常见的架构模式,用于分离UI逻辑和业务逻辑。maac_mvvm_with_riverpod 是一个结合了MVVM架构和状态管理插件 Riverpod 的库,它帮助开发者更轻松地实现MVVM模式。

MVVM 架构概述

  • Model: 负责数据层,通常包括数据获取、存储和处理。
  • View: 负责UI层,展示数据和接收用户输入。
  • ViewModel: 作为View和Model之间的桥梁,处理业务逻辑,并将数据转换为View可以使用的形式。

Riverpod 概述

Riverpod 是Flutter中一个强大的状态管理库,它是Provider的改进版,提供了更好的类型安全性和灵活性。

maac_mvvm_with_riverpod 的使用

1. 添加依赖

首先,在pubspec.yaml中添加maac_mvvm_with_riverpod依赖:

dependencies:
  flutter:
    sdk: flutter
  maac_mvvm_with_riverpod: ^1.0.0

然后运行flutter pub get安装依赖。

2. 创建 Model

Model 通常是一个简单的数据类,负责数据的存储和处理。

class User {
  final String name;
  final int age;

  User(this.name, this.age);
}

3. 创建 ViewModel

ViewModel 继承自 ChangeNotifier 或使用 RiverpodStateNotifier,负责处理业务逻辑。

import 'package:maac_mvvm_with_riverpod/maac_mvvm_with_riverpod.dart';

class UserViewModel extends ChangeNotifier {
  User? _user;

  User? get user => _user;

  void fetchUser() {
    // 模拟数据获取
    _user = User("John Doe", 30);
    notifyListeners();
  }
}

4. 使用 Riverpod 管理状态

使用 Riverpod 来管理 ViewModel 的状态。

import 'package:flutter_riverpod/flutter_riverpod.dart';

final userViewModelProvider = ChangeNotifierProvider<UserViewModel>((ref) {
  return UserViewModel();
});

5. 创建 View

View 中,使用 ConsumerConsumerWidget 来监听 ViewModel 的状态变化。

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

class UserView extends ConsumerWidget {
  [@override](/user/override)
  Widget build(BuildContext context, WidgetRef ref) {
    final userViewModel = ref.watch(userViewModelProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('User Profile'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (userViewModel.user != null)
              Text('Name: ${userViewModel.user!.name}'),
            if (userViewModel.user != null)
              Text('Age: ${userViewModel.user!.age}'),
            ElevatedButton(
              onPressed: () {
                userViewModel.fetchUser();
              },
              child: Text('Fetch User'),
            ),
          ],
        ),
      ),
    );
  }
}

6. 运行应用

main.dart 中运行应用。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'user_view.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MVVM with Riverpod',
      home: UserView(),
    );
  }
}
回到顶部