返回博客
架构2025年9月29日安安科技团队阅读时间 12 分钟

Flutter 应用架构
最佳实践

掌握 Flutter 应用架构:以整洁的模式、关注点分离及可扩展的代码结构,让代码库随团队与需求一同成长。

Flutter 应用架构最佳实践
// 简报

一个架构良好的 Flutter 应用,决定了代码库是能够从容扩展,还是沦为维护噩梦。良好的架构让你的应用在需求演变时更易于测试、调试和扩展。

本指南涵盖经过验证的架构模式、依赖注入策略和代码组织技巧,帮助你构建可维护、可测试、足以应对企业级开发的 Flutter 应用。

CLEAN ARCHITECTURE:黄金标准

Clean Architecture 将应用划分为职责分明、依赖清晰的多个分层,业务逻辑独立于框架、UI 和外部数据源。

clean_architecture.dart
// Domain Layer - Business Logic
abstract class UserRepository {
  Future<User> getUser(String id);
  Future<void> updateUser(User user);
}

class User {
  final String id;
  final String name;
  final String email;
  
  const User({
    required this.id,
    required this.name,
    required this.email,
  });
}

class GetUserUseCase {
  final UserRepository repository;
  
  GetUserUseCase(this.repository);
  
  Future<User> call(String userId) {
    return repository.getUser(userId);
  }
}

// Data Layer - External Data Sources
class ApiUserRepository implements UserRepository {
  final ApiClient apiClient;
  
  ApiUserRepository(this.apiClient);
  
  @override
  Future<User> getUser(String id) async {
    final response = await apiClient.get('/users/$id');
    return User.fromJson(response.data);
  }
  
  @override
  Future<void> updateUser(User user) async {
    await apiClient.put('/users/${user.id}', user.toJson());
  }
}

// Presentation Layer - UI Logic
class UserBloc extends Bloc<UserEvent, UserState> {
  final GetUserUseCase getUserUseCase;
  
  UserBloc(this.getUserUseCase) : super(UserInitial()) {
    on<LoadUser>(_onLoadUser);
  }
  
  Future<void> _onLoadUser(LoadUser event, Emitter<UserState> emit) async {
    emit(UserLoading());
    try {
      final user = await getUserUseCase(event.userId);
      emit(UserLoaded(user));
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }
}

领域层

  • 业务实体
  • 用例
  • Repository 接口
  • 业务规则

数据层

  • Repository 实现
  • 数据源(API、数据库)
  • 数据模型
  • 外部服务

展示层

  • UI Widget
  • 状态管理
  • ViewModel / BLoC
  • 导航

MVVM:MODEL-VIEW-VIEWMODEL

MVVM 通过 ViewModel 将 UI 逻辑与业务逻辑分离,特别适合 UI 交互复杂的应用,与 Provider 或 Riverpod 搭配使用效果极佳。

mvvm_pattern.dart
// Model
class Product {
  final String id;
  final String name;
  final double price;
  final String imageUrl;
  
  Product({
    required this.id,
    required this.name,
    required this.price,
    required this.imageUrl,
  });
}

// ViewModel
class ProductListViewModel extends ChangeNotifier {
  final ProductRepository _repository;
  
  ProductListViewModel(this._repository);
  
  List<Product> _products = [];
  bool _isLoading = false;
  String? _error;
  
  List<Product> get products => _products;
  bool get isLoading => _isLoading;
  String? get error => _error;
  
  Future<void> loadProducts() async {
    _setLoading(true);
    _setError(null);
    
    try {
      _products = await _repository.getProducts();
      notifyListeners();
    } catch (e) {
      _setError(e.toString());
    } finally {
      _setLoading(false);
    }
  }
  
  Future<void> addToCart(Product product) async {
    try {
      await _repository.addToCart(product.id);
      // Show success message or update UI
    } catch (e) {
      _setError('Failed to add product to cart');
    }
  }
  
