← Назад к вопросам

Опишите архитектуру 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
  • Тестируемость — каждый слой тестируется через мок-объекты
  • Переиспользование — код переиспользуется во многих местах
  • Масштабируемость — новые функции добавляются по одной схеме

Эта архитектура подходит для средних и крупных приложений, обеспечивая долгосрочную поддержку и качество кода.