Flutter Clean Architecture Sample App – Dasher
该项目是 Flutter 应用程序的起点。Dasher App 将向您介绍干净的架构结构以及内层/外层如何连接。
架构结构
Dasher 应用使用 手册 中描述的架构结构。
表现层
此层没有业务逻辑,仅用于显示 UI 和处理事件。在 手册 中了解有关表示层的更多信息。
Widgets (UI)
- 通知 Presenter 事件,例如屏幕显示和用户触摸事件。
- 观察 Presenter 状态,并在状态更改时重建。
Presenter
- 包含表示逻辑,通常控制视图状态。
领域层
此层负责业务逻辑。
Interactor
- Interactor 的主要工作是组合不同的存储库并处理业务逻辑。
Data Holder
- Singleton 类,将数据保存在内存中,它不调用存储库或其他外层。
Outer layer
Repository
- 它使用 dio、hive、add2calendar、其他插件等具体实现,并将其从应用程序的其余部分抽象出来。
- Repository 应该在接口之后。
- 接口属于域,实现属于外层。
Source remote
- 表示与远程源(Web、HTTP 客户端、套接字)的通信。
Source local
- 表示与本地源(数据库、shared_prefs)的通信。
设备
- 代表与设备硬件(例如传感器)或软件(日历、权限)的通信。
Folder structure
您将在项目中的 /lib 下找到的顶级文件夹结构
- app 包含 app run_app,并进行各种设置,例如 flutter.onError 崩溃处理和依赖项初始化。
- common 包含所有层通用的代码,并且所有层都可以访问。
- device 是一个外层,代表与设备硬件(例如传感器)或软件(日历、权限)的通信。
- domain 是内层,通常包含 interactors、data holders。此层应仅包含业务逻辑,并且不应了解 UI、Web 等的特定内容或其他层。
- source_local 是一个外层,代表与本地源(数据库、shared_prefs)的通信。
- source_remote 是一个外层,代表与远程源(Web、HTTP 客户端、套接字)的通信。
- ui 是我们按功能打包小部件和 presenters 的层。Presenters 包含表示逻辑,它们访问 domain,并通过 Provider/Riverpod 包在视图树中提供。
- main_production.dart 和 main_staging.dart 两个版本的 main 文件,每个版本都有自己的 flavor。在实践中,这通常意味着有两个版本。在此处 查找有关 flavor 的更多信息。
Riverpod and GetIt
此架构结构使用 Riverpod 用于表示层,并使用 GetIt 用于域和外层(source remote、source local 和 device)。
在 手册 中了解有关如何使用 riverpod 的更多信息。
架构流程示例
在此示例中,我们将展示在 Dashboard 屏幕上获取新 Tweets 的架构流程。
表现层
Widget
Dashboard 屏幕上的一个小部件是 DasherTweetsList。在 Tweets 列表小部件中,创建了一个引用来监视 feedRequestPresenter。
final _presenter = ref.watch(feedRequestPresenter);
Presenter
对于 FeedRequestPresenter,我们使用 RequestProvider,您可以在此处 找到有关它的更多信息。
在 FeedRequestPresenter 中,我们创建了一个 FetchFeedInteractor 接口的实例。
final feedRequestPresenter = ChangeNotifierProvider.autoDispose<FeedRequestPresenter>(
(ref) => FeedRequestPresenter(GetIt.instance.get()),
);
class FeedRequestPresenter extends RequestProvider<List<Tweet>> {
FeedRequestPresenter(this._feedTimelineInteractor) {
fetchTweetsTimeline();
}
final FetchFeedInteractor _feedTimelineInteractor;
Future<void> fetchTweetsTimeline() {
return executeRequest(requestBuilder: _feedTimelineInteractor.fetchFeedTimeline);
}
}
从这里开始,我们逐渐过渡到 Domain 层。
领域层
Interactor
Domain 是业务逻辑层,我们有一个 FetchFeedInteractor 的实现,名为 FetchFeedInteractorImpl。我们的任务是创建一个负责处理获取用户时间线 Tweets 的外部逻辑的 Repository 实例。FeedRepository 也位于接口之后。
class FetchFeedInteractorImpl implements FetchFeedInteractor {
FetchFeedInteractorImpl(this._feedRepository);
final FeedRepository _feedRepository;
@override
Future<List<Tweet>> fetchFeedTimeline() {
return _feedRepository.fetchFeedTimeline();
}
}
Repository
FeedRepositoryImpl 是 Source remote 层的一部分。此存储库使用 twitter_api_v2 包从 Twitter API 获取数据。
Future<List<Tweet>> fetchFeedTimeline() async {
final response = await twitterApi.tweetsService.lookupHomeTimeline(
userId: userDataHolder.user!.id,
tweetFields: [
TweetField.publicMetrics,
TweetField.createdAt,
],
userFields: [
UserField.createdAt,
UserField.profileImageUrl,
],
expansions: [
TweetExpansion.authorId,
],
);
return _getTweetsListWithAuthors(response);
}
在成功响应后,数据会通过他的状态传递回 FeedRequestPresenter,然后触发状态监听器。在 DasherTweetsList 的 build 方法中,我们使用 FeedRequestPresenter 的状态监听器,以便可以轻松地显示/隐藏小部件,具体取决于发出的事件。
_presenter.state.maybeWhen(
success: (feed) => _TweetsList(
feed: feed,
),
initial: () => const CircularProgressIndicator(),
loading: (feed) {
if (feed == null) {
return const CircularProgressIndicator();
} else {
return _TweetsList(
feed: feed,
);
}
},
failure: (e) => Text('Error occurred $e'),
orElse: () => const CircularProgressIndicator(),
),
屏幕截图
| 登录 | Feed |
|---|---|
![]() |
![]() |
| 个人资料 | New Tweet |
|---|---|
![]() |
![]() |
Infinum 架构 Mason brick
在项目中设置我们架构的最简单方法是使用 Mason bricks。infinum_architecture brick 发布在 https://brickhub.dev/bricks/infinum_architecture/ 上,它将生成所有必需的目录和文件,以便开始项目。
如何使用
Tools to install
确保您已安装 FVM – Flutter Version Management。
dart pub global activate fvm
还要安装 Mason CLI,它是使用 Mason bricks 的必备工具。
dart pub global activate mason_cli
Create new project
创建新的 Flutter 项目
flutter create {project_name}
进入项目文件夹
cd {project_name}
Mason brick setup
初始化 mason
mason init
将 mason brick 添加到您的项目
mason add infinum_architecture
开始生成 Infinum 架构文件夹结构
mason make infinum_architecture --on-conflict overwrite
变量
| Variable | 描述 | 默认值 | 类型 |
|---|---|---|---|
project_name |
此名称用于命名主函数和文件 run{project_name}App() |
示例 | string |
flutter_version |
定义您要安装的 FVM 版本 | stable | string |
brick_look |
可选外观 | 真 | 布尔值 |
brick_request_provider |
可选请求提供程序 | 真 | 布尔值 |
Outputs
? lib
┣ ? app
┃ ┣ ? di
┃ ┃ ┗ ? inject_dependencies.dart
┃ ┣ ? example_app.dart
┃ ┗ ? run_example_app.dart
┣ ? common
┃ ┣ ? error_handling
┃ ┃ ┣ ? base
┃ ┃ ┃ ┣ ? expected_exception.dart
┃ ┃ ┃ ┗ ? localized_exception.dart
┃ ┃ ┗ ? error_formatter.dart
┃ ┣ ? flavor
┃ ┃ ┣ ? app_build_mode.dart
┃ ┃ ┣ ? flavor.dart
┃ ┃ ┣ ? flavor_config.dart
┃ ┃ ┗ ? flavor_values.dart
┃ ┗ ? logger
┃ ┣ ? custom_loggers.dart
┃ ┗ ? firebase_log_printer.dart
┣ ? device
┃ ┗ ? di
┃ ┗ ? inject_dependencies.dart
┣ ? domain
┃ ┗ ? di
┃ ┗ ? inject_dependencies.dart
┣ ? source_local
┃ ┗ ? di
┃ ┗ ? inject_dependencies.dart
┣ ? source_remote
┃ ┗ ? di
┃ ┗ ? inject_dependencies.dart
┣ ? ui
┃ ┣ ? common
┃ ┃ ┣ ? generic
┃ ┃ ┃ ┗ ? generic_error.dart
┃ ┗ ? home
┃ ┗ ? home_screen.dart
┣ ? main_production.dart
┗ ? main_staging.dart