  void _setLoading(bool loading) {
    _isLoading = loading;
    notifyListeners();
  }
  
  void _setError(String? error) {
    _error = error;
    notifyListeners();
  }
}

// View
class ProductListView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => ProductListViewModel(context.read<ProductRepository>())
        ..loadProducts(),
      child: Scaffold(
        appBar: AppBar(title: Text('Products')),
        body: Consumer<ProductListViewModel>(
          builder: (context, viewModel, child) {
            if (viewModel.isLoading) {
              return Center(child: CircularProgressIndicator());
            }
            
            if (viewModel.error != null) {
              return Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text('Error: ${viewModel.error}'),
                    ElevatedButton(
                      onPressed: viewModel.loadProducts,
                      child: Text('Retry'),
                    ),
                  ],
                ),
              );
            }
            
            return ListView.builder(
              itemCount: viewModel.products.length,
              itemBuilder: (context, index) {
                final product = viewModel.products[index];
                return ProductTile(
                  product: product,
                  onAddToCart: () => viewModel.addToCart(product),
                );
              },
            );
          },
        ),
      ),
    );
  }
}

MVVM 的优势

  • UI 与业务逻辑清晰分离
  • ViewModel 易于进行单元测试
  • ViewModel 可在不同页面复用
  • 与响应式编程配合良好

依赖注入模式

依赖注入让代码更易测试、更具弹性,并遵循依赖倒置原则。以下是 Flutter 应用中行之有效的模式。

service_locator.dart
// Service Locator Pattern with GetIt
final GetIt sl = GetIt.instance;

void setupServiceLocator() {
  // External services
  sl.registerLazySingleton<ApiClient>(() => ApiClient());
  sl.registerLazySingleton<DatabaseHelper>(() => DatabaseHelper());
  
  // Repositories
  sl.registerLazySingleton<UserRepository>(
    () => ApiUserRepository(sl<ApiClient>()),
  );
  sl.registerLazySingleton<ProductRepository>(
    () => ApiProductRepository(sl<ApiClient>()),
  );
  
  // Use cases
  sl.registerLazySingleton<GetUserUseCase>(
    () => GetUserUseCase(sl<UserRepository>()),
  );
  sl.registerLazySingleton<GetProductsUseCase>(
    () => GetProductsUseCase(sl<ProductRepository>()),
  );
  
  // ViewModels
  sl.registerFactory<UserViewModel>(
    () => UserViewModel(sl<GetUserUseCase>()),
  );
  sl.registerFactory<ProductListViewModel>(
    () => ProductListViewModel(sl<GetProductsUseCase>()),
  );
}

// Provider with dependency injection
class UserPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<UserViewModel>(
      create: (_) => sl<UserViewModel>(),
      child: UserView(),
    );
  }
}

// Factory pattern for complex objects
abstract class RepositoryFactory {
  UserRepository createUserRepository();
  ProductRepository createProductRepository();
}

class ApiRepositoryFactory implements RepositoryFactory {
  final ApiClient apiClient;
  
  ApiRepositoryFactory(this.apiClient);
  
  @override
  UserRepository createUserRepository() {
    return ApiUserRepository(apiClient);
  }
  
  @override
  ProductRepository createProductRepository() {
    return ApiProductRepository(apiClient);
  }
}

// Environment-based configuration
class AppConfig {
  static const String _baseUrl = String.fromEnvironment(
    'BASE_URL',
    defaultValue: 'https://api.example.com',
  );
  
  static const bool _isProduction = bool.fromEnvironment(
    'PRODUCTION',
    defaultValue: false,
  );
  
  static String get baseUrl => _baseUrl;
  static bool get isProduction => _isProduction;
  static bool get isDevelopment => !_isProduction;
}

void setupEnvironmentDependencies() {
  if (AppConfig.isDevelopment) {
    sl.registerLazySingleton<ApiClient>(
      () => MockApiClient(), // Use mock in development
    );
  } else {
    sl.registerLazySingleton<ApiClient>(
      () => ApiClient(baseUrl: AppConfig.baseUrl),
    );
  }
}

