发布于 2026-01-06 5 阅读
0

在 Flutter 应用中使用堆叠式架构

在 Flutter 应用中使用堆叠式架构

架构的概念是当今编程领域最多元化的主题之一。人们开发了许多解决方案,旨在解决各种各样的缺陷。应用架构是少数几个需要个人选择的领域之一。如果某种架构能够高效地满足你的需求,并帮助你发布高质量的代码,那么就选择它。

本文旨在向您介绍堆叠式架构,它为您的下一个 Flutter 应用架构提供了简洁高效的解决方案。从依赖注入到开箱即用的服务,再到遵循整洁架构原则的分层结构等等,应有尽有。要开始使用 Flutter 进行开发,请查看Flutter 团队的这篇文章。

Stacked ArchitectureFilledStacks的 Dane Mackier 开发,是一种 MVVM 架构解决方案,它提供了许多组件,用于构建高度可扩展、可测试、可维护和易用的 Flutter 应用程序。

  • 国家管理,
  • 依赖注入(依赖倒置)
  • 导航抽象
  • 服务(开箱即用)

还有更多优势。所有这些都是堆叠式架构所提供的。

国家管理

在 Flutter 应用中使用 Stacked 来管理状态简直完美。它提供了各种组件和构造函数,用于控制应用中状态的传递和管理方式。让我们通过一个示例应用来深入了解一下。

首先,需要创建一个新的 Flutter 项目。

     flutter create intro_to_stacked
Enter fullscreen mode Exit fullscreen mode

此命令会生成所有文件和文件夹,为 Flutter 应用奠定基础。
接下来,需要在 pubspec.yaml 文件中添加stacked作为依赖项。

    stacked: ^2.2.7
Enter fullscreen mode Exit fullscreen mode

接下来,我们需要为示例应用程序设置基本文件夹结构。进入 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();
       },
      );
     }
    }
Enter fullscreen mode Exit fullscreen mode

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;

    }
Enter fullscreen mode Exit fullscreen mode

回到视图,为了在我们的构建器中使用此状态,我们使用构建器提供的模型,将 ViewModel 中的状态连接到使用它的 UI 屏幕。

    Scaffold(
         body: Center(
          child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
           children: [
            const Text('Stacked Introduction'),
            Text(model.myDeclaration),
           ],
          ),
         ),
        );
Enter fullscreen mode Exit fullscreen mode

在应用程序状态更新时,我们编写一个函数来更新文本,并在更新后调用通知监听器。我们将此函数链接到文本按钮的 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();
     }
Enter fullscreen mode Exit fullscreen mode

notifyListeners 调用会通知视图重建,因为 ViewModel 中的状态发生了变化。

如需深入了解所提供的状态管理解决方案,请查看FilledStacks 团队提供的这篇文章。

导航和依赖注入呢?

Stacked 提供了一种开箱即用的导航方式,无需上下文。借助它提供的 NavigationService,我们可以在 ViewModel 中轻松声明导航,并在视图中使用它们。此 NavigationService 可实现简洁的 UI,避免逻辑或复杂路由代码的干扰。借助构建运行器和 Stacked 生成器,我们可以自动生成路由并流畅地执行导航。

让我们开始吧。

首先,在 pubspec.yaml 文件的 dev_dependencies 部分添加build_runnerstacked_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
Enter fullscreen mode Exit fullscreen mode

运行该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;
    }
Enter fullscreen mode Exit fullscreen mode

在 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),
           ],
          ),
         ),
        );
       },
      );
     }
    }
Enter fullscreen mode Exit fullscreen mode

接下来,在 lib 目录下创建一个名为 app 的文件夹。在该文件夹内,创建一个名为 app.dart 的新文件。该文件将包含所有路由所需的代码以及所有依赖项。

创建一个名为 AppSetup 的类,并使用 StackedApp 注解对其进行标记;该类仅用于存放注解,除此之外不做其他任何操作。该类的主要功能是注解,因为它接收两个参数:路由和依赖项。

    @StackedApp()
    class AppSetup {
     /** This class has no puporse besides housing the annotation that generates the required functionality **/
    }
Enter fullscreen mode Exit fullscreen mode

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 {}
Enter fullscreen mode Exit fullscreen mode

由于 HomeView 是我们接触到的第一个页面,我们将初始参数设置为 true。

设置完成后,运行 flutter 命令自动生成路由所需的文件。

    flutter pub run build_runner build --delete-conflicting-outputs
Enter fullscreen mode Exit fullscreen mode

该命令将生成导航和依赖注入设置所需的文件。

该过程的最后一步是将应用程序的主要顶级函数转换为 Future,并等待 app.locator.dart 文件中由先前使用的命令生成的 setupLocator 函数。

    Future main() async {
     WidgetsFlutterBinding.ensureInitialized();
     await setupLocator();
     runApp(MyApp());
    }
Enter fullscreen mode Exit fullscreen mode

我们该如何使用它?

为了在 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);
     }
    }
Enter fullscreen mode Exit fullscreen mode

接下来,需要在视图中创建一个按钮,并将该函数传递给它的 onPressed 参数。

     TextButton(
       onPressed: () => model.navigateToProfileView(),
       child: const Text('Go To Profile View'),
     )
Enter fullscreen mode Exit fullscreen mode

保存文件并运行应用程序;您将看到该按钮,单击该按钮,即可看到我们使用 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