Flutter架构设计终极指南:从bizz84/flutter-tips-and-tricks提炼的7条黄金法则与实战方案

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 渐进式复杂度:从小处着手,按需扩展

核心原则:架构不应过度设计,小型应用采用简单方案,随规模增长逐步引入复杂模式。

mermaid

实施策略:新项目从简单架构开始,当出现以下信号时考虑升级:

  • 单文件超过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 (整洁架构)

架构层次mermaid

核心特点

  • 依赖规则:内层不依赖外层,通过接口反转实现
  • 领域模型独立于框架和技术细节
  • 业务规则集中在领域层,不依赖数据来源

优缺点

  • ✅ 极致的松耦合和可维护性
  • ✅ 完美支持大型团队协作
  • ✅ 适应技术栈变迁
  • ❌ 学习曲线陡峭
  • ❌ 大量样板代码
  • ❌ 小型应用开发效率低
Bloc Architecture

架构层次

  • 表现层:Widget和BlocBuilder
  • 业务逻辑层:Bloc/Cubit
  • 数据层:Repository和DataProvider

核心特点

  • 基于事件驱动:UI发送Event,Bloc处理后输出State
  • 严格的单向数据流
  • 完整的生命周期管理

优缺点

  • ✅ 状态高度可预测
  • ✅ 优秀的调试工具
  • ✅ 大型应用稳定性好
  • ❌ 样板代码过多
  • ❌ 简单场景显得笨重
Riverpod+DDD混合架构

架构层次mermaid

核心特点

  • 结合DDD的领域建模和Riverpod的状态管理
  • 功能优先的项目组织方式
  • 强类型和编译时安全

优缺点

  • ✅ 兼具架构严谨性和开发效率
  • ✅ 优秀的测试体验
  • ✅ 类型安全和编译时检查
  • ✅ 适合从小型到大型的所有应用
  • ❌ 需要理解Riverpod和DDD双重概念

2.3 架构选型决策树

mermaid

三、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),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值