Flutter 项目架构技术指南

Flutter 项目架构技术指南

视频

https://www.bilibili.com/video/BV1rx4y127kN/

前言

原文 https://ducafecat.com/blog/flutter-clean-architecture-guide

探讨Flutter项目代码组织架构的关键方面和建议。了解设计原则SOLID、Clean Architecture,以及架构模式MVC、MVP、MVVM,如何有机结合使用,打造优秀的应用架构。

参考

https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

https://developer.mozilla.org/en-US/docs/Glossary/MVC

https://en.wikipedia.org/wiki/Model–view–presenter

https://zh.wikipedia.org/zh-hant/MVVM

SOLID 原则

SOLID(单一功能、开闭原则、里氏替换、接口隔离以及依赖反转)是由罗伯特·C·马丁在21世纪早期引入,指代了面向对象编程和面向对象设计的五个基本原则。

在 Flutter 中遵循 SOLID 设计原则具有重要性,因为这些原则有助于提高代码质量、可维护性和可扩展性,同时降低代码的复杂度和耦合度。

  1. 单一职责原则 (Single Responsibility Principle)

    每个类应该只有一个责任。在 Flutter 中,您可以将不同功能拆分为不同的小部件(widget),每个小部件负责特定的 UI 展示或交互逻辑。

    dart 复制代码
    // 单一职责原则示例:一个负责显示用户信息的小部件
    
    class UserInfoWidget extends StatelessWidget {
      final User user;
    
      UserInfoWidget(this.user);
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Text('Name: ${user.name}'),
            Text('Age: ${user.age}'),
          ],
        );
      }
    }
  2. 开闭原则 (Open/Closed Principle)

    软件实体应该对扩展开放,对修改关闭。在 Flutter 中,您可以通过使用组合、继承和多态来实现这一原则。例如,通过创建可重用的小部件并根据需要进行扩展,而不是直接修改现有代码。

    dart 复制代码
    // 开闭原则示例:通过继承实现可扩展的主题切换功能
    
    abstract class Theme {
      ThemeData getThemeData();
    }
    
    class LightTheme extends Theme {
      @override
      ThemeData getThemeData() {
        return ThemeData.light();
      }
    }
    
    class DarkTheme extends Theme {
      @override
      ThemeData getThemeData() {
        return ThemeData.dark();
      }
    }
  3. 里氏替换原则 (Liskov Substitution Principle)

    子类应该能够替换其父类并保持行为一致。在 Flutter 中,确保子类可以替换父类而不会引起意外行为是很重要的。继承关系应该是 is-a 的关系,而不是 has-a 的关系。

    dart 复制代码
    // 里氏替换原则示例:确保子类可以替换父类而不引起问题
    
    abstract class Shape {
      double getArea();
    }
    
    class Rectangle extends Shape {
      double width;
      double height;
    
      @override
      double getArea() {
        return width * height;
      }
    }
    
    class Square extends Shape {
      double side;
    
      @override
      double getArea() {
        return side * side;
      }
    }
  4. 接口隔离原则 (Interface Segregation Principle)

    客户端不应该被迫依赖它们不使用的接口。在 Flutter 中,您可以根据需要创建多个接口,以确保每个接口只包含客户端所需的方法。

    dart 复制代码
    // 接口隔离原则示例:将接口细分为更小的接口
    
    abstract class CanFly {
      void fly();
    }
    
    abstract class CanSwim {
      void swim();
    }
    
    class Bird implements CanFly {
      @override
      void fly() {
        print('Bird is flying');
      }
    }
    
    class Fish implements CanSwim {
      @override
      void swim() {
        print('Fish is swimming');
      }
    }
  5. 依赖反转原则 (Dependency Inversion Principle)

    高层模块不应该依赖于低层模块,二者都应该依赖于抽象。在 Flutter 中,您可以通过依赖注入、接口抽象等方式实现依赖反转,以减少模块之间的耦合度。

    dart 复制代码
    // 依赖反转原则示例:通过依赖注入实现依赖反转
    
    class UserRepository {
      Future<User> getUser() async {
        // Fetch user data from API
      }
    }
    
    class UserBloc {
      final UserRepository userRepository;
    
      UserBloc(this.userRepository);
    
      Future<void> fetchUser() async {
        User user = await userRepository.getUser();
        // Process user data
      }
    }

