← Назад к вопросам
Какие архитектурные паттерны используются во Flutter (MVP, MVVM, Clean Architecture)?
3.0 Senior🔥 282 комментариев
#Архитектура Flutter#ООП и паттерны
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Архитектурные паттерны во Flutter
Архитектура определяет качество, масштабируемость и maintainability проекта. Рассмотрю три основных подхода.
1. MVC (Model-View-Controller) — старый подход
Одна из первых архитектур, но во Flutter редко используется.
┌─────────────┐
│ Controller │
│ (Logic) │
└──────┬──────┘
│
├──→ Model (State, Data)
└──→ View (UI)
Проблемы:
- Controller быстро становится жирным
- Сложно тестировать UI
- Tight coupling между компонентами
2. MVP (Model-View-Presenter) — лучше, но громоздко
┌─────────────────────────────────┐
│ Presenter │
│ (Business Logic & UI Logic) │
└─────────────────────────────────┘
↓ implements ↑ updates
┌─────────────┐ ┌──────────┐
│ View │ │ Model │
│ (UI только)│ │ (Data) │
└─────────────┘ └──────────┘
Примеры:
- User taps button
→ View notifies Presenter
→ Presenter updates Model
→ Model notifies View
→ View rebuilds
// Контракт между View и Presenter
abstract class LoginView {
void showProgress();
void hideProgress();
void showError(String message);
void navigateToHome();
}
class LoginPresenter {
final LoginView view;
final AuthRepository repository;
LoginPresenter({required this.view, required this.repository});
Future<void> login(String email, String password) async {
view.showProgress();
try {
final result = await repository.login(email, password);
view.hideProgress();
view.navigateToHome();
} catch (e) {
view.hideProgress();
view.showError(e.toString());
}
}
}
// View реализует контракт
class LoginScreen extends StatefulWidget {
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> implements LoginView {
late LoginPresenter presenter;
bool isLoading = false;
@override
void initState() {
super.initState();
presenter = LoginPresenter(
view: this,
repository: AuthRepository(),
);
}
@override
void showProgress() {
setState(() => isLoading = true);
}
@override
void hideProgress() {
setState(() => isLoading = false);
}
@override
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
@override
void navigateToHome() {
Navigator.of(context).pushReplacementNamed('/home');
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: () => presenter.login('test@test.com', 'password'),
child: Text('Login'),
),
);
}
}
Минусы: Много boilerplate кода, interface для каждого экрана.
3. MVVM (Model-View-ViewModel) — модерный подход
ВViewModel хранится бизнес-логика и состояние, View только отображает.
┌──────────────────────────────┐
│ ViewModel │
│ (State + Business Logic) │
│ Notifies listeners │
└──────────────────────────────┘
↑ ↓
Watch Update
↑ ↓
┌──────────────────────────────┐
│ View (UI) │
│ Rebuilds on state change │
└──────────────────────────────┘
↓
┌──────────────────────────────┐
│ Model (Data) │
└──────────────────────────────┘
// ViewModel с Provider
class LoginViewModel extends ChangeNotifier {
final AuthRepository repository;
String? _error;
bool _isLoading = false;
LoginViewModel({required this.repository});
String? get error => _error;
bool get isLoading => _isLoading;
Future<void> login(String email, String password) async {
_isLoading = true;
_error = null;
notifyListeners();
try {
await repository.login(email, password);
_isLoading = false;
notifyListeners();
} catch (e) {
_isLoading = false;
_error = e.toString();
notifyListeners();
}
}
}
// View просто отображает состояние
class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer<LoginViewModel>(
builder: (context, viewModel, child) {
if (viewModel.isLoading) {
return Center(child: CircularProgressIndicator());
}
return Column(
children: [
if (viewModel.error != null)
Text('Ошибка: ${viewModel.error}', style: TextStyle(color: Colors.red)),
ElevatedButton(
onPressed: () => viewModel.login('test@test.com', 'password'),
child: Text('Login'),
),
],
);
},
),
);
}
}
4. Clean Architecture — enterprise подход
┌─────────────────────────────────┐
│ Presentation Layer │ ← UI, Pages, Widgets
│ (Views, ViewModels) │
└──────────────┬──────────────────┘
↓ depends on
┌─────────────────────────────────┐
│ Application Layer │ ← Use Cases, DTOs
│ (Repositories, Services) │
└──────────────┬──────────────────┘
↓ depends on
┌─────────────────────────────────┐
│ Domain Layer │ ← Business Rules
│ (Entities, Interfaces) │
└──────────────┬──────────────────┘
↓ depends on
┌─────────────────────────────────┐
│ Infrastructure Layer │ ← DB, API, External Services
│ (Data Sources, Adapters) │
└─────────────────────────────────┘
Правило: зависимости ТОЛЬКО вниз
presentation ← application ← domain → infrastructure
// lib/domain/entities/user.dart
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
}
// lib/domain/repositories/user_repository.dart
abstract class UserRepository {
Future<User> getUser(int id);
Future<void> updateUser(User user);
}
// lib/domain/use_cases/get_user_use_case.dart
class GetUserUseCase {
final UserRepository repository;
GetUserUseCase({required this.repository});
Future<User> call(int id) async {
return await repository.getUser(id);
}
}
// lib/infrastructure/data_sources/user_remote_data_source.dart
class UserRemoteDataSource {
final http.Client httpClient;
Future<UserModel> getUser(int id) async {
final response = await httpClient.get(Uri.parse('...'));
return UserModel.fromJson(jsonDecode(response.body));
}
}
// lib/infrastructure/repositories/user_repository_impl.dart
class UserRepositoryImpl implements UserRepository {
final UserRemoteDataSource remoteDataSource;
@override
Future<User> getUser(int id) async {
return await remoteDataSource.getUser(id);
}
}
// lib/presentation/view_models/user_view_model.dart
class UserViewModel extends ChangeNotifier {
final GetUserUseCase getUserUseCase;
User? _user;
bool _isLoading = false;
Future<void> loadUser(int id) async {
_isLoading = true;
notifyListeners();
try {
_user = await getUserUseCase(id);
} finally {
_isLoading = false;
notifyListeners();
}
}
}
Сравнение
| Критерий | MVP | MVVM | Clean |
|---|---|---|---|
| Сложность | Средняя | Простая | Высокая |
| Boilerplate | Много | Мало | Много |
| Тестируемость | Хорошая | Отличная | Отличная |
| Масштабируемость | Среднее | Хорошее | Отличное |
| Кривая обучения | Средняя | Низкая | Высокая |
Рекомендации для production
Стартапы / MVPs: MVVM + Provider
- Быстро разработать
- Легко добавить тесты
- Понятный код
Enterprise проекты: Clean Architecture + Riverpod
- Масштабируемость
- Независимость слоёв
- Простота тестирования
Средние проекты: MVVM + GetX или Riverpod
- Баланс между простотой и масштабируемостью