層級之間的溝通
除了為架構的每個元件定義清晰的職責之外,考量元件如何溝通也很重要。這指的是規範溝通的規則,以及元件如何溝通的技術實作。應用程式的架構應回答以下問題
- 哪些元件允許與其他哪些元件溝通(包括相同類型的元件)?
- 這些元件彼此暴露哪些輸出?
- 任何給定層級如何「連接」到另一個層級?
使用此圖表作為指南,互動規則如下
元件 | 互動規則 |
---|---|
視圖 (View) |
|
視圖模型 (ViewModel) |
|
儲存庫 (Repository) |
|
服務 (Service) |
|
依賴注入
#本指南已說明這些不同的元件如何透過使用輸入和輸出彼此溝通。在所有情況下,兩個層級之間的溝通是透過將元件傳遞到建構函式方法(消耗其資料的元件)中來促進的,例如將 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,
),
// In the Compass app, additional service and repository providers live here.
],
),
child: const MainApp(),
);
服務只會公開,以便它們可以立即透過 provider
中的 BuildContext.read
方法注入到儲存庫中,如前面的程式碼片段所示。然後會公開儲存庫,以便它們可以根據需要注入到視圖模型中。
在 Widget 樹中稍微低一點的位置,對應於全螢幕的視圖模型會在 package:go_router
配置中建立,其中再次使用 provider 來注入必要的儲存庫。
// This code was modified for demo purposes.
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 的最新穩定版本。本頁最後更新於 2024-12-04。 檢視原始碼 或 回報問題。