Flutter MVVM架构与依赖注入插件maac_mvvm_with_get_it的使用

Flutter MVVM架构与依赖注入插件maac_mvvm_with_get_it的使用

MVVM架构图

MVVM

maac_mvvm_with_get_itmaac_mvvm 的一个扩展包,用于与 GetIt 一起使用。该包保留了 maac_mvvm 包中的架构和组件,如 ViewModel、StreamData 和 ViewModelWidget,并添加了支持 GetIt 的额外组件,即 DependencyViewModelWidget。

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

请也查看 maac_mvvm

特性

DependencyViewModelWidget

用于构建绑定到 ViewModel 的 UI 小部件,同时也支持依赖于 ViewModel 的 GetIt。

开始使用

1 - 安装包

flutter pub add maac_mvvm_with_get_it

2 - 设置 GetIt

在使用该包之前,需要在 GetIt 中注册你的依赖项和 ViewModels:

import 'package:get_it/get_it.dart';
import 'package:maac_mvvm_with_get_it/maac_mvvm_with_get_it.dart';

final GetIt sl = GetIt.instance;

void setupGetIt() {
  // 设置你的 GetIt 依赖注入。
}

void registerViewModels() {
  // 注册一个工厂函数来创建 `ExamplePageViewModel` 实例。
  // 当小部件请求此 ViewModel 时,该包将:
  // 1. 调用此工厂函数以创建新实例。
  // 2. 将此实例注入到小部件中,并在小部件销毁时自动注销。
  registerViewModel(() => ExamplePageViewModel()); 
}

void main() {
  setupGetIt();
  registerViewModels();
  runApp(const MyApp());
}

3 - 创建你的 ViewModel

使用该包可以创建一个 ViewModel 来自动处理初始化、恢复、暂停和销毁。这个 ViewModel 将有权访问状态和方法,以及在 maac_mvvm 的 ViewModel 中定义的方法。下面是一个简单的 ViewModel,它持有从小部件增加计数的逻辑:

class ExamplePageViewModel extends ViewModel {
  ExamplePageViewModel();

  late final _uiState = 0.mutableData(this);
  late final uiState = _uiState.streamData;

  void increaseCounter() {
    _uiState.postValue(_uiState.data + 1);
  }
}

4 - 创建你的小部件并绑定 ViewModel

DependencyViewModelWidget 只包含两个方法:createViewModelbuild

  • buildWidget 方法是构建界面的地方。
  • viewModelProvider 方法是提供 ViewModel 提供者的地点。

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

class ExamplePage extends DependencyViewModelWidget<ExamplePageViewModel> {
  const ExamplePage({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context, ExamplePageViewModel viewModel) {
    return BuildYourUiWidget();
  }
}

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

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

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

  [@override](/user/override)
  void awake(WrapperContext wrapperContext, ExamplePageViewModel viewModel) => viewModel.setup(initValue);

  [@override](/user/override)
  Widget build(BuildContext context, ExamplePageViewModel viewModel) {
    return BuildYourUiWidget();;
  }
}

5 - 监听来自 ViewModel 的数据变化

监听来自 ViewModel 的数据变化并使用 StreamDataConsumer 更新 UI。

StreamDataConsumer 是一个监听数据流更改并相应更新其界面的小部件。它可以用于显示 ViewModel 中的数据并在数据更改时更新界面。

要使用 StreamDataConsumer,首先需要在你的 ViewModel 中创建一个数据流。这可以通过使用 StreamDataViewModel 来完成。

一旦创建了数据流,你可以将其作为参数传递给 StreamDataConsumer 小部件。StreamDataConsumer 将监听数据流的更改并相应地更新其界面。

例如,如果你在 ViewModel 中有一个你希望在 UI 中显示的计数器变量,你可以为它创建一个 StreamDataViewModel 并将其传递给 StreamDataConsumer 小部件。每当计数器更改时,StreamDataConsumer 将更新其界面以显示新的值。

Widget _buildCounterDisplay(ExamplePageViewModel viewModel) {
  return Center(
    child: StreamDataConsumer<int>(
      streamData: viewModel.uiState,
      builder: (context, data) {
        return Text("You have pressed the button $data times.");
      },
    ),
  );
}

完整示例

// 这是我们全局的服务定位器
GetIt sl = GetIt.instance;

void setupGetIt() {
  // 设置你的 GetIt 依赖注入。
}

void registerViewModels(){
  registerViewModel(() => ExamplePageViewModel());
}