Clean Architecture 原则

在 Flutter 开发中,Clean Architecture(CA)清晰架构是一种软件架构设计模式,旨在将应用程序分解为不同的层级,每一层级都有明确定义的职责,以实现代码的可维护性、可测试性和可扩展性。Clean Architecture 通过明确定义各层之间的依赖关系,将业务逻辑与框架、库和外部依赖分离开来,从而使代码更加灵活和独立。

示例中其中包括实体层、数据层、领域层和表示层。

实体层(Entities):

dart 复制代码
// 实体类
class User {
  final String id;
  final String name;

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

数据层(Data Layer):

dart 复制代码
// 数据接口
abstract class UserRepository {
  Future<User> getUserById(String userId);
}

// 数据实现
class UserRepositoryImpl implements UserRepository {
  @override
  Future<User> getUserById(String userId) {
    // 实现获取用户逻辑
  }
}

领域层(Domain Layer):

dart 复制代码
// 用例类
class GetUserByIdUseCase {
  final UserRepository userRepository;

  GetUserByIdUseCase(this.userRepository);

  Future<User> execute(String userId) {
    return userRepository.getUserById(userId);
  }
}

表示层(Presentation Layer):

dart 复制代码
// Flutter 页面
class UserPage extends StatelessWidget {
  final GetUserByIdUseCase getUserByIdUseCase;

  UserPage(this.getUserByIdUseCase);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('User Page'),
      ),
      body: Center(
        child: FutureBuilder<User>(
          future: getUserByIdUseCase.execute('1'),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return Text('User: ${snapshot.data!.name}');
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            }
            return CircularProgressIndicator();
          },
        ),
      ),
    );
  }
}

架构模式

软件架构模式,用于组织代码、分离关注点以及提高代码的可维护性和可测试性。常见模式有 Model-View-Controller(模型-视图-控制器)、Model-View-Presenter(模型-视图-展示器)和Model-View-ViewModel(模型-视图-视图模型)。

1. MVC(Model-View-Controller):

  • 模型(Model):代表应用程序的数据和业务逻辑。
  • 视图(View):负责展示数据给用户以及接收用户输入。
  • 控制器(Controller):处理用户输入、更新模型和视图之间的关系。

在 MVC 中,视图和控制器之间通过双向通信进行交互,控制器负责更新模型和视图。MVC 帮助将应用程序分解为三个独立的部分,以便更好地管理代码逻辑。

Model:
dart 复制代码
class UserModel {
  String id;
  String name;

  UserModel({required this.id, required this.name});
}
View:
dart 复制代码
class UserView extends StatelessWidget {
  final UserModel user;

  UserView(this.user);

  @override
  Widget build(BuildContext context) {
    return Text('User: ${user.name}');
  }
}
Controller:
dart 复制代码
class UserController {
  UserModel user = UserModel(id: '1', name: 'John Doe');
}

IOS 就是典型的 MVC 模式,通过事件触发控制器最后内部机制更新视图

2. MVP(Model-View-Presenter):

  • 模型(Model):同样代表应用程序的数据和业务逻辑。
  • 视图(View):负责展示数据给用户以及接收用户输入。
  • 展示器(Presenter):类似于控制器,负责处理用户输入、更新模型和更新视图。

在 MVP 中,视图和展示器之间通过接口进行通信,展示器负责从模型获取数据并更新视图。MVP 将视图和模型解耦,使得更容易进行单元测试和维护。

Model:

同上

View:
dart 复制代码
class UserView extends StatelessWidget {
  final UserModel user;
  final UserPresenter presenter;

