上篇文章讲的是状况管理(传送门)说到了 Flutter BLoC
,比较与原生的 setState()
及Provider
等有哪些优缺点,并结合实际项目写了一个简单的运用,接下来本篇文章来讲 Flutter
大型项目是如何进行分层规划的,费话不多说,直接进入正题哈。
为啥需求分层规划
其实这个没有啥固定答案,或许只是由于某一天看到手里的代码好像屎山一样,如下图,而跟着事务功用的增加,不停的往这上面堆,这个屎山也会益发庞大和紊乱,假如这样继续下去,直到某一天由于一个小小的Bug,你需求花半响的时间来排查问题出在哪里,最终当你觉得问题终于改好了的时候,却不料碰了不该碰的当地,成果就是 fixing 1 bug will create 10 new bugs
,乃至程序的溃散。
跟着这种问题的凸显,所以团队里的显眼包A提出了要求团队里的每个人都必须担任完结给自己写的代码增加注释和文档,规范命名等措施,一段时间后,发现代码是规范了,但问题仍然存在,这时候才发现假如工程的架构分层没有做好,再规范的代码和注释也只是在屎山上雕花,治标不治本罢了。
请原谅我打了一个这么俗的比方,但话糙理不糙,那么啥是应用的分层规划呢?
简单的来说,应用的分层规划是一种将应用程序划分为不同层级的办法,每个层级担任特定的功用或责任。其间表示层(Presentation Layer
)担任用户界面和用户交互,将数据呈现给用户并接纳用户输入;事务逻辑层(Business Logic Layer
)处理应用程序的事务逻辑,包含数据验证、处理和转化;数据拜访层(Data Access Layer
)担任与数据存储交互,包含数据库或文件体系的读取和写入操作。
这样做有什么优点呢?一句话总结就是为了让代码层级责任明晰,保护、扩展和重用便利,每个模块能独立开发、测验和修改。
App
原生开发的分层规划
说到 iOS
、Android
的分层规划,就会想到如 MVC
、MVVM
等,它们主要是围绕着控制器层(Controller
)、视图层(View
)、和数据层(Model
),还有衔接 View
和 Model
之间的模型视图层(ViewModel
)这些来讲的。
然而,MVC
、MVVM
概念还不算完整的分层架构,它们只是重视的 App
分层规划傍边的应用层(Applicaiton Layer
)组织方式,对于一个简单规划较小的App来说,可能单单一个应用层就能搞定,不用忧虑事务增量和复杂度上升对后期开发的压力,而一旦 App
上了规划之后就有点应付不过来了。
当 App
有了必定规划之后,必然会涉及到分层的规划,还有模块化、Hybrid
机制、数据库、跨项目开发等等,拿 iOS
的原生分层规划落地实践来说,通常会将工程拆分成多个Pod
私有库组件,拆分的规范视情况而定,每一个分层组件是独立的开发和测验,再在主工程增加 Pod
私有库依赖来做分层规划开发。
此处应该有 Pod
分层组件化规划的配图,但是太懒了,就没有一个个的去建立新项目和 Pod
私有库,不过 iOS
原生分层规划不是本篇文章的重点,本篇主要议论的是 Flutter App
的分层规划。
Flutter
的分层规划
分层架构规划的理念其实是相通的,不同在于语言的特性和详细项目实施上,Flutter
项目也是如此。试想一下,当各种逻辑混合在一次的时候,即便是挑选了像 Bloc
这样的状况管理结构来隔离视图层和逻辑完成层,也很难轻松的增强代码的拓展性,这时候挑选采用一个洁净的分层架构就显得尤为重要,怎样做到这一点呢,就需求将代码分成独立的层,并依赖于笼统而不是详细的完成。
Flutter App
想要完成分层规划,就不得不说到包管理工具,假如在将一切分层组件代码放在主工程里面,那样并不能到达每个组件独自开发、保护和测验的意图,而假如放在新建的 Dart Package
中,没发跨多个组件改代码和测验,无法完成本地包链接和装置。运用 melos 就能处理这个问题,类似于 iOS
包管理工具 Pod
, 而 melos
是 Flutter
项意图包管理工具。
组件包管理工具
-
装置
Melos
,将Melos
装置为全局包,这样整个体系环境都可以运用: dartpubglobalactivatemelos -
创立
workspace
文件夹,我这儿命名为flutter_architecture_design
,增加melos
的配置文件melos.yaml
和pubspec.yaml
,其目录结构大概是这样的:flutter_architecture_design ├──melos.yaml ├──pubspec.yaml └──README.md
-
新建组件,以开发工具
Android Studio
为例,挑选File
->New
->New Flutter Project
,根据需求创立组件包,需求留意的是组件包寄存的位置要放在workspace
目录中。 -
修改
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.
-
打开指令行,切换到
workspace
目录,也就是flutter_architecture_design
目录,执行指令。melosbootstrap
呈现
SUCCESS
之后,现在的目录结构是这样的: -
点击
Android Studio
的add configuration
,将下图中的Shell Scripts
选中后点击OK
。以上的
Scripts
增加完后就可以在这儿看到了,操作起来也很便利,不需求去指令行那里执行指令。
Flutter
分层规划实践
接下来介绍一下上面创立的几个组件库。
-
app
:项意图主工程,寄存事务逻辑代码、UI
页面和Bloc
,还有styles
、colors
等等。 -
domain
:实体类(entity
)组件包,还有一些接口类,如repository
、usercase
等。 -
data
:数据供给组件包,主要有:api_request
,database
、shared_preference
等,该组件包一切的调用完成都在domain
中接口repository
的完成类repository_impl
中。 -
shared
:工具类组件包,包含:util
、helper
、enum
、constants
、exception
、mixins
等等。 -
resources
:资源类组件包,有intl
、公共的images
等 -
initializer
:模块初始化组件包。 -
widgets
:公共的UI
组件包,如常用的:alert
、button
、toast
、slider
等等。
它们之间的调用联系如下图:
其间 shared
和 resources
作为基础组件包,本身不依赖任何组件,而是给其它组件包供给支持。
作为主工程 App
也不会直接依赖 data
组件包,其调用是通过 domain
组件包中 UseCase
来完成,在 UseCase
会获取数据、处理列表数据的分页、参数校验、异常处理等等,获取数据是通过调用笼统类 repository
中相关函数,而不是直接调用详细完成类,此时App
的 pubspec.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
去做事务调用,错误处理等;最终一步:在bloc
的event
中调用。这么一趟下来,确实有些繁琐或许说是过度规划。但是假如维度设定在大的项目中多人合作开发的时候,却能规避很多问题,每个分层组件都有自己的责任互不干扰,都支持独自的开发测验,尽可能的做到依赖于笼统而不是详细的完成。
本篇文章就到这儿,源码后边这个系列的文章里放出来,感谢您的阅览,也希望您能重视我的公众号“Flutter技能实践”,原创不易,您的重视是我更新下去最大的动力。