← Назад к вопросам
Опишите архитектуру Flutter приложения.
2.4 Senior🔥 201 комментариев
#Архитектура Flutter#ООП и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Архитектура Flutter приложения: Clean Architecture + BLoC
Профессиональные Flutter приложения строятся на принципах Clean Architecture, которая разделяет код на слои с четкими обязанностями. Это обеспечивает масштабируемость, тестируемость и поддерживаемость.
Структура проекта
lib/
├── core/
│ ├── errors/
│ ├── constants/
│ └── usecases/
├── features/
│ └── posts/
│ ├── data/
│ │ ├── datasources/
│ │ ├── models/
│ │ └── repositories/
│ ├── domain/
│ │ ├── entities/
│ │ ├── repositories/
│ │ └── usecases/
│ └── presentation/
│ ├── bloc/
│ ├── pages/
│ └── widgets/
1. Domain слой (бизнес логика)
Индепендентный слой без зависимостей от фреймворков.
class Post {
final String id;
final String title;
final String content;
Post({required this.id, required this.title, required this.content});
}
abstract class PostRepository {
Future<List<Post>> getPosts();
}
class GetPostsUseCase {
final PostRepository repository;
GetPostsUseCase(this.repository);
Future<List<Post>> call() => repository.getPosts();
}
2. Data слой (работа с источниками данных)
Реализует интерфейсы Domain, взаимодействует с API и БД.
class PostModel extends Post {
PostModel({
required String id,
required String title,
required String content,
}) : super(id: id, title: title, content: content);
factory PostModel.fromJson(Map<String, dynamic> json) {
return PostModel(
id: json['id'],
title: json['title'],
content: json['content'],
);
}
}
class PostRemoteDataSource {
final http.Client httpClient;
PostRemoteDataSource(this.httpClient);
Future<List<PostModel>> getPosts() async {
final response = await httpClient.get(
Uri.parse('https://api.example.com/posts'),
);
if (response.statusCode == 200) {
final List<dynamic> jsonList = json.decode(response.body);
return jsonList.map((item) => PostModel.fromJson(item)).toList();
} else {
throw ServerException();
}
}
}
class PostRepositoryImpl implements PostRepository {
final PostRemoteDataSource remoteDataSource;
final NetworkInfo networkInfo;
PostRepositoryImpl({
required this.remoteDataSource,
required this.networkInfo,
});
@override
Future<List<Post>> getPosts() async {
if (await networkInfo.isConnected) {
try {
final remotePosts = await remoteDataSource.getPosts();
return remotePosts;
} on ServerException {
throw ServerFailure();
}
} else {
throw NoInternetFailure();
}
}
}
3. Presentation слой (BLoC + UI)
Управление состоянием через BLoC паттерн.
abstract class PostEvent extends Equatable {}
class FetchPostsEvent extends PostEvent {
@override
List<Object?> get props => [];
}
abstract class PostState extends Equatable {}
class PostInitial extends PostState {
@override
List<Object?> get props => [];
}
class PostLoading extends PostState {
@override
List<Object?> get props => [];
}
class PostLoaded extends PostState {
final List<Post> posts;
PostLoaded(this.posts);
@override
List<Object?> get props => [posts];
}
class PostError extends PostState {
final String message;
PostError(this.message);
@override
List<Object?> get props => [message];
}
class PostBloc extends Bloc<PostEvent, PostState> {
final GetPostsUseCase getPostsUseCase;
PostBloc({required this.getPostsUseCase}) : super(PostInitial()) {
on<FetchPostsEvent>(_onFetchPosts);
}
Future<void> _onFetchPosts(
FetchPostsEvent event,
Emitter<PostState> emit,
) async {
emit(PostLoading());
try {
final posts = await getPostsUseCase();
emit(PostLoaded(posts));
} catch (e) {
emit(PostError('Ошибка загрузки'));
}
}
}
class PostsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Посты')),
body: BlocBuilder<PostBloc, PostState>(
builder: (context, state) {
if (state is PostLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is PostLoaded) {
return ListView.builder(
itemCount: state.posts.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(state.posts[index].title),
);
},
);
} else if (state is PostError) {
return Center(child: Text(state.message));
}
return const Center(child: Text('Начните загрузку'));
},
),
);
}
}
Ключевые компоненты
- Domain: Бизнес логика, UseCases, Entities
- Data: DataSources, Models, Repository реализация
- Presentation: BLoC, Pages, Widgets
Dependency Injection (GetIt)
final getIt = GetIt.instance;
void setupServiceLocator() {
getIt.registerSingleton(http.Client());
getIt.registerSingleton<PostRemoteDataSource>(
PostRemoteDataSource(getIt<http.Client>()),
);
getIt.registerSingleton<PostRepository>(
PostRepositoryImpl(remoteDataSource: getIt()),
);
getIt.registerSingleton(GetPostsUseCase(getIt()));
getIt.registerFactory(() => PostBloc(getPostsUseCase: getIt()));
}
Best Practices
- Разделение ответственности — каждый слой отвечает за одно
- Независимость слоев — Domain не знает о Data и Presentation
- Тестируемость — каждый слой тестируется через мок-объекты
- Переиспользование — код переиспользуется во многих местах
- Масштабируемость — новые функции добавляются по одной схеме
Эта архитектура подходит для средних и крупных приложений, обеспечивая долгосрочную поддержку и качество кода.