  UserView(this.user, this.presenter);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('User: ${user.name}'),
        ElevatedButton(
          onPressed: () {
            presenter.updateUserName();
          },
          child: Text('Update Name'),
        ),
      ],
    );
  }
}
Presenter:
dart 复制代码
class UserPresenter {
  UserModel user = UserModel(id: '1', name: 'John Doe');
  UserView view;

  UserPresenter(this.view);

  void updateUserName() {
    user.name = 'Jane Smith';
    view.updateView(user);
  }
}

Presenter 中有视图方法来更新

3. MVVM(Model-View-ViewModel):

  • 模型(Model):同样代表应用程序的数据和业务逻辑。
  • 视图(View):负责展示数据给用户以及接收用户输入。
  • 视图模型(ViewModel):连接视图和模型,负责处理视图逻辑、数据绑定以及与模型的交互。

在 MVVM 中,视图模型充当了视图和模型之间的中介,负责处理大部分视图逻辑,同时通过数据绑定将视图与模型连接起来。MVVM 的目标是将视图的状态和行为与业务逻辑分离,以实现更好的可维护性和可测试性。

Model:

同上

View:
dart 复制代码
class UserView extends StatelessWidget {
  final UserViewModel viewModel;

  UserView(this.viewModel);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('User: ${viewModel.user.name}'),
        ElevatedButton(
          onPressed: () {
            viewModel.updateUserName();
          },
          child: Text('Update Name'),
        ),
      ],
    );
  }
}
ViewModel:
dart 复制代码
class UserViewModel {
  UserModel user = UserModel(id: '1', name: 'John Doe');

  void updateUserName() {
    user.name = 'Jane Smith';
    notifyListeners();
  }
}

与 MVP 最大的区别是 MVVM 可以同时更新多个视图

Packages 优秀插件

freezed

https://pub-web.flutter-io.cn/packages/freezed

一个用于数据类 / 联合体 / 模式匹配 / 克隆的代码生成器。

详见 <flutter freezed json 转 model 代码生成> https://ducafecat.com/blog/flutter_application_freezed

get_it

https://pub-web.flutter-io.cn/packages/get_it

依赖管理工具包 懒加载、单例、依赖注入、作用域、注入管理... 。

详见 <在 getx 中使用 get_it 管理依赖注入> https://ducafecat.com/blog/use-get_it-in-getx

Equatable

https://pub-web.flutter-io.cn/packages/equatable

equatable 可以帮助开发人员轻松地重写类的 ==hashCode 方法,从而简化对象之间的相等性比较。

equatable 可以与状态管理、数据模型等方面结合使用,帮助开发人员更轻松地处理对象的相等性比较。

状态管理

  • Provider
  • Bloc
  • GetX
  • Riverpod

详见 <盘点主流 Flutter 状态管理库2024>https://ducafecat.com/blog/flutter-state-management-libraries-2024

小结

本文探讨了Flutter项目代码组织架构的关键方面,包括设计原则SOLID、Clean Architecture,以及架构模式MVC、MVP、MVVM的有机结合。通过本文的指导和建议,读者可以更好地了解如何打造优秀的Flutter应用架构,提高代码可维护性和扩展性。务必在实际项目中灵活运用这些架构原则,为应用的长期发展奠定坚实基础。

感谢阅读本文

如果有什么建议,请在评论中让我知道。我很乐意改进。


© 猫哥

ducafecat.com

end

相关推荐
javaDocker1 小时前
业务架构、数据架构、应用架构和技术架构
架构
Jinkey2 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
JosieBook3 小时前
【架构】主流企业架构Zachman、ToGAF、FEA、DoDAF介绍
架构
.生产的驴3 小时前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
丁总学Java4 小时前
ARM 架构(Advanced RISC Machine)精简指令集计算机(Reduced Instruction Set Computer)
arm开发·架构
ZOMI酱6 小时前
【AI系统】GPU 架构与 CUDA 关系
人工智能·架构
Summer不秃6 小时前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰6 小时前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
sunly_6 小时前
Flutter:AnimatedSwitcher当子元素改变时,触发动画
flutter