Flutter架构设计终极指南:从bizz84/flutter-tips-and-tricks提炼的7条黄金法则与实战方案
你是否曾陷入Flutter应用架构的选择困境?在MVC、MVVM、Clean Architecture和Bloc之间摇摆不定?是否在项目扩张后遭遇状态管理混乱、测试困难和团队协作障碍?本文将从bizz84/flutter-tips-and-tricks项目中提炼7条架构设计黄金法则,结合Riverpod生态系统和Domain-Driven Design思想,帮你构建可扩展、可测试、易维护的Flutter应用架构。
读完本文你将掌握:
- 7条经过实战验证的架构设计原则
- 5种主流Flutter架构的对比与选型指南
- Riverpod状态管理的最佳实践与Provider选型
- 功能优先vs层优先的项目结构决策框架
- 从单元测试到集成测试的全链路测试策略
一、架构设计的7条黄金法则
1.1 严格分离关注点:业务逻辑零依赖UI
核心原则:业务逻辑组件(Bloc/ViewModel/Notifier)绝对不能包含BuildContext或任何Flutter框架代码。
// 错误示例:业务逻辑依赖BuildContext
class UserNotifier extends StateNotifier<User> {
final BuildContext context; // ❌ 禁止在Notifier中存储上下文
UserNotifier(this.context) : super(User.empty()) {
_loadUser();
}
Future<void> _loadUser() async {
try {
// ...加载逻辑
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(...); // ❌ 直接操作UI
}
}
}
// 正确示例:通过状态传递而非直接操作
class UserNotifier extends StateNotifier<AsyncValue<User>> {
UserNotifier() : super(const AsyncValue.loading()) {
_loadUser();
}
Future<void> _loadUser() async {
state = await AsyncValue.guard(() => userRepository.getUser());
}
}
// UI层处理错误展示
class UserProfile extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final userState = ref.watch(userNotifierProvider);
return userState.when(
loading: () => CircularProgressIndicator(),
error: (error, stack) => ErrorWidget(
message: error.toString(),
onRetry: () => ref.refresh(userNotifierProvider),
),
data: (user) => UserDetails(user: user),
);
}
}
实施策略:创建独立的业务逻辑层,通过AsyncValue封装加载/错误/数据状态,UI层仅负责状态消费和用户交互触发。
1.2 依赖注入取代单例:提升可测试性与灵活性
核心原则:使用依赖注入(DI)管理服务和存储库,避免全局单例。
// 错误示例:使用单例
class AuthService {
static final AuthService instance = AuthService._(); // ❌ 全局单例
AuthService._();
Future<void> login(String email, String password) async {
// ...登录逻辑
}
}
// 正确示例:通过Provider注入
final authServiceProvider = Provider<AuthService>((ref) {
final apiClient = ref.watch(apiClientProvider);
return AuthService(apiClient); // ✅ 依赖通过构造函数注入
});
final authNotifierProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
final authService = ref.watch(authServiceProvider); // ✅ 通过Provider获取依赖
return AuthNotifier(authService);
});
实施策略:使用Riverpod的Provider创建服务实例,通过构造函数注入依赖,便于测试时替换为Fake实现。
1.3 明确定义状态边界:单向数据流的实践
核心原则:状态变更应遵循可预测的单向流程,避免分散的状态修改。
// 错误示例:分散的状态修改
class CartManager {
final List<CartItem> _items = [];
void addItem(CartItem item) {
_items.add(item); // ❌ 直接修改内部状态
}
void removeItem(String id) {
_items.removeWhere((item) => item.id == id); // ❌ 直接修改内部状态
}
}
// 正确示例:使用StateNotifier实现可观测状态
class CartNotifier extends StateNotifier<CartState> {
CartNotifier() : super(CartState.empty());
void addItem(CartItem item) {
state = state.copyWith(
items: [...state.items, item],
total: state.total + item.price,
); // ✅ 通过不可变对象更新状态
}
void removeItem(String id) {
final itemToRemove = state.items.firstWhere((item) => item.id == id);
state = state.copyWith(
items: state.items.where((item) => item.id != id).toList(),
total: state.total - itemToRemove.price,
); // ✅ 状态变更可追踪
}
}
实施策略:使用不可变数据类和copyWith方法,通过StateNotifier集中管理状态变更,确保状态变更可预测、可调试。
1.4 导航逻辑归位UI层:业务逻辑不处理路由
核心原则:导航和对话框等UI交互必须在Widget层处理,业务逻辑仅输出状态事件。
// 错误示例:业务逻辑处理导航
class OrderNotifier extends StateNotifier<OrderState> {
final NavigatorState navigator; // ❌ 持有导航器引用
OrderNotifier(this.navigator) : super(OrderState.initial());
Future<void> placeOrder() async {
state = state.copyWith(isLoading: true);
try {
await orderRepository.submit(state.items);
navigator.pushReplacementNamed('/order-confirmation'); // ❌ 直接导航
} catch (e) {
// ...错误处理
} finally {
state = state.copyWith(isLoading: false);
}
}
}
// 正确示例:通过状态触发导航
class OrderNotifier extends StateNotifier<OrderState> {
OrderNotifier() : super(OrderState.initial());
Future<void> placeOrder() async {
state = state.copyWith(isLoading: true);
try {
await orderRepository.submit(state.items);
state = state.copyWith(status: OrderStatus.completed); // ✅ 更新状态
} catch (e) {
state = state.copyWith(
status: OrderStatus.error,
errorMessage: e.toString()
);
} finally {
state = state.copyWith(isLoading: false);
}
}
}
// UI层处理导航
class OrderScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen(orderNotifierProvider, (prev, next) {
if (next.status == OrderStatus.completed) {
Navigator.of(context).pushReplacementNamed('/order-confirmation');
}
});
return Scaffold(/* ... */);
}
}
实施策略:使用ref.listen监听状态变化,在Widget层统一处理导航和对话框展示,保持业务逻辑纯净。
1.5 契约优先设计:定义清晰的接口边界
核心原则:通过抽象类定义组件间通信契约,实现松耦合。
// 错误示例:紧耦合实现
class ProductService {
final FirebaseFirestore _firestore;
ProductService(this._firestore);
Future<List<Product>> getProducts() async {
final snapshot = await _firestore.collection('products').get();
return snapshot.docs.map((doc) => Product.fromFirestore(doc)).toList();
}
}
// 正确示例:基于接口编程
abstract class ProductRepository {
Future<List<Product>> getProducts();
}
class FirebaseProductRepository implements ProductRepository {
final FirebaseFirestore _firestore;
FirebaseProductRepository(this._firestore);
@override
Future<List<Product>> getProducts() async {
// Firebase实现
}
}
class MockProductRepository implements ProductRepository {
@override
Future<List<Product>> getProducts() async {
// 测试用实现
}
}
// 使用抽象类型依赖
final productRepositoryProvider = Provider<ProductRepository>((ref) {
return FirebaseProductRepository(FirebaseFirestore.instance);
});
实施策略:为所有数据访问组件定义抽象接口,依赖注入时使用抽象类型,便于切换实现和单元测试。
1.6 测试驱动架构:从设计阶段考虑可测试性
核心原则:架构设计必须确保每个组件都能独立测试,无需依赖Flutter框架或真实后端。
// 可测试的Notifier设计
class ProductNotifier extends StateNotifier<AsyncValue<List<Product>>> {
final ProductRepository repository;
ProductNotifier({required this.repository}) : super(const AsyncValue.loading()) {
loadProducts();
}
Future<void> loadProducts() async {
state = await AsyncValue.guard(repository.getProducts);
}
}
// 单元测试示例
void main() {
test('ProductNotifier loads products correctly', () async {
// 1. 创建Fake实现
final fakeRepo = MockProductRepository();
when(fakeRepo.getProducts()).thenAnswer((_) async => [
Product(id: '1', name: 'Test Product', price: 99.99)
]);
// 2. 实例化Notifier(无需Flutter测试环境)
final notifier = ProductNotifier(repository: fakeRepo);
// 3. 验证初始状态
expect(notifier.state, isA<AsyncLoading>());
// 4. 等待操作完成
await Future.delayed(Duration.zero);
// 5. 验证最终状态
expect(notifier.state, isA<AsyncData>());
expect((notifier.state as AsyncData).value.length, 1);
});
}
实施策略:使用Fake Repository模式隔离外部依赖,编写不依赖Flutter框架的纯Dart单元测试,确保业务逻辑100%可测试。
1.7 渐进式复杂度:从小处着手,按需扩展
核心原则:架构不应过度设计,小型应用采用简单方案,随规模增长逐步引入复杂模式。
实施策略:新项目从简单架构开始,当出现以下信号时考虑升级:
- 单文件超过300行代码
- 状态共享逻辑重复3次以上
- 同一功能修改涉及5个以上文件
- 测试覆盖率低于70%
二、主流Flutter架构深度对比与选型
2.1 架构特性矩阵对比
| 架构类型 | 学习曲线 | 样板代码量 | 状态可预测性 | 测试友好度 | 团队协作 | 适用规模 |
|---|---|---|---|---|---|---|
| MVC | ⭐⭐⭐⭐ | 中等 | ⭐⭐⭐ | 中等 | 一般 | 小型应用 |
| MVVM | ⭐⭐⭐ | 中等 | ⭐⭐⭐⭐ | 良好 | 良好 | 中小应用 |
| Clean Architecture | ⭐ | 大量 | ⭐⭐⭐⭐⭐ | 优秀 | 优秀 | 大型应用 |
| Bloc | ⭐⭐ | 大量 | ⭐⭐⭐⭐⭐ | 优秀 | 优秀 | 中大型应用 |
| Riverpod+DDD | ⭐⭐ | 中等 | ⭐⭐⭐⭐⭐ | 优秀 | 优秀 | 所有规模 |
2.2 架构模式详解
MVC (Model-View-Controller)
架构层次:
- Model: 数据和业务逻辑
- View: UI组件
- Controller: 协调Model和View
Flutter实现:
// Model
class CounterModel {
int value = 0;
void increment() => value++;
void decrement() => value--;
}
// Controller
class CounterController {
final CounterModel model = CounterModel();
void increment() => model.increment();
void decrement() => model.decrement();
}
// View
class CounterView extends StatefulWidget {
final CounterController controller;
const CounterView({super.key, required this.controller});
@override
State<CounterView> createState() => _CounterViewState();
}
class _CounterViewState extends State<CounterView> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('${widget.controller.model.value}'),
ElevatedButton(
onPressed: () {
setState(() => widget.controller.increment());
},
child: const Text('Increment'),
),
],
);
}
}
优缺点:
- ✅ 简单直观,易于理解
- ✅ 适合小型应用快速开发
- ❌ Model和View紧耦合
- ❌ 状态管理分散,复杂应用难以维护
MVVM (Model-View-ViewModel)
架构层次:
- Model: 数据模型
- View: UI组件
- ViewModel: 持有View状态,处理用户交互
Flutter实现:
// ViewModel
class CounterViewModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知View更新
}
void decrement() {
_count--;
notifyListeners();
}
}
// View
class CounterView extends StatelessWidget {
final CounterViewModel viewModel;
const CounterView({super.key, required this.viewModel});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: viewModel,
child: Consumer<CounterViewModel>(
builder: (context, vm, child) {
return Column(
children: [
Text('${vm.count}'),
ElevatedButton(
onPressed: vm.increment,
child: const Text('Increment'),
),
],
);
},
),
);
}
}
优缺点:
- ✅ View和ViewModel解耦
- ✅ 可测试性良好
- ✅ 适合中等规模应用
- ❌ 缺乏严格规范,易演变为"MVVM反模式"
Clean Architecture (整洁架构)
架构层次:
核心特点:
- 依赖规则:内层不依赖外层,通过接口反转实现
- 领域模型独立于框架和技术细节
- 业务规则集中在领域层,不依赖数据来源
优缺点:
- ✅ 极致的松耦合和可维护性
- ✅ 完美支持大型团队协作
- ✅ 适应技术栈变迁
- ❌ 学习曲线陡峭
- ❌ 大量样板代码
- ❌ 小型应用开发效率低
Bloc Architecture
架构层次:
- 表现层:Widget和BlocBuilder
- 业务逻辑层:Bloc/Cubit
- 数据层:Repository和DataProvider
核心特点:
- 基于事件驱动:UI发送Event,Bloc处理后输出State
- 严格的单向数据流
- 完整的生命周期管理
优缺点:
- ✅ 状态高度可预测
- ✅ 优秀的调试工具
- ✅ 大型应用稳定性好
- ❌ 样板代码过多
- ❌ 简单场景显得笨重
Riverpod+DDD混合架构
架构层次:
核心特点:
- 结合DDD的领域建模和Riverpod的状态管理
- 功能优先的项目组织方式
- 强类型和编译时安全
优缺点:
- ✅ 兼具架构严谨性和开发效率
- ✅ 优秀的测试体验
- ✅ 类型安全和编译时检查
- ✅ 适合从小型到大型的所有应用
- ❌ 需要理解Riverpod和DDD双重概念
2.3 架构选型决策树
三、Riverpod状态管理全家桶:从入门到精通
3.1 6种Provider类型全解析
Provider: 依赖注入基础
用途:提供静态依赖、共享工具类、格式化器等不变化的对象
// 提供日期格式化器
final dateFormatterProvider = Provider<DateFormat>((ref) {
return DateFormat.yMd().add_jm();
});
// 提供日志服务
final loggerProvider = Provider<Logger>((ref) {
return Logger(level: Level.INFO);
});
// 在Widget中使用
class EventCard extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final formatter = ref.watch(dateFormatterProvider);
return Text(formatter.format(event.date));
}
}
StateProvider: 简单状态管理
用途:管理简单值类型状态(计数器、开关状态、选择项)
// 定义计数器状态
final counterProvider = StateProvider<int>((ref) => 0);
// 在UI中使用
class CounterButton extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 读取当前值
final count = ref.watch(counterProvider);
return ElevatedButton(
onPressed: () {
// 更新状态
ref.read(counterProvider.notifier).state++;
},
child: Text('Count: $count'),
);
}
}
StateNotifierProvider: 复杂状态管理
用途:管理包含业务逻辑的复杂状态
// 定义状态类
@immutable
class CartState {
final List<CartItem> items;
final double total;
CartState({required this.items, required this.total});
CartState copyWith({List<CartItem>? items, double? total}) {
return CartState(
items: items ?? this.items,
total: total ?? this.total,
);
}
}
// 定义Notifier
class CartNotifier extends StateNotifier<CartState> {
CartNotifier() : super(CartState(items: [], total: 0.0));
void addItem(CartItem item) {
final newItems = [...state.items, item];
final newTotal = newItems.fold(0, (sum, item) => sum + item.price);
state = state.copyWith(items: newItems, total: newTotal);
}
void removeItem(String id) {
final itemToRemove = state.items.firstWhere((item) => item.id == id);
final newItems = state.items.where((item) => item.id != id).toList();
final newTotal = state.total - itemToRemove.price;
state = state.copyWith(items: newItems, total: newTotal);
}
}
// 提供Notifier
final cartProvider = StateNotifierProvider<CartNotifier, CartState>((ref) {
return CartNotifier();
});
FutureProvider: 异步数据处理
用途:处理单次异步操作(API请求、数据库查询)
// 定义FutureProvider
final productsProvider = FutureProvider<List<Product>>((ref) async {
final repository = ref.watch(productRepositoryProvider);
return repository.getProducts();
});
// 在UI中使用
class ProductsScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final productsAsync = ref.watch(productsProvider);
return productsAsync.when(
loading: () => Center(child: CircularProgressIndicator()),
error: (error, stack) => ErrorView(message: error.toString()),
data: (products) => ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) => ProductCard(products[index]),
),
);
}
}
StreamProvider: 实时数据处理
用途:处理持续更新的数据流(WebSocket、实时数据库)
// 定义StreamProvider
final notificationsProvider = StreamProvider<List<Notification>>((ref) {
final service = ref.watch(notificationServiceProvider);
return service.notificationsStream;
});
// 在UI中使用
class NotificationsScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final notificationsAsync = ref.watch(notificationsProvider);
return notificationsAsync.when(
loading: () => Center(child: CircularProgressIndicator()),
error: (error, stack) => ErrorView(message: error.toString()),
data: (notifications) {
if (notifications.isEmpty) {
return EmptyState(text: 'No notifications yet');
}
return ListView.builder(
itemCount: notifications.length,
itemBuilder: (context, index) => NotificationItem(notifications[index]),
);
},
);
}
}
ChangeNotifierProvider: 遗留代码兼容
用途:兼容基于ChangeNotifier的现有代码
// 注意:新项目应优先使用StateNotifierProvider
final themeProvider = ChangeNotifierProvider<ThemeProvider>((ref) {
return ThemeProvider();
});
class ThemeProvider extends ChangeNotifier {
bool _darkMode = false;
bool get darkMode => _darkMode;
void toggleTheme() {
_darkMode = !_darkMode;
notifyListeners();
}
}
3.2 ref方法深度解析:watch/read/listen/onDispose
ref.watch: 响应式状态监听
核心特性:
- 在Widget或Provider中建立响应式依赖
- 自动追踪依赖变化并触发重建/更新
- 只能在构建方法或Provider创建过程中使用
使用场景对比:
| 使用位置 | 作用 | 注意事项 |
|---|---|---|
| Widget build方法 | 重建Widget以反映最新状态 | 避免在循环或条件中使用 |
| Provider创建函数 | 建立Provider间依赖 | 会导致Provider自动重建 |
| StateNotifier构造函数 | 初始化依赖 | 谨慎使用,可能导致重建循环 |
// Widget中使用
Widget build(BuildContext context, WidgetRef ref) {
// 每次counter变化都会重建Widget
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
// Provider间依赖
final userOrdersProvider = FutureProvider<List<Order>>((ref) async {
// 当userId变化时,自动重新获取订单
final userId = ref.watch(authProvider).value?.id;
if (userId == null) return [];
return ref.watch(orderRepositoryProvider).getUserOrders(userId);
});
ref.read: 单次状态读取
核心特性:
- 非响应式读取,不会触发重建
- 适合在事件处理中使用
- 可获取Provider的notifier实例
使用场景:
| 使用场景 | 代码示例 |
|---|---|
| 按钮点击事件 | onPressed: () => ref.read(counterProvider.notifier).increment() |
| 初始化一次性操作 | ref.read(analyticsServiceProvider).logScreenView('home') |
| 获取非响应式数据 | final config = ref.read(appConfigProvider); |
// 正确用法:事件处理中读取
ElevatedButton(
onPressed: () {
// 单次读取,不建立依赖
final notifier = ref.read(cartProvider.notifier);
notifier.addItem(product);
},
child: Text('Add to Cart'),
)
// 错误用法:构建方法中读取
Widget build(BuildContext context, WidgetRef ref) {
// ❌ 不会响应状态变化
final count = ref.read(counterProvider);
return Text('Count: $count');
}
ref.listen: 状态变化监听与副作用
核心特性:
- 监听状态变化但不触发Widget重建
- 适合处理副作用(导航、日志、分析)
- 可访问前一个状态和当前状态
使用场景:
// 监听认证状态变化处理导航
void initState() {
super.initState();
// 监听状态变化
_authSubscription = ref.listen(authProvider, (previous, next) {
if (previous?.isAuthenticated == false && next.isAuthenticated == true) {
// 用户登录成功,导航到主页
Navigator.pushReplacementNamed(context, '/home');
} else if (previous?.isAuthenticated == true && next.isAuthenticated == false) {
// 用户登出,导航到登录页
Navigator.pushReplacementNamed(context, '/login');
}
});
}
// 监听错误状态显示提示
ref.listen<AsyncValue<List<Product>>>(
productsProvider,
(previous, next) {
next.whenOrNull(
error: (error, stack) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to load products: $error')),
);
},
);
},
);
ref.onDispose: 资源清理注册
核心特性:
- 注册清理回调,在Widget/Provider销毁时执行
- 防止内存泄漏的关键机制
- 支持取消订阅、关闭流等操作
使用场景:
// Widget中清理资源
@override
void initState() {
super.initState();
_stream = ref.read(dataServiceProvider).getUpdates();
_subscription = _stream.listen((data) {
// 处理数据
});
// 注册清理回调
ref.onDispose(() {
_subscription.cancel();
});
}
// StateNotifier中清理
class DataNotifier extends StateNotifier<DataState> {
final DataService _service;
late final StreamSubscription _subscription;
DataNotifier(this._service) : super(DataState.initial()) {
_subscription = _service.updates.listen((data) {
state = state.copyWith(data: data);
});
// 注册清理
ref.onDispose(() {
_subscription.cancel();
});
}
}
3.3 高级状态管理模式
AsyncValue: 异步状态的统一处理
核心优势:
- 封装加载/错误/数据三种状态
- 提供统一的处理API
- 简化异步UI逻辑
使用模式:
// 基本用法
final dataAsync = ref.watch(dataProvider);
dataAsync.when(
loading: () => LoadingWidget(),
error: (error, stack) => ErrorWidget(error),
data: (data) => DataDisplayWidget(data),
);
// 高级用法:whenOrNull
dataAsync.whenOrNull(
data: (data) => DataDisplayWidget(data),
error: (error, _) => showErrorToast(error),
);
// 转换数据:map
final displayText = dataAsync.map(
loading: (_) => 'Loading...',
error: (e) => 'Error: ${e.error}',
data: (d) => 'Data: ${d.value}',
);
家族Provider: 动态创建Provider
核心优势:
- 根据参数动态创建Provider实例
- 适合列表项等重复组件的状态管理
- 自动缓存和清理实例
使用示例:
// 定义Family Provider
final todoProvider = StateNotifierProvider.family<TodoNotifier, Todo, String>((ref, todoId) {
final repository = ref.watch(todoRepositoryProvider);
return TodoNotifier(todoId: todoId, repository: repository);
});
// 在列表中使用
ListView.builder(
itemCount: todoIds.length,
itemBuilder: (context, index) {
final todoId = todoIds[index];
// 为每个ID创建独立的Provider实例
final todoAsync = ref.watch(todoProvider(todoId));
return todoAsync.when(
loading: () => TodoSkeleton(),
error: (error, stack) => TodoErrorItem(error),
data: (todo) => TodoItem(todo: todo),
);
},
);
Provider组合: 构建复杂状态依赖
核心模式:
// 1. 基础数据Provider
final userProvider = StreamProvider<User?>((ref) {
return ref.watch(authServiceProvider).userChanges;
});
// 2. 派生数据Provider
final isPremiumProvider = Provider<bool>((ref) {
return ref.watch(userProvider).value?.isPremium ?? false;
});
// 3. 依赖多个Provider
final filteredProductsProvider = Provider<List<Product>>((ref) {
final allProducts = ref.watch(productsProvider).value ?? [];
final filter = ref.watch(productFilterProvider);
final isPremium = ref.watch(isPremiumProvider);
return allProducts.where((product) {
if (!isPremium && product.isPremiumOnly) return false;
return product.category == filter.category &&
product.price <= filter.maxPrice;
}).toList();
});
四、项目结构设计:功能优先vs层优先
4.1 两种组织方式深度对比
层优先结构 (Layer-First)
目录结构:
lib/
├── presentation/ # 表现层
│ ├── screens/ # 屏幕
│ ├── widgets/ # 通用组件
│ ├── blocs/ # 业务逻辑组件
│ └── navigation/ # 导航
├── domain/ # 领域层
│ ├── entities/ # 实体
│ ├── usecases/ # 用例
│ └── repositories/ # 仓储接口
├── data/ # 数据层
│ ├── repositories/ # 仓储实现
│ ├── datasources/ # 数据源
│ └── models/ # 数据模型
└── core/ # 核心功能
├── config/ # 配置
├── errors/ # 错误处理
└── utils/ # 工具类
适用场景:
- 小型应用或工具类应用
- 技术驱动而非业务驱动的项目
- 团队按技术角色划分(UI团队、后端团队)
优缺点:
- ✅ 技术职责清晰,新人易于理解
- ✅ 适合技术迭代和重构
- ❌ 功能修改需要跨多层目录
- ❌ 大型项目目录导航困难
- ❌ 业务逻辑分散
功能优先结构 (Feature-First)
目录结构:
lib/
├── features/ # 功能模块
│ ├── auth/ # 认证功能
│ │ ├── presentation/ # 表现层
│ │ ├── domain/ # 领域层
│ │ ├── data/ # 数据层
│ │ └── auth.dart # 公共API导出
│ ├── products/ # 产品功能
│ │ ├── presentation/
│ │ ├── domain/
│ │ └── data/
│ └── cart/ # 购物车功能
│ ├── presentation/
│ ├── domain/
│ └── data/
├── core/ # 核心功能
│ ├── state/ # 全局状态
│ ├── network/ # 网络
│ └── utils/ # 工具类
└── app/ # 应用入口
├── router/ # 路由配置
└── app.dart # 根组件
适用场景:
- 中大型业务应用
- 业务功能明确且边界清晰
- 团队按业务功能划分(认证团队、订单团队)
优缺点:
- ✅ 功能内聚,修改集中在单一目录
- ✅ 支持团队并行开发
- ✅ 便于功能模块化和代码复用
- ✅ 符合DDD领域边界思想
- ❌ 初期结构设计难度高
- ❌ 通用组件管理复杂
4.2 功能模块内部结构最佳实践
推荐的功能模块结构:
features/product/
├── presentation/
│ ├── widgets/ # 专用UI组件
│ │ ├── product_card.dart
│ │ ├── product_image.dart
│ │ └── product_price.dart
│ ├── screens/ # 屏幕
│ │ ├── product_list_screen.dart
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



