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

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

摘要

本篇文档将详细介绍如何在Flutter项目中使用MVVM架构及依赖注入插件my_own_mvvm_with_dependency_injection_blackjack_and_hookers。通过一个简单的认证示例,我们将展示如何分离关注点(SoC),如何使用ViewModel进行业务逻辑协调,以及如何通过依赖注入管理依赖关系。

为什么分离关注点很重要

分离关注点(SoC)是一种基本的设计原则,它将复杂的软件开发过程转化为易于管理和扩展的过程。通过将应用程序分解为不同的、专注的模块,我们可以实现:

  • 模块化:每个组件都有单一、明确的责任。
  • 可维护性:一个模块的变化对其他模块的影响最小。
  • 可测试性:可以单独测试各个组件。
  • 可扩展性:添加新功能时摩擦最小。

ViewModel:业务逻辑协调器

ViewModel作为一个纯粹的业务逻辑协调器,负责以下工作:

  • 将数据转换为UI呈现的形式。
  • 管理UI状态。
  • 将I/O操作委托给专门的提供者。
  • 仅通过接口通信。

关键原则

  • 所有依赖项都是其自身范围内的单例。
  • 可以有多个范围,当widget树被销毁时,这些范围及其所属的所有依赖项也会被销毁(这就是为什么你的依赖项可以实现IDisposable)。
  • 不直接执行I/O操作。
  • 依赖于抽象,而不是具体实现。
  • 维持业务逻辑和数据源之间的清晰分离。

ViewModel包含什么?

  • 你应该能够在不使用任何Flutter类、小部件或上下文的情况下创建和测试ViewModel。
  • 如果它有一个BuildContext,一些Flutter类,如Material、Widget、Cupertino等,则它应该属于视图(View)。
  • 如果你有一个可以由多个ViewModel共享的业务逻辑类(例如signOut),我们称这些类为Service。在这种情况下,服务必须具有const构造函数(因此它们不能有任何状态)。ViewModel总是非const的(因为它们继承了ChangeNotifier)。

依赖注入:解耦你的应用

依赖项是你应用程序需要的外部服务:

  • 认证服务
  • 数据库连接
  • 文件系统访问
  • 网络API
  • 平台特定插件

我们的依赖注入框架允许你:

  • 在运行时注入依赖项。
  • 轻松地交换实现。
  • 创建模块化且可测试的代码。

简单认证示例

// 提供者就像操作系统驱动程序:它们实现了某些功能,比如API访问、认证(通过FirebaseAuth)、存储等。
// 任何实现`IInitializable`的ViewModel或依赖项将在依赖项构建时初始化。
// ViewModel只拥有一个合同,即与这些提供者的通信接口:
abstract interface class IAuthenticationProvider
  implements IInitializable {
  Future<bool> authenticate(String username, String password);
}

// ViewModel是使用提供者的业务逻辑。它是为一个视图而设计的。
// 如果你有希望在多个ViewModel之间共享的公共逻辑,比如`signOut`,你可以创建一个类来负责持有业务逻辑并将其绑定到ViewModel。
final class AuthViewModel extends ChangeNotifier {
  AuthViewModel(this._authProvider);

  final AuthenticationProvider _authProvider;

  bool _isAuthenticated = false;
  bool get isAuthenticated => _isAuthenticated;

  Future<void> login(String username, String password) async {
    _isAuthenticated = await _authProvider.authenticate(username, password);
    notifyListeners();
  }
}

// 一个创建和管理ViewModel的小部件。它包含了一些不错的方法,如`initState`或`dispose`。
final class AuthenticationView extends ViewWidget<AuthViewModel> {
  const AuthenticationView({super.key});

  [@override](/user/override)
  AuthViewModel viewModelFactory(Dependencies scope) {
    return AuthViewModel(scope.get<IAuthenticationProvider>());
  }

  [@override](/user/override)
  Widget build(BuildContext context, AuthViewModel viewModel) {
    return Text(
      viewModel.isAuthenticated 
        ? "已认证" 
        : "未认证"
    );
  }
}