void main() {
  setupGetIt();
  registerViewModels();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const ExamplePage(),
    );
  }
}

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

  [@override](/user/override)
  Widget build(BuildContext context, ExamplePageViewModel viewModel) {
    return Scaffold(
      appBar: AppBar(title: const Text("Main")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
              textAlign: TextAlign.center,
            ),
            StreamDataConsumer<int>(
              streamData: viewModel.uiState,
              builder: (context, data) {
                return Text(
                  '$data',
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
            ElevatedButton(
              onPressed: () => _navigateTo(context, const ExampleAPage()),
              child: const Text("Move to A"),
            ),
            ElevatedButton(
              onPressed: () => _navigateTo(context, const ExampleBPage()),
              child: const Text("Move to B"),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: sl.get<ExamplePageViewModel>().increaseCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }

  Future<dynamic> _navigateTo(BuildContext context, Widget page) {
    return Navigator.of(context).push(MaterialPageRoute(builder: (_) => page));
  }
}

class ExampleAPage extends DependencyViewModelWidget<ExampleAPageViewModel> {
  const ExampleAPage({super.key});

  [@override](/user/override)
  Widget build(BuildContext context, ExampleAPageViewModel viewModel) {
    return Scaffold(
      appBar: AppBar(title: const Text("A")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            StreamDataConsumer<int>(
              streamData: viewModel.uiState,
              builder: (context, data, _) {
                return Text(
                  "$data",
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
            StreamDataConsumer<String>(
              streamData: viewModel.uiStateMap,
              builder: (context, data, _) {
                return Text(
                  data,
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: sl.get<ExampleAPageViewModel>().increaseCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class ExampleBPage extends DependencyViewModelWidget<ExampleBPageViewModel> {
  const ExampleBPage({super.key});

  [@override](/user/override)
  Widget build(BuildContext context, ExampleBPageViewModel viewModel) {
    return Scaffold(
      appBar: AppBar(title: const Text("B")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            StreamDataConsumer<String>(
              streamData: viewModel.dataApi,
              builder: (context, data, child) {
                return Text(data);
              },
            ),
            const Text(
              'You have pushed the button this many times:',
            ),
            StreamDataConsumer<int>(
              streamData: viewModel.uiState,
              builder: (context, data, child) {
                return Text(
                  '$data',
                  style: Theme.of(context).textTheme.headlineMedium,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: sl.get<ExampleBPageViewModel>().increaseCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class ExamplePageViewModel extends ViewModel {
  late final _uiState = 0.mutableData(this);
  late final uiState = _uiState.streamData;

  void increaseCounter() {
    _uiState.postValue(_uiState.data + 1);
  }

  int returnTestValue() {
    return 1;
  }
}

class ExampleAPageViewModel extends ViewModel {
  late final _uiState = 0.mutableData(this);

  StreamData<int> get uiState => _uiState;

  StreamData<String> get uiStateMap => _uiState.map(
        mapper: (data) => "${data + 3}",
      );

  void increaseCounter() {
    _uiState.postValue(_uiState.data + 1);
  }

}

class ExampleBPageViewModel extends ViewModel {
  final SimpleRepository _repository;

  ExampleBPageViewModel({required SimpleRepository repository})
      : _repository = repository;

  late final _uiState = 0.mutableData(this);

  StreamData<int> get uiState => _uiState.streamData;

  late final _dataApi = "".mutableData(this);

  StreamData<String> get dataApi => _dataApi.streamData;

  [@override](/user/override)
  void onInitState() {
    super.onInitState();
    Future.delayed(Duration.zero, () {
      _repository.fakeFetch().then((data) => _dataApi.postValue(data));
    });
  }

  void increaseCounter() {
    _uiState.postValue(_uiState.data + 1);
  }
}

class SimpleRepository {
  const SimpleRepository();

  Future<String> fakeFetch() async {
    await Future.delayed(const Duration(milliseconds: 200));
    return "Hello there!";
  }
}

更多关于Flutter MVVM架构与依赖注入插件maac_mvvm_with_get_it的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter MVVM架构与依赖注入插件maac_mvvm_with_get_it的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,MVVM(Model-View-ViewModel)架构是一种常见的架构模式,用于分离UI逻辑和业务逻辑。maac_mvvm_with_get_it 是一个结合了MVVM架构和依赖注入(DI)的插件,使用 get_it 作为依赖注入容器。

MVVM 架构概览

MVVM 架构由以下三个主要部分组成:

  1. Model: 负责数据层,处理数据的获取、存储和更新。
  2. View: 负责UI的展示,通常是无状态的,依赖于ViewModel。
  3. ViewModel: 负责业务逻辑,处理用户交互并更新Model,同时将数据暴露给View。

依赖注入与 get_it

get_it 是一个轻量级的依赖注入容器,允许你在应用中注册和解析依赖项。通过依赖注入,你可以轻松地将依赖项(如Service、Repository、ViewModel等)注入到需要它们的类中,而无需手动创建实例。

使用 maac_mvvm_with_get_it

maac_mvvm_with_get_it 是一个结合了MVVM架构和 get_it 的插件,简化了ViewModel的创建和依赖注入的流程。以下是使用它的基本步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 maac_mvvm_with_get_itget_it 的依赖:

dependencies:
  flutter:
    sdk: flutter
  get_it: ^7.2.0
  maac_mvvm_with_get_it: ^1.0.0

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

2. 配置 get_it

在应用的入口文件(通常是 main.dart)中配置 get_it,注册你的依赖项:

import 'package:get_it/get_it.dart';

final getIt = GetIt.instance;

void setupLocator() {
  // 注册你的依赖项
  getIt.registerSingleton<MyService>(MyService());
  getIt.registerFactory<MyViewModel>(() => MyViewModel(getIt<MyService>()));
}

void main() {
  setupLocator();
  runApp(MyApp());
}

3. 创建 ViewModel

创建一个ViewModel类,继承自 BaseViewModel

import 'package:maac_mvvm_with_get_it/maac_mvvm_with_get_it.dart';

class MyViewModel extends BaseViewModel {
  final MyService _service;

  MyViewModel(this._service);

  // 添加你的业务逻辑
  void fetchData() {
    // 使用 _service 获取数据
  }
}

4. 在 View 中使用 ViewModel

在UI组件(如 StatefulWidgetStatelessWidget)中使用 ViewModelProvider 来获取ViewModel:

import 'package:flutter/material.dart';
import 'package:maac_mvvm_with_get_it/maac_mvvm_with_get_it.dart';
import 'package:get_it/get_it.dart';

class MyView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return ViewModelProvider<MyViewModel>(
      create: () => getIt<MyViewModel>(),
      builder: (context, viewModel, child) {
        return Scaffold(
          appBar: AppBar(
            title: Text('MVVM with Get It'),
          ),
          body: Center(
            child: ElevatedButton(
              onPressed: viewModel.fetchData,
              child: Text('Fetch Data'),
            ),
          ),
        );
      },
    );
  }
}
回到顶部