上篇文章讲的是状况管理(传送门)说到了 Flutter BLoC ,比较与原生的 setState()Provider等有哪些优缺点,并结合实际项目写了一个简单的运用,接下来本篇文章来讲 Flutter 大型项目是如何进行分层规划的,费话不多说,直接进入正题哈。

为啥需求分层规划

其实这个没有啥固定答案,或许只是由于某一天看到手里的代码好像屎山一样,如下图,而跟着事务功用的增加,不停的往这上面堆,这个屎山也会益发庞大和紊乱,假如这样继续下去,直到某一天由于一个小小的Bug,你需求花半响的时间来排查问题出在哪里,最终当你觉得问题终于改好了的时候,却不料碰了不该碰的当地,成果就是 fixing 1 bug will create 10 new bugs,乃至程序的溃散。

Flutter大型项目架构:分层规划篇

跟着这种问题的凸显,所以团队里的显眼包A提出了要求团队里的每个人都必须担任完结给自己写的代码增加注释和文档,规范命名等措施,一段时间后,发现代码是规范了,但问题仍然存在,这时候才发现假如工程的架构分层没有做好,再规范的代码和注释也只是在屎山上雕花,治标不治本罢了。

Flutter大型项目架构:分层规划篇

请原谅我打了一个这么俗的比方,但话糙理不糙,那么啥是应用的分层规划呢?

简单的来说,应用的分层规划是一种将应用程序划分为不同层级的办法,每个层级担任特定的功用或责任。其间表示层(Presentation Layer)担任用户界面和用户交互,将数据呈现给用户并接纳用户输入;事务逻辑层(Business Logic Layer)处理应用程序的事务逻辑,包含数据验证、处理和转化;数据拜访层(Data Access Layer)担任与数据存储交互,包含数据库或文件体系的读取和写入操作。

Flutter大型项目架构:分层规划篇

这样做有什么优点呢?一句话总结就是为了让代码层级责任明晰,保护、扩展和重用便利,每个模块能独立开发、测验和修改。

App 原生开发的分层规划

说到 iOSAndroid 的分层规划,就会想到如 MVCMVVM 等,它们主要是围绕着控制器层(Controller)、视图层(View)、和数据层(Model),还有衔接 ViewModel 之间的模型视图层(ViewModel)这些来讲的。

Flutter大型项目架构:分层规划篇

然而,MVCMVVM 概念还不算完整的分层架构,它们只是重视的 App 分层规划傍边的应用层(Applicaiton Layer)组织方式,对于一个简单规划较小的App来说,可能单单一个应用层就能搞定,不用忧虑事务增量和复杂度上升对后期开发的压力,而一旦 App 上了规划之后就有点应付不过来了。

App 有了必定规划之后,必然会涉及到分层的规划,还有模块化、Hybrid 机制、数据库、跨项目开发等等,拿 iOS 的原生分层规划落地实践来说,通常会将工程拆分成多个Pod私有库组件,拆分的规范视情况而定,每一个分层组件是独立的开发和测验,再在主工程增加 Pod 私有库依赖来做分层规划开发。

此处应该有 Pod 分层组件化规划的配图,但是太懒了,就没有一个个的去建立新项目和 Pod 私有库,不过 iOS 原生分层规划不是本篇文章的重点,本篇主要议论的是 Flutter App 的分层规划。

Flutter 的分层规划

分层架构规划的理念其实是相通的,不同在于语言的特性和详细项目实施上,Flutter 项目也是如此。试想一下,当各种逻辑混合在一次的时候,即便是挑选了像 Bloc 这样的状况管理结构来隔离视图层和逻辑完成层,也很难轻松的增强代码的拓展性,这时候挑选采用一个洁净的分层架构就显得尤为重要,怎样做到这一点呢,就需求将代码分成独立的层,并依赖于笼统而不是详细的完成。

Flutter大型项目架构:分层规划篇

Flutter App 想要完成分层规划,就不得不说到包管理工具,假如在将一切分层组件代码放在主工程里面,那样并不能到达每个组件独自开发、保护和测验的意图,而假如放在新建的 Dart Package 中,没发跨多个组件改代码和测验,无法完成本地包链接和装置。运用 melos 就能处理这个问题,类似于 iOS 包管理工具 Pod, 而 melosFlutter 项意图包管理工具。