// 你的根小部件应该是Dependencies小部件:

runApp(
  // 从这里开始构建依赖注入作用域,并初始化所有`IInitializable`依赖项。
  //
  // 依赖项的顺序并不重要,因为包会自动按依赖关系排序。
  DependenciesBuilder(
    dependencies: [
      // 每当有人需要一个`IAuthenticationProvider`时,都会提供一个`FirebaseAuthenticationProvider`。
      Dependency<IAuthenticationProvider>(
        (scope) => FirebaseAuthenticationProvider(),
      ),
      Dependency<ISomeOtherProvider>(
        (scope) => SomeOtherProvider(
          authenticationProvider:
            // 这就是如何从作用域中获取依赖项。
            scope.get<IAuthenticationProvider>(),
        ),
        // 当一个依赖项依赖于另一个时,这很重要,以确保依赖项的正确实例化和初始化顺序。
        dependsOn: [IAuthenticationProvider],
      ),
    ],
    builder: (context, scope) => const MainApp(),
  );
);

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

1 回复

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


Flutter 中的 MVVM(Model-View-ViewModel)架构是一种常用的设计模式,用于分离业务逻辑与 UI 层。依赖注入(Dependency Injection, DI)则是一种实现松耦合的设计模式,通过将依赖项注入到类中,而不是在类内部创建它们。

你提到的 my_own_mvvm_with_dependency_injection_blackjack_and_hookers 似乎是一个自定义的插件,虽然我无法直接找到关于这个插件的具体文档,但我可以为你提供如何使用 MVVM 架构和依赖注入的一般指导,并结合 Flutter 中的常见依赖注入库(如 get_itprovider)进行说明。

1. MVVM 架构概述

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

  • Model: 负责数据管理和业务逻辑。
  • View: 负责 UI 展示和用户交互。
  • ViewModel: 负责将 Model 的数据转换为 View 可以展示的格式,并处理用户交互逻辑。

2. 依赖注入 (DI) 概述

依赖注入是一种设计模式,它允许你将对象的依赖关系从类内部移动到外部,从而实现松耦合。常见的依赖注入库包括 get_itprovider

3. 使用 get_it 进行依赖注入

get_it 是一个轻量级的依赖注入库,允许你全局注册和获取依赖项。

安装 get_it

pubspec.yaml 中添加依赖:

dependencies:
  get_it: ^7.2.0

注册依赖项

main.dart 中注册依赖项:

import 'package:get_it/get_it.dart';

final getIt = GetIt.instance;

void setupLocator() {
  getIt.registerSingleton<MyViewModel>(MyViewModel());
}

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

使用依赖项

View 中使用 get_it 获取 ViewModel

class MyView extends StatelessWidget {
  final MyViewModel _viewModel = getIt<MyViewModel>();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('MVVM with DI'),
      ),
      body: Center(
        child: Text(_viewModel.someData),
      ),
    );
  }
}

4. 结合 MVVM 架构

Model

class MyModel {
  String getData() {
    return "Hello from Model!";
  }
}

ViewModel

class MyViewModel {
  final MyModel _model = MyModel();

  String get someData => _model.getData();
}

View

class MyView extends StatelessWidget {
  final MyViewModel _viewModel = getIt<MyViewModel>();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('MVVM with DI'),
      ),
      body: Center(
        child: Text(_viewModel.someData),
      ),
    );
  }
}

5. 使用 provider 进行依赖注入

provider 是另一个流行的状态管理库,它也可以用于依赖注入。

安装 provider

pubspec.yaml 中添加依赖:

dependencies:
  provider: ^6.0.0

注册依赖项

main.dart 中注册依赖项:

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

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => MyViewModel()),
      ],
      child: MyApp(),
    ),
  );
}

使用依赖项

View 中使用 provider 获取 ViewModel

class MyView extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final MyViewModel _viewModel = Provider.of<MyViewModel>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('MVVM with DI'),
      ),
      body: Center(
        child: Text(_viewModel.someData),
      ),
    );
  }
}
回到顶部