Flutter Clean Architecture 实践(1)

发布于 1周前 作者 gougou168 来自 Flutter

Flutter Clean Architecture 实践(1)

问题概述

在 Flutter 中保持代码的清晰与可维护性尤为重要,因为 Flutter 中逻辑代码和 UI 代码都使用 Dart 编写,容易导致业务逻辑和 UI 代码混杂。即便像 BLoC(或其子集 Cubit)这样的状态管理方案,在代码可扩展性方面也可能显得力不从心。那么,如何在 Flutter 中保持代码的可维护性?

解决方案

保持应用程序可维护的秘诀是由“Bob 大叔”提出的 Clean Architecture(清洁架构)。具体实现方式如下:

  1. 分层设计

    • 将代码分成独立的层,每层依赖于抽象,而不是具体实现。
    • 分层示意图中,水平箭头(→)表示依赖流向。例如,实体(Entities)完全独立,仅用例(Use Cases)依赖于实体,依次类推。
  2. 项目目标

    • 使用 Firebase 作为后端服务。
    • 选择 Cubit(BLoC 的子集)作为状态管理工具(当然,也可以选择其他状态管理方案)。
    • 清洁架构的核心并不依赖于特定的状态管理工具。
  3. Reso Coder 提出的清洁架构模型

    • 项目结构包括三个主要层次:Domain(领域层)、Data(数据层)和 Presentation(展示层)。

项目结构详解

  1. Presentation(展示层)

    • 负责在屏幕上显示内容,并通过控件触发事件。
    • 逻辑处理器(如 BLoC / Cubit)不会包含太多逻辑,而是将所有任务委派给用例层。
    • 展示层结构:
      • Cubit 类存放在 Cubit 目录下。
      • 页面存放在 Pages 目录下。
      • 其他小组件存放在 Widgets 目录下。
  2. Domain(领域层)

    • 最核心的部分,包含核心业务逻辑(用例)和业务对象(实体)。
    • 该层完全独立于其他层,但其他层会依赖于它。
    • 用例类负责封装应用程序特定业务场景的逻辑。
    • 存储库(Repository)处于数据层与领域层之间,仅定义契约,具体实现在数据层中完成。
    • 领域层结构:包含用例和实体等目录。
  3. Data(数据层)

    • 包含存储库的实现和数据源(远程数据源与本地数据源)。
    • 远程数据源:如 Firebase(Cloud Firestore)、API 或其他数据库。
    • 本地数据源:用于缓存数据。
    • 存储库决定是返回真实数据还是缓存数据。
    • 模型类负责数据转换,例如:JSON ↔ Dart 对象快照 ↔ Dart 对象。
    • 数据层结构:包含存储库实现和数据源等目录。

总结

我们已经构建了 Instagram 克隆项目的基础目录结构,接下来会先从 UI 开始,然后再逐步实现其他功能。

参考资料


更多关于Flutter Clean Architecture 实践(1)的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter Clean Architecture 实践(1)的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中实现Clean Architecture(干净架构)的实践是一个复杂但非常值得的过程,它能帮助你构建可维护、可扩展和测试性强的应用。以下是一个简化的示例,展示了如何在Flutter项目中应用Clean Architecture的基本原则。由于篇幅限制,这里将重点展示架构的核心部分,包括数据层(Data Layer)、领域层(Domain Layer)和呈现层(Presentation Layer)的分离。

1. 项目结构

首先,我们需要一个合理的项目结构来支持Clean Architecture。以下是一个可能的目录结构:

my_flutter_app/
├── android/
├── ios/
├── lib/
│   ├── data/
│   │   ├── repositories/
│   │   │   └── user_repository.dart
│   │   ├── datasources/
│   │   │   └── user_data_source.dart
│   │   └── models/
│   │       └── user_model.dart
│   ├── domain/
│   │   ├── entities/
│   │   │   └── user.dart
│   │   ├── usecases/
│   │   │   └── get_user_usecase.dart
│   │   └── repositories/
│   │       └── user_repository.dart (Interface)
│   ├── presentation/
│   │   ├── blocs/
│   │   │   └── user_bloc.dart
│   │   ├── pages/
│   │   │   └── user_page.dart
│   │   ├── widgets/
│   │   │   └── user_widget.dart
│   │   └── viewmodels/
│   │       └── user_viewmodel.dart
│   ├── main.dart
└── pubspec.yaml

2. 数据层(Data Layer)

数据层负责数据的获取和持久化。我们定义一个简单的UserDataSource和一个UserRepository

user_data_source.dart

class UserDataSource {
  Future<UserModel> fetchUserById(String id) async {
    // 模拟数据获取
    return UserModel(id: id, name: "John Doe", email: "john.doe@example.com");
  }
}

user_repository.dart

class UserRepositoryImpl implements UserRepository {
  final UserDataSource dataSource;

  UserRepositoryImpl(this.dataSource);

  @override
  Future<UserModel> getUserById(String id) {
    return dataSource.fetchUserById(id);
  }
}

3. 领域层(Domain Layer)

领域层定义了业务逻辑和实体。我们定义一个User实体和一个GetUserUseCase

user.dart

class User {
  final String id;
  final String name;
  final String email;

  User({required this.id, required this.name, required this.email});
}

get_user_usecase.dart

class GetUserUseCase {
  final UserRepository userRepository;

  GetUserUseCase(this.userRepository);

  Future<User> execute(String id) async {
    UserModel userModel = await userRepository.getUserById(id);
    return User(id: userModel.id, name: userModel.name, email: userModel.email);
  }
}

4. 呈现层(Presentation Layer)

呈现层负责UI逻辑和展示。我们使用BLoC模式来管理状态和事件。

user_bloc.dart

import 'package:flutter_bloc/flutter_bloc.dart';
import 'get_user_usecase.dart';

class UserState {
  final User? user;
  final bool isLoading;
  final String? error;

  UserState({this.user, this.isLoading = false, this.error});
}

class UserEvent {
  final String userId;

  UserEvent(this.userId);
}

class UserBloc extends Bloc<UserEvent, UserState> {
  final GetUserUseCase getUserUseCase;

  UserBloc(this.getUserUseCase) : super(UserState(isLoading: true));

  @override
  Stream<UserState> mapEventToState(UserEvent event) async* {
    try {
      User user = await getUserUseCase.execute(event.userId);
      yield UserState(user: user, isLoading: false);
    } catch (e) {
      yield UserState(error: e.toString(), isLoading: false);
    }
  }
}

user_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'user_bloc.dart';

class UserPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => UserBloc(GetUserUseCase(UserRepositoryImpl(UserDataSource()))),
      child: Scaffold(
        appBar: AppBar(title: Text('User Info')),
        body: BlocBuilder<UserBloc, UserState>(
          builder: (context, state) {
            if (state.isLoading) {
              return Center(child: CircularProgressIndicator());
            } else if (state.error != null) {
              return Center(child: Text('Error: ${state.error!}'));
            } else {
              return Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text('Name: ${state.user?.name}'),
                    Text('Email: ${state.user?.email}'),
                  ],
                ),
              );
            }
          },
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            BlocProvider.of<UserBloc>(context).add(UserEvent('1'));
          },
          tooltip: 'Fetch User',
          child: Icon(Icons.person),
        ),
      ),
    );
  }
}

以上代码展示了如何在Flutter中实现Clean Architecture的基本框架。通过分离关注点,我们提高了代码的可维护性和可扩展性。当然,这只是一个简单的示例,实际应用中可能需要更复杂的逻辑和更多的组件。

回到顶部