在 Flutter 应用中使用堆叠式架构
架构的概念是当今编程领域最多元化的主题之一。人们开发了许多解决方案,旨在解决各种各样的缺陷。应用架构是少数几个需要个人选择的领域之一。如果某种架构能够高效地满足你的需求,并帮助你发布高质量的代码,那么就选择它。
本文旨在向您介绍堆叠式架构,它为您的下一个 Flutter 应用架构提供了简洁高效的解决方案。从依赖注入到开箱即用的服务,再到遵循整洁架构原则的分层结构等等,应有尽有。要开始使用 Flutter 进行开发,请查看Flutter 团队的这篇文章。
Stacked Architecture由FilledStacks的 Dane Mackier 开发,是一种 MVVM 架构解决方案,它提供了许多组件,用于构建高度可扩展、可测试、可维护和易用的 Flutter 应用程序。
- 国家管理,
- 依赖注入(依赖倒置)
- 导航抽象
- 服务(开箱即用)
还有更多优势。所有这些都是堆叠式架构所提供的。
国家管理
在 Flutter 应用中使用 Stacked 来管理状态简直完美。它提供了各种组件和构造函数,用于控制应用中状态的传递和管理方式。让我们通过一个示例应用来深入了解一下。
首先,需要创建一个新的 Flutter 项目。
flutter create intro_to_stacked
此命令会生成所有文件和文件夹,为 Flutter 应用奠定基础。
接下来,需要在 pubspec.yaml 文件中添加stacked作为依赖项。
stacked: ^2.2.7
接下来,我们需要为示例应用程序设置基本文件夹结构。进入 lib 文件夹,创建一个名为 home 的新文件夹。在该文件夹中,创建两个文件,分别命名为 home_view.dart 和 home_viewmodel.dart。homeViewModel 文件将负责管理向用户显示的 homeView 的状态。我们严格遵循“整洁架构”原则,即视图不应包含任何逻辑;所有逻辑都位于 ViewModel 中。
视图通过 Stacked 提供的 ViewModelBuilder 小部件绑定到 ViewModel。此小部件接受两个参数,分别是:
- 构建器,用于构建用户界面,其状态位于 ViewModel 中。
- viewModelBuilder 函数会返回此小部件的 ViewModel。
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:intro_to_stacked/ui/views/home/home_viewmodel.dart';
class HomeView extends StatelessWidget {
const HomeView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<HomeViewModel>.reactive(
viewModelBuilder:()=> HomeViewModel(),
builder: (context, model, child) {
return Scaffold();
},
);
}
}
ViewModelBuilder 提供了两个构造函数,分别用于管理状态并更新应用程序的 UI。它们分别是 `.reactive()` 和 `.nonReactive()` 构造函数。`.reactive()` 构造函数会在每次 ViewModel 中调用 `notifyListeners` 时重建 UI。而 `.nonReactive()` 构造函数只会重建一次 UI,之后在 ViewModel 中不会再次重建。
至此,我们已经设置好了视图文件。接下来,我们进入 ViewModel 部分,创建一个继承自 Stacked 提供的 BaseViewModel 类的类。BaseViewModel 类提供了用于维护应用程序状态的功能。在 ViewModel 中,我们有一个字符串,内容为“Stacked is cool”(绝对正确)。这个字符串代表了 ViewModel 当前提供的状态,需要显示在屏幕上。
import 'package:stacked/stacked.dart';
class HomeViewModel extends BaseViewModel {
final String _declaration = 'Stacked is soo cool';
String get myDeclaration => _declaration;
}
回到视图,为了在我们的构建器中使用此状态,我们使用构建器提供的模型,将 ViewModel 中的状态连接到使用它的 UI 屏幕。
Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Stacked Introduction'),
Text(model.myDeclaration),
],
),
),
);
在应用程序状态更新时,我们编写一个函数来更新文本,并在更新后调用通知监听器。我们将此函数链接到文本按钮的 onPressed 事件。
//In View(home_view.dart)
TextButton(
onPressed: () => model.updateDeclaration(),
child: const Text('Update Text'),
)
//In ViewModel (home_viewmodel.dart)
void updateDeclaration() {
_declaration = 'I say Stacked is sooo cool';
notifyListeners();
}
notifyListeners 调用会通知视图重建,因为 ViewModel 中的状态发生了变化。
如需深入了解所提供的状态管理解决方案,请查看FilledStacks 团队提供的这篇文章。
导航和依赖注入呢?
Stacked 提供了一种开箱即用的导航方式,无需上下文。借助它提供的 NavigationService,我们可以在 ViewModel 中轻松声明导航,并在视图中使用它们。此 NavigationService 可实现简洁的 UI,避免逻辑或复杂路由代码的干扰。借助构建运行器和 Stacked 生成器,我们可以自动生成路由并流畅地执行导航。
让我们开始吧。
首先,在 pubspec.yaml 文件的 dev_dependencies 部分添加build_runner和stacked_generator 。此外,还要在 dependency 部分添加stacked_services 。
dependencies:
stacked: ^2.2.7
stacked_services: ^0.8.15
dev_dependencies:
build_runner: ^2.1.4
stacked_generator: ^0.5.5
运行该flutter pub get命令将文件获取到本地以供使用。
接下来,我们需要创建路由指向的新视图。在 views 目录下创建一个名为 profile 的文件夹。在该文件夹内,创建两个文件:profile_view.dart 和 profile_viewmodel.dart。
在 profile_viewmodel.dart 文件中,创建一个名为 ProfileViewModel 的类,该类继承自 BaseViewModel。在 ViewModel 中,我们声明一个字符串,应用程序将在视图中显示该字符串。
import 'package:stacked/stacked.dart';
class ProfileViewModel extends BaseViewModel {
String _pageName = 'This is the Profile Page';
String get pageName => _pageName;
}
在 profile_view.dart 文件中,创建一个返回 ViewModelBuilder 的无状态控件,并将其绑定到 ProfileViewModel。此视图的设置与 HomeView 类似,用于显示个人资料视图文本。
import 'package:flutter/material.dart';
import 'package:intro_to_stacked/ui/views/profile/profile_viewmodel.dart';
import 'package:stacked/stacked.dart';
class ProfileView extends StatelessWidget {
const ProfileView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<ProfileViewModel>.reactive(
viewModelBuilder: () => ProfileViewModel(),
builder: (context, viewModel, child) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(viewModel.pageName),
],
),
),
);
},
);
}
}
接下来,在 lib 目录下创建一个名为 app 的文件夹。在该文件夹内,创建一个名为 app.dart 的新文件。该文件将包含所有路由所需的代码以及所有依赖项。
创建一个名为 AppSetup 的类,并使用 StackedApp 注解对其进行标记;该类仅用于存放注解,除此之外不做其他任何操作。该类的主要功能是注解,因为它接收两个参数:路由和依赖项。
@StackedApp()
class AppSetup {
/** This class has no puporse besides housing the annotation that generates the required functionality **/
}
Stacked 提供三种不同的路线类型,这些路线决定了过渡模式。
- 材料路线
- 库比蒂诺路线
- 自定义路线
使用 MaterialRoute,我们可以注册应用程序中将使用 NavigationService 的视图。
除了路由之外,我们还需要声明应用中使用的依赖项,以便 Stacked 能够创建定位器文件来处理已声明类型的注入。我们会将 NavigationService 注册为 LazySingleton,这意味着它只有在应用中首次使用时才会初始化。
import '../ui/views/home/home_view.dart';
import '../ui/views/profile/profile_view.dart';
import 'package:stacked/stacked_annotations.dart';
@StackedApp(
routes: [
MaterialRoute(page: HomeView, initial: true),
MaterialRoute(page: ProfileView)
],
dependencies: [
LazySingleton(classType: NavigationService),
],
)
class AppSetUp {}
由于 HomeView 是我们接触到的第一个页面,我们将初始参数设置为 true。
设置完成后,运行 flutter 命令自动生成路由所需的文件。
flutter pub run build_runner build --delete-conflicting-outputs
该命令将生成导航和依赖注入设置所需的文件。
该过程的最后一步是将应用程序的主要顶级函数转换为 Future,并等待 app.locator.dart 文件中由先前使用的命令生成的 setupLocator 函数。
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await setupLocator();
runApp(MyApp());
}
我们该如何使用它?
为了在 HomeView 中使用导航功能,我们在 HomeViewModel 中创建一个执行导航的函数。首先,我们声明定位器,以便访问之前注册的导航服务。
import 'package:intro_to_stacked/app/app.router.dart';
import '../../../app/app.locator.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
class HomeViewModel extends BaseViewModel {
String _declaration = 'Stacked is soo cool';
String get myDeclaration => _declaration;
final _navigationService = locator<NavigationService>();
void navigateToProfileView() {
_navigationService.navigateTo(Routes.profileView);
}
}
接下来,需要在视图中创建一个按钮,并将该函数传递给它的 onPressed 参数。
TextButton(
onPressed: () => model.navigateToProfileView(),
child: const Text('Go To Profile View'),
)
保存文件并运行应用程序;您将看到该按钮,单击该按钮,即可看到我们使用 NavigationService Stacked 实现的无缝过渡效果。
服务
Stacked 提供多种可在应用内设置和使用的服务。这些服务包括:
- 导航服务
- 底纸服务
- 对话框服务
- 小吃店服务
这些服务可以根据不同的使用场景进行设置和定制,与 NavigationService 类似,它们可以在应用程序中即时注册和使用。更多信息,请查看pub.dev 上的软件包以及FilledStacks上的文章。
结论
Stacked Architecture 是一个功能强大的工具包,可随时启用并用于快速构建简洁、可扩展的应用程序。本文解释了 Stacked Architecture 的一些概念和组件,并创建了一个使用这些组件的示例应用程序。如需了解构成 Stacked Architecture 的各种特性,请访问FilledStack 。
您可以在这里找到示例应用程序的代码。如有任何疑问,请随时通过 Twitter( @Blazebrain)或 LinkedIn(@Blazebrain)联系我。
干杯!
文章来源:https://dev.to/blazebrain/using-stacked-architecture-in-flutter-app-2ecg