DI 最佳实践

  • 注册接口而非具体实现
  • 有状态的对象使用 factory 注册
  • 单例服务尽量保持无状态
  • 在应用启动时完成依赖设置

理想的文件夹结构

条理分明的文件夹结构让代码导航更直观,也有助于维持架构边界。以下是适用于 Flutter 应用的可扩展结构。

lib/
lib/
├── core/                           # Shared app foundation
│   ├── constants/                  # App-wide constants
│   │   ├── api_constants.dart
│   │   ├── app_strings.dart
│   │   └── app_colors.dart
│   ├── error/                      # Error handling
│   │   ├── failures.dart
│   │   └── exceptions.dart
│   ├── network/                    # Network layer
│   │   ├── api_client.dart
│   │   └── network_info.dart
│   ├── utils/                      # Utility functions
│   │   ├── validators.dart
│   │   └── formatters.dart
│   └── injection_container.dart    # Dependency injection setup
├── features/                       # Feature-based organization
│   ├── authentication/
│   │   ├── data/
│   │   │   ├── datasources/
│   │   │   │   ├── auth_local_datasource.dart
│   │   │   │   └── auth_remote_datasource.dart
│   │   │   ├── models/
│   │   │   │   └── user_model.dart
│   │   │   └── repositories/
│   │   │       └── auth_repository_impl.dart
│   │   ├── domain/
│   │   │   ├── entities/
│   │   │   │   └── user.dart
│   │   │   ├── repositories/
│   │   │   │   └── auth_repository.dart
│   │   │   └── usecases/
│   │   │       ├── login_usecase.dart
│   │   │       └── logout_usecase.dart
│   │   └── presentation/
│   │       ├── bloc/
│   │       │   ├── auth_bloc.dart
│   │       │   ├── auth_event.dart
│   │       │   └── auth_state.dart
│   │       ├── pages/
│   │       │   ├── login_page.dart
│   │       │   └── signup_page.dart
│   │       └── widgets/
│   │           ├── login_form.dart
│   │           └── password_field.dart
│   ├── products/
│   │   ├── data/
│   │   ├── domain/
│   │   └── presentation/
│   └── cart/
│       ├── data/
│       ├── domain/
│       └── presentation/
├── shared/                         # Shared across features
│   ├── widgets/                    # Reusable UI components
│   │   ├── buttons/
│   │   ├── forms/
│   │   └── loading/
│   ├── extensions/                 # Dart extensions
│   │   ├── string_extensions.dart
│   │   └── date_extensions.dart
│   └── mixins/                     # Reusable behavior
│       ├── validation_mixin.dart
│       └── loading_mixin.dart
├── config/                         # App configuration
│   ├── app_config.dart
│   ├── routes.dart
│   └── themes.dart
└── main.dart                       # App entry point

结构优势

  • 关注点清晰分离
  • 功能易于定位和修改
  • 强化架构边界
  • 随团队规模与复杂度扩展

Flutter 架构原则

1

关注点分离:每一分层只承担单一职责——UI 负责呈现,领域层承载业务逻辑,数据层管理外部数据源。

2

依赖倒置:高层模块不应依赖低层模块,两者都应依赖抽象(接口)。

3

可测试性优先:设计架构时以方便单元测试为前提。如果代码难以测试,多半是设计欠佳。

4

按功能组织:以功能而非技术分层来组织代码,让开发特定功能时更加得心应手。

5

渐进式增强:从简单起步,按需要逐步增加复杂度,不要为尚未出现的需求过度设计。

// 蓝图

准备好规划你的 Flutter 应用架构?

我们的团队专注于构建可扩展、易维护的 Flutter 架构。让我们帮你设计一个随业务一同成长的应用结构!

联系我们
// 信号增幅

分享这份指南