Flutter Clean Architecture 实践(1)
Flutter Clean Architecture 实践(1)
问题概述
在 Flutter 中保持代码的清晰与可维护性尤为重要,因为 Flutter 中逻辑代码和 UI 代码都使用 Dart 编写,容易导致业务逻辑和 UI 代码混杂。即便像 BLoC(或其子集 Cubit)这样的状态管理方案,在代码可扩展性方面也可能显得力不从心。那么,如何在 Flutter 中保持代码的可维护性?
解决方案
保持应用程序可维护的秘诀是由“Bob 大叔”提出的 Clean Architecture(清洁架构)。具体实现方式如下:
-
分层设计:
- 将代码分成独立的层,每层依赖于抽象,而不是具体实现。
- 分层示意图中,水平箭头(→)表示依赖流向。例如,实体(Entities)完全独立,仅用例(Use Cases)依赖于实体,依次类推。
-
项目目标:
- 使用 Firebase 作为后端服务。
- 选择 Cubit(BLoC 的子集)作为状态管理工具(当然,也可以选择其他状态管理方案)。
- 清洁架构的核心并不依赖于特定的状态管理工具。
-
Reso Coder 提出的清洁架构模型:
- 项目结构包括三个主要层次:Domain(领域层)、Data(数据层)和 Presentation(展示层)。
项目结构详解
-
Presentation(展示层):
- 负责在屏幕上显示内容,并通过控件触发事件。
- 逻辑处理器(如 BLoC / Cubit)不会包含太多逻辑,而是将所有任务委派给用例层。
- 展示层结构:
- Cubit 类存放在
Cubit
目录下。 - 页面存放在
Pages
目录下。 - 其他小组件存放在
Widgets
目录下。
- Cubit 类存放在
-
Domain(领域层):
- 最核心的部分,包含核心业务逻辑(用例)和业务对象(实体)。
- 该层完全独立于其他层,但其他层会依赖于它。
- 用例类负责封装应用程序特定业务场景的逻辑。
- 存储库(Repository)处于数据层与领域层之间,仅定义契约,具体实现在数据层中完成。
- 领域层结构:包含用例和实体等目录。
-
Data(数据层):
- 包含存储库的实现和数据源(远程数据源与本地数据源)。
- 远程数据源:如 Firebase(Cloud Firestore)、API 或其他数据库。
- 本地数据源:用于缓存数据。
- 存储库决定是返回真实数据还是缓存数据。
- 模型类负责数据转换,例如:JSON ↔ Dart 对象快照 ↔ Dart 对象。
- 数据层结构:包含存储库实现和数据源等目录。
总结
我们已经构建了 Instagram 克隆项目的基础目录结构,接下来会先从 UI 开始,然后再逐步实现其他功能。
参考资料
- 视频演示:https://youtube.com/shorts/zcvIhsGWcRk
- 原文链接:[https://medium.com/@ak187429/instagram-clone-clean-architecture-flutter-part-1-7feed0118ce8](https://medium.com/@ak187429/instagram-clone-clean-architecture-flutter-part-1-7feed0118ce8)
更多关于Flutter Clean Architecture 实践(1)的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter Clean Architecture 实践(1)的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中实现Clean Architecture(干净架构)的实践是一个复杂但非常值得的过程,它能帮助你构建可维护、可扩展和测试性强的应用。以下是一个简化的示例,展示了如何在Flutter项目中应用Clean Architecture的基本原则。由于篇幅限制,这里将重点展示架构的核心部分,包括数据层(Data Layer)、领域层(Domain Layer)和呈现层(Presentation Layer)的分离。
1. 项目结构
首先,我们需要一个合理的项目结构来支持Clean Architecture。以下是一个可能的目录结构:
my_flutter_app/
├── android/
├── ios/
├── lib/
│ ├── data/
│ │ ├── repositories/
│ │ │ └── user_repository.dart
│ │ ├── datasources/
│ │ │ └── user_data_source.dart
│ │ └── models/
│ │ └── user_model.dart
│ ├── domain/
│ │ ├── entities/
│ │ │ └── user.dart
│ │ ├── usecases/
│ │ │ └── get_user_usecase.dart
│ │ └── repositories/
│ │ └── user_repository.dart (Interface)
│ ├── presentation/
│ │ ├── blocs/
│ │ │ └── user_bloc.dart
│ │ ├── pages/
│ │ │ └── user_page.dart
│ │ ├── widgets/
│ │ │ └── user_widget.dart
│ │ └── viewmodels/
│ │ └── user_viewmodel.dart
│ ├── main.dart
└── pubspec.yaml
2. 数据层(Data Layer)
数据层负责数据的获取和持久化。我们定义一个简单的UserDataSource
和一个UserRepository
。
user_data_source.dart
class UserDataSource {
Future<UserModel> fetchUserById(String id) async {
// 模拟数据获取
return UserModel(id: id, name: "John Doe", email: "john.doe@example.com");
}
}
user_repository.dart
class UserRepositoryImpl implements UserRepository {
final UserDataSource dataSource;
UserRepositoryImpl(this.dataSource);
@override
Future<UserModel> getUserById(String id) {
return dataSource.fetchUserById(id);
}
}
3. 领域层(Domain Layer)
领域层定义了业务逻辑和实体。我们定义一个User
实体和一个GetUserUseCase
。
user.dart
class User {
final String id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
}
get_user_usecase.dart
class GetUserUseCase {
final UserRepository userRepository;
GetUserUseCase(this.userRepository);
Future<User> execute(String id) async {
UserModel userModel = await userRepository.getUserById(id);
return User(id: userModel.id, name: userModel.name, email: userModel.email);
}
}
4. 呈现层(Presentation Layer)
呈现层负责UI逻辑和展示。我们使用BLoC模式来管理状态和事件。
user_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'get_user_usecase.dart';
class UserState {
final User? user;
final bool isLoading;
final String? error;
UserState({this.user, this.isLoading = false, this.error});
}
class UserEvent {
final String userId;
UserEvent(this.userId);
}
class UserBloc extends Bloc<UserEvent, UserState> {
final GetUserUseCase getUserUseCase;
UserBloc(this.getUserUseCase) : super(UserState(isLoading: true));
@override
Stream<UserState> mapEventToState(UserEvent event) async* {
try {
User user = await getUserUseCase.execute(event.userId);
yield UserState(user: user, isLoading: false);
} catch (e) {
yield UserState(error: e.toString(), isLoading: false);
}
}
}
user_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'user_bloc.dart';
class UserPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => UserBloc(GetUserUseCase(UserRepositoryImpl(UserDataSource()))),
child: Scaffold(
appBar: AppBar(title: Text('User Info')),
body: BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state.isLoading) {
return Center(child: CircularProgressIndicator());
} else if (state.error != null) {
return Center(child: Text('Error: ${state.error!}'));
} else {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Name: ${state.user?.name}'),
Text('Email: ${state.user?.email}'),
],
),
);
}
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
BlocProvider.of<UserBloc>(context).add(UserEvent('1'));
},
tooltip: 'Fetch User',
child: Icon(Icons.person),
),
),
);
}
}
以上代码展示了如何在Flutter中实现Clean Architecture的基本框架。通过分离关注点,我们提高了代码的可维护性和可扩展性。当然,这只是一个简单的示例,实际应用中可能需要更复杂的逻辑和更多的组件。