组件包管理工具

  1. 装置 Melos,将 Melos 装置为全局包,这样整个体系环境都可以运用: dartpubglobalactivatemelos

  2. 创立 workspace 文件夹,我这儿命名为 flutter_architecture_design,增加 melos 的配置文件melos.yamlpubspec.yaml,其目录结构大概是这样的:

    flutter_architecture_design
    ├──melos.yaml
    ├──pubspec.yaml
    └──README.md
    
  3. 新建组件,以开发工具 Android Studio 为例,挑选 File -> New -> New Flutter Project,根据需求创立组件包,需求留意的是组件包寄存的位置要放在 workspace 目录中。

    Flutter大型项目架构:分层规划篇

  4. 修改 melos.yaml 配置文件,将上一步新建的组件包名放在 packages 之下,增加 scripts 相关指令,其意图请看下一步:

    name:flutter_architecture_design
    packages:
    -widgets/**
    -shared/**
    -data/**
    -initializer/**
    -domain/**
    -resources/**
    -app/**
    command:
    bootstrap:
    usePubspecOverrides:true
    scripts:
    analyze:
    run:dartpubglobalrunmelosexec--flutter"flutteranalyze--no-pub--suppress-analytics"
    description:Runanalyze.
    pub_get:
    run:dartpubglobalrunmelosexec--flutter"flutterpubget"
    description:pubget
    build_all:
    run:dartpubglobalrunmelosexec--depends-on="build_runner""flutterpackagespubrunbuild_runnerbuild--delete-conflicting-outputs"
    description:build_runnerbuildallmodules.
    build_data:
    run:dartpubglobalrunmelosexec--fail-fast--scope="*data*"--depends-on="build_runner""flutterpackagespubrunbuild_runnerbuild--delete-conflicting-outputs"
    description:build_runnerbuilddatamodule.
    build_domain:
    run:dartpubglobalrunmelosexec--fail-fast--scope="*domain*"--depends-on="build_runner""flutterpackagespubrunbuild_runnerbuild--delete-conflicting-outputs"
    description:build_runnerbuilddomainmodule.
    build_app:
    run:dartpubglobalrunmelosexec--fail-fast--scope="*app*"--depends-on="build_runner""flutterpackagespubrunbuild_runnerbuild--delete-conflicting-outputs"
    description:build_runnerbuildappmodule.
    build_shared:
    run:dartpubglobalrunmelosexec--fail-fast--scope="*shared*"--depends-on="build_runner""flutterpackagespubrunbuild_runnerbuild--delete-conflicting-outputs"
    description:build_runnerbuildsharedmodule.
    build_widgets:
    run:dartpubglobalrunmelosexec--fail-fast--scope="*widgets*"--depends-on="build_runner""flutterpackagespubrunbuild_runnerbuild--delete-conflicting-outputs"
    description:build_runnerbuildsharedmodule.
    
  5. 打开指令行,切换到 workspace 目录,也就是 flutter_architecture_design 目录,执行指令。

    melosbootstrap
    

    呈现 SUCCESS 之后,现在的目录结构是这样的:

    Flutter大型项目架构:分层规划篇

  6. 点击 Android Studioadd configuration,将下图中的 Shell Scripts 选中后点击 OK

    Flutter大型项目架构:分层规划篇

    以上的 Scripts 增加完后就可以在这儿看到了,操作起来也很便利,不需求去指令行那里执行指令。

    Flutter大型项目架构:分层规划篇

Flutter 分层规划实践

接下来介绍一下上面创立的几个组件库。

  • app:项意图主工程,寄存事务逻辑代码、 UI 页面和 Bloc,还有 stylescolors 等等。
  • domain:实体类(entity)组件包,还有一些接口类,如 repositoryusercase等。
  • data:数据供给组件包,主要有:api_requestdatabaseshared_preference等,该组件包一切的调用完成都在 domain 中接口 repository 的完成类 repository_impl 中。
  • shared:工具类组件包,包含:utilhelperenumconstantsexceptionmixins等等。
  • resources:资源类组件包,有intl、公共的images
  • initializer:模块初始化组件包。
  • widgets:公共的 UI 组件包,如常用的:alertbuttontoastslider 等等。

它们之间的调用联系如下图:

Flutter大型项目架构:分层规划篇

其间 sharedresources 作为基础组件包,本身不依赖任何组件,而是给其它组件包供给支持。

作为主工程 App 也不会直接依赖 data 组件包,其调用是通过 domain 组件包中 UseCase 来完成,在 UseCase 会获取数据、处理列表数据的分页、参数校验、异常处理等等,获取数据是通过调用笼统类 repository 中相关函数,而不是直接调用详细完成类,此时Apppubspec.yaml 中配置是这样的:

name:app
description:AnewFlutterproject.
publish_to:'none'#Removethislineifyouwishtopublishtopub.dev
version:1.0.0+1
environment:
sdk:">=2.17.0<3.0.0"
dependencies:
flutter:
sdk:flutter
cupertino_icons:^1.0.2
widgets:
path:../widgets
shared:
path:../shared
domain:
path:../domain
resources:
path:../resources
initializer:
path:../initializer
dev_dependencies:
flutter_test:
sdk:flutter
flutter:
uses-material-design:true
generate:false
assets:
-assets/images/

供给的数据组件包 data 完成了笼统类 repository 中相关函数,只担任调用 Api 接口获取数据,或许从数据库获取数据。当上层调用的时候不需求关怀数据是从哪里来的,全部交给 data 组件包担任。

initializer 作为模块初始化组件包,仅有一个 AppInitializer 类,其主要意图是将其它的模块的初始化收集起来放在 AppInitializer 类中 init() 函数中,然后在主工程入口函数:main() 调用这个 init() 函数,常见的初始化如:GetIt 初始化、数据库 objectbox 初始化、SharedPreferences初始化,这些相关的初始会散布在各自的组件包中。

classAppInitializer{
AppInitializer();
Future<void>init()async{
awaitSharedConfig.getInstance().init();
awaitDataConfig.getInstance().init();
awaitDomainConfig.getInstance().init();
}
}

widgets 作为公共的 UI 组件库,不处理事务逻辑,在多项目开发时经常会运用到。上图中的 Other Plugin Module 指的的是其它组件包,特别是需求独自开发与原生交互的插件时会用到,

这种分层规划出来的架构或许在开发过程中带来一下不便,如调用一个接口,第一步:需求先在笼统类 repository 写好函数声明;第二步:然后再去Api Service 写详细恳求代码,并在repository_impl 完成类中调用;第三步:还需求在 UserCase 去做事务调用,错误处理等;最终一步:在blocevent中调用。这么一趟下来,确实有些繁琐或许说是过度规划。但是假如维度设定在大的项目中多人合作开发的时候,却能规避很多问题,每个分层组件都有自己的责任互不干扰,都支持独自的开发测验,尽可能的做到依赖于笼统而不是详细的完成。

本篇文章就到这儿,源码后边这个系列的文章里放出来,感谢您的阅览,也希望您能重视我的公众号“Flutter技能实践”,原创不易,您的重视是我更新下去最大的动力。