层间通信
除了定义架构中每个组件的明确职责外,还必须考虑组件如何通信。这既指规定通信的规则,也指组件通信的技术实现。应用程序的架构应解答以下问题:
- 哪些组件可以与哪些其他组件通信(包括相同类型的组件)?
- 这些组件彼此公开哪些输出?
- 如何将任何给定层“连接”到另一层?
使用此图作为指南,参与规则如下:
组件 | 参与规则 |
---|---|
视图 |
|
视图模型 |
|
资源库 |
|
服务 |
|
依赖注入
#本指南展示了这些不同组件如何通过使用输入和输出彼此通信。在每种情况下,两层之间的通信都是通过将组件传递到构造函数方法(使用其数据的组件的构造函数方法)来实现的,例如将 Service
传递到 Repository
。
class MyRepository {
MyRepository({required MyService myService})
: _myService = myService;
late final MyService _myService;
}
然而,缺少的一件事是对象创建。在应用程序中,MyService
实例在哪里创建,以便可以将其传递到 MyRepository
?这个问题的答案涉及一种称为依赖注入 的模式。
在 Compass 应用程序中,依赖注入 使用 package:provider
处理。根据他们在构建 Flutter 应用程序方面的经验,Google 的团队建议使用 package:provider
来实现依赖注入。
服务和资源库作为 Provider
对象公开到 Flutter 应用程序的 widget 树的顶层。
runApp(
MultiProvider(
providers: [
Provider(create: (context) => AuthApiClient()),
Provider(create: (context) => ApiClient()),
Provider(create: (context) => SharedPreferencesService()),
ChangeNotifierProvider(
create: (context) => AuthRepositoryRemote(
authApiClient: context.read(),
apiClient: context.read(),
sharedPreferencesService: context.read(),
) as AuthRepository,
),
Provider(create: (context) =>
DestinationRepositoryRemote(
apiClient: context.read(),
) as DestinationRepository,
),
Provider(create: (context) =>
ContinentRepositoryRemote(
apiClient: context.read(),
) as ContinentRepository,
),
// 在 Compass 应用程序中,其他服务和资源库提供程序位于此处。
],
),
child: const MainApp(),
);
服务仅公开,以便它们可以立即通过 provider
中的 BuildContext.read
方法注入到资源库中,如前面的代码片段所示。然后公开资源库,以便根据需要将其注入到视图模型中。
在 widget 树中稍低的位置,对应于全屏的视图模型在 package:go_router
配置中创建,其中再次使用 provider 来注入必要的资源库。
// 此代码已为演示目的修改。
GoRouter router(
AuthRepository authRepository,
) =>
GoRouter(
initialLocation: Routes.home,
debugLogDiagnostics: true,
redirect: _redirect,
refreshListenable: authRepository,
routes: [
GoRoute(
path: Routes.login,
builder: (context, state) {
return LoginScreen(
viewModel: LoginViewModel(
authRepository: context.read(),
),
);
},
),
GoRoute(
path: Routes.home,
builder: (context, state) {
final viewModel = HomeViewModel(
bookingRepository: context.read(),
);
return HomeScreen(viewModel: viewModel);
},
routes: [
// ...
],
),
],
);
在视图模型或资源库中,注入的组件应该是私有的。例如,HomeViewModel
类如下所示:
class HomeViewModel extends ChangeNotifier {
HomeViewModel({
required BookingRepository bookingRepository,
required UserRepository userRepository,
}) : _bookingRepository = bookingRepository,
_userRepository = userRepository;
final BookingRepository _bookingRepository;
final UserRepository _userRepository;
// ...
}
私有方法阻止具有访问视图模型权限的视图直接调用资源库上的方法。
这结束了 Compass 应用程序的代码演练。此页面仅演练了与架构相关的代码,但并没有讲述全部内容。大多数实用程序代码、widget 代码和 UI 样式都被忽略了。浏览Compass 应用程序资源库 中的代码,以获取遵循这些原则构建的健壮 Flutter 应用程序的完整示例。
反馈
#由于本网站的这一部分正在不断发展,我们欢迎您的反馈!
除非另有说明,否则本网站上的文档反映的是 Flutter 的最新稳定版本。页面最后更新于 2025-01-30。 查看源代码 或 报告问题。