
一个架构良好的 Flutter 应用,决定了代码库是能够从容扩展,还是沦为维护噩梦。良好的架构让你的应用在需求演变时更易于测试、调试和扩展。
本指南涵盖经过验证的架构模式、依赖注入策略和代码组织技巧,帮助你构建可维护、可测试、足以应对企业级开发的 Flutter 应用。
CLEAN ARCHITECTURE:黄金标准
Clean Architecture 将应用划分为职责分明、依赖清晰的多个分层,业务逻辑独立于框架、UI 和外部数据源。
// 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 搭配使用效果极佳。
// 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 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/ ├── 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 架构原则
关注点分离:每一分层只承担单一职责——UI 负责呈现,领域层承载业务逻辑,数据层管理外部数据源。
依赖倒置:高层模块不应依赖低层模块,两者都应依赖抽象(接口)。
可测试性优先:设计架构时以方便单元测试为前提。如果代码难以测试,多半是设计欠佳。
按功能组织:以功能而非技术分层来组织代码,让开发特定功能时更加得心应手。
渐进式增强:从简单起步,按需要逐步增加复杂度,不要为尚未出现的需求过度设计。


