# Flutter 工程化框架选择 — 搞定 UI 生产力 ``` 本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究! ``` 这是 《Flutter 工程化框架选择》 系列的第二篇 ,就像之前说的,这个系列只是单纯告诉你,创建一个 Flutter 工程,或者说搭建一个 Flutter 工程脚手架,应该如何快速选择适合自己的功能模块,或者说这是一个指引系列,所以比较适合新手同学。 本篇主要介绍 UI 相关的,**但是完全单纯介绍 UI 好像又有点水,那就是加一些小知识来吸吸水分吧**。 做为前端开发,我们的大部分职责就是开发 UI ,但是如果有人帮我们提前把 UI 做好,那岂不美哉?事实上很多时候 UI 确实是可以模版化,而前端领域也一直是这样,例如 `Ant Design` 、`Element-UI` 等 ,那 Flutter 上是否也有这样的支持? 答案肯定是有的,但是在介绍它们之前,**我们先聊一个 Flutter UI 的问题:嵌套**。 > 为了不太水,我们前言聊技术,后半部分推荐项目。 # 前言- UI 嵌套 谈到 Flutter 肯定就有人说嵌套,是的, Flutter 本身就是通过直接嵌套 `Widget` 来渲染 UI , 所以大家可能就会吐槽类似下面的代码,虽然这段代码没有意义,但是这里我们要先思考两个问题: - **Flutter 怕不怕 `Widget `嵌套影响性能**? - **Flutter 有没有办法解决嵌套**? ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image1.png) **首先第一点就是,Flutter 一般情况下不怕嵌套影响性能,因为 “众所周知” 的原因,Flutter 里的 `Widget` 并不是真正的控件,我更愿意说 `Widget` 是配置文件,真正的绘制和布局对象是它背后的 `RenderObejct` 等相关逻辑**。 > 嵌套层级看起来很多的原因,是因为 `Widget` 颗粒度太细。 当然这个还要看你嵌套的 `Widget` 做了什么?或者说是嵌套的 `Widget` 的 `RenderObject` 做了什么,例如: - `Padding` 的 `RenderPadding` 就是在 layout 时多了一个 `Size` 和 `childParentData.offset` 计算 - `ColoredBox` 的 `_RenderColoredBox` 就是多一句 `drawRect` - `Align` 的 `RenderPositionedBox` 就是多计算一个 `childParentData.offset` 所以这些 `Widget` 的颗粒度很细,但是也很轻,我们直接用的时候可能会一层一层嵌套,看起来不美观,但是实际渲染时对性能影响不大。 > **当然,并不是所有的 `Widget` 都很轻不怕嵌套**,例如 `Clip` 、`Transform` 和 `Opacity ` 等,如果涉及到 `pushLayer` 等操作时,在需要做图层合成的时候,那确实对性能影响还是比较大的。 **那第二个问题,如何解决嵌套?这时候你就需要 “配置模版” ,通过封装来优化代码结构**。 “配置模版”是什么意思?举个例子 : `Container` 应该用过吧? `Container` 其实就是官方给大家准备的 “模版” ,它本身只是一个 `StatelessWidget` ,也就是一个没有 `RenderObject` 的 `Widget` ,它靠的就是把各种功能的 `Widget ` 组合起来使用,如下图就是使用 `Container` 的对比情况。 | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image2.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image3.png) | | ----------------------------------------------------------- | ----------------------------------------------------------- | 所以在使用 Flutter 构建 UI 时,就可以谈及到两个点: - `Widget` 是配置文件,它一般很轻,不怕嵌套,真正绘制渲染的是它背后的 `RenderObject` - 通过各种 UI 配置模版来解决嵌套,特别是抽象出适合自己业务逻辑的 UI 模版 举个例子,如下方的第三方开源布局包 [dashboard](https://github.com/Mehmetyaz/dashboard) ,在这种复杂的 UI 布局上难道就靠直接一级一级嵌套 `Column` 和 `Row` 来解决?答案肯定不是! | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image4.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image5.png) | | --------------------------------------------- | ----------------------------------------------------------- | [dashboard](https://github.com/Mehmetyaz/dashboard) 的是通过 `Stack` + `Positioned` 组成模版,在手势移动时计算,通过 `AnimatedBuilder` 实现动画偏移,自动计算控件位置。 > 其实也可以直接用 `AnimatedPositioned` ,具体可见 [《Flutter 小技巧之有趣的动画技巧》](https://juejin.cn/post/7111071430292275213) 所以可以看到, **Flutter 里你其实可以不那么嵌套,具体还是看你的封装方式**,除了这样,还有就是直接在 `RenderObject` 上进行自定义布局,比如下方这两个例子: | [cloud](https://github.com/CarGuo/gsy_flutter_demo/tree/master/lib/widget/cloud) | [custom_multi_render](https://github.com/CarGuo/gsy_flutter_demo/blob/master/lib/widget/custom_multi_render_demo_page.dart) | | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![image-20220922120414245](http://img.cdn.guoshuyu.cn/20220923_Z3/image6.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image7.png) | 你说上面的布局用 `Stack` + `Positioned` 的模式能不能做?肯定是可以,但是在 `RenderObject` 层实现不是更优雅吗?**把脏活累活写在 `RenderObject` , 通过 `Widget` 提供配置接口,这样就不会一上来就向用户露 “底裤” 不是么**。 > 所以,把眼界打开,不要什么都盯着 `Widget` 嵌套,往 `RenderObject` 层面探索,你会发现 Flutter 其实不像表面那么浮躁,再不济来尝试下 `CustomMultiChildLayout` ,它也可以帮助你解决一些复杂布局下的嵌套问题。 当然,还有一些项目另辟蹊径,**比如 [niku](https://github.com/SaltyAom/niku) ,这个项目通过 `typedef` 和抽象拓展,利用语法对官方控件进行二次封装**,实现创建了一个 “非正道” 的 UI 配置效果,具体如下图所示,喜不喜欢就看个人爱好了,但是它确实一定程度解决了嵌套可视化的问题。 ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image8.png) > 作者是个二次元,但是看地址他应该是泰国哥们 当然,我们这一期的关键是提高 UI 生产力,单说源码实现就没劲了,所以重点让我们看后半部分。 # UI 套件 在前端领域,**使用统一的 UI 套件可以加快开发的节奏,减少开发和设计之间的摩擦,而且风格统一**。一般情况下,在企业内部都是在不知不觉中沉淀下来各种组件,最后形成组件池,从而落地成 UI 套件。 比如**贝壳的 [bruno](https://github.com/LianjiaTech/bruno) ,我愿意称它为 Flutter 界的 `Element-UI`** ,目前已经支持到 Flutter 3 ,作为少有国内大厂维护的 Flutter UI 项目,甚至它还提供了 [sketch 设计指引](https://bruno.ke.com/page/guide/sketch) 和 [设计物料下载](https://bruno.ke.com/download/sketch) 。 | [bruno](https://github.com/LianjiaTech/bruno) | [getwidget](https://github.com/ionicfirebaseapp/getwidget) | [fsuper](https://github.com/Fliggy-Mobile/fsuper) | | ----------------------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image9.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image10.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image11.png) | 当然,除了 bruno 之后,像 [getwidget](https://github.com/ionicfirebaseapp/getwidget) 、 [fsuper](https://github.com/Fliggy-Mobile/fsuper) 也提供了日常开发中常用的 UI 套件,虽然风格风格上可能并没有 bruno 统一,但是还是可以在一定程度提高开发的生产力。 > 事实上对于个人开发者来说,这种套件可以解决很多设计上的问题。 **另外聊到 Flutter UI 套件就要一定要介绍国内的 [fluttercandies](https://github.com/fluttercandies)** 组织,fluttercandies 是由大佬们共同维护的一系列 Flutter 开源项目,记住,是大佬们,并且一直在持续更新: - [extended_image](https://pub.flutter-io.cn/packages/extended_image) - [extended_nested_scroll_view](https://pub.flutter-io.cn/packages/extended_nested_scroll_view) - [extended_text](https://pub.flutter-io.cn/packages/extended_text) - [extended_text_field](https://pub.flutter-io.cn/packages/extended_text_field) - [extended_list](https://pub.flutter-io.cn/packages/extended_list) - [extended_sliver](https://pub.flutter-io.cn/packages/extended_sliver) - [extended_tabs](https://pub.flutter-io.cn/packages/extended_tabs) - [wechat_assets_picker](https://pub.flutter-io.cn/packages/wechat_assets_picker) - [waterfall_flow](https://pub.flutter-io.cn/packages/waterfall_flow) > 举个例子,如果 flutter framework 短期不能解决的问题,那就大佬就会 cv 一份控件自己维护,这就是 fluttercandies 的节奏和优势。 ## PC 既然介绍 Flutter UI ,就不得不介绍 PC 相关的风格的 UI ,因为 Flutter 不只是 Android 和 iOS ,它还支持 Web 和 PC, 所以类似 PC 的 UI 风格也值得推荐,**比如 ant_design_flutter 就是一个很有意思的项目**。 | [fluent_ui](https://github.com/bdlukaa/fluent_ui) | [macos_ui](https://github.com/GroovinChip/macos_ui) | [ant_design_flutter](https://github.com/CalsRanna/ant_design_flutter) | | ----------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image12.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image13.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image14.png) | ## Responsive 那有的人可能就说,**我想要一套代码适配多平台的屏幕尺寸**行不行?答案肯定是可以的,下面这几个 package 就提供了不同屏幕尺寸下一套代码的动态适配方案,我个人可能会比较喜欢 ResponsiveFramework 。 | [ResponsiveFramework](https://github.com/Codelessly/ResponsiveFramework) | [responsive_sizer](https://github.com/CoderUni/responsive_sizer/) | [flutter_adaptive_ui](https://github.com/mohammadtaherri/flutter_flexible_ui/tree/main/packages/flutter_adaptive_ui) | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image15.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image16.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image17.png) | ## Appbar 这类 Appbar 的实现其实是我被问过最多的,其实它的核心实现都是 Sliver ,严格意义上我觉得并不需要第三方库,自己用 Sliver 就可以实现,但是本着能不动手就不动手原则,也推荐几个库吧: | [draggable_home](https://github.com/4-alok/draggable_home) | [extended_sliver](https://github.com/fluttercandies/extended_sliver) | [scroll_app_bar](https://github.com/edsonbonfim/scroll_bars/tree/master/scroll_app_bar) | | ---------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image18.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image19.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image20.gif) | > [gsy_flutter_demo](https://github.com/CarGuo/gsy_flutter_demo) 里也提供了几种实现思路,其实并不复杂。 ## Drawer 可能有人会觉得,不会吧不会吧, Drawer 也需要第三方库? 还真有,因为有时候可能需要不一样的动画效果,另外这里的 `sidebarx` ,也和官方提供的 `NavigationRail`有异曲同工之妙,能在 UI 上适配多平台的操作习惯。 | [sidebarx](https://github.com/Frezyx/sidebarx) | [flutter_advanced_drawer](https://github.com/alex-melnyk/flutter_advanced_drawer) | [curved_drawer](https://github.com/undrbridge/curved_drawer) | | ---------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image21.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image22.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image23.GIF) | ## Tarbar 既然都说到 `Drawer `,那 `Tabbar` 也提供几个花里胡哨的动画效果,主要是切换时的动画效果,另外 `tab_container` 可能算是比较有意思的库,用的 `Path` 来编绘背景动画效果。 | [flutter-cupertino-tabbar](https://github.com/aliyigitbireroglu/flutter-cupertino-tabbar) | [tab_indicator_styler](https://github.com/adar2378/tab_indicator_styler) | [tab_container]( https://github.com/sourcemain/tab_container) | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image24.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image25.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image26.gif) | ## BottomBar 说到 `Tabbar` 相对应的还有 BottomBar 相关,这里也提供几个库,主要是动画效果很有趣,我个人还是挺喜欢这种曲线的动画效果。 | [curved_navigation_bar](https://github.com/rafalbednarczuk/curved_navigation_bar) | [salomon_bottom_bar](https://github.com/lukepighetti/salomon_bottom_bar) | [bubble_bottom_bar](https://github.com/westdabestdb/bubble_bottom_bar) | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image27.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image28.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image29.gif) | ## 指引 启动指引这个需求,正常情况下一个 `PageView` 就可以满足产品经理的场景,但是有时候可能会需要你来“亿”点点动画效果来增加 KPI,所示拿着也许就对你有用了,当然你也可以把它当作 `PageView` 动画来使用。 | [concentric_transition](https://pub.dev/packages/concentric_transition) | [nice_intro](https://github.com/Ethiel97/nice_intro) | [intro_views_flutter](https://github.com/aagarwal1012/IntroViews-Flutter) | | ------------------------------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image30.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image31.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image32.gif) | ## 角标 这个应该无需多言了,基本上 App 都会需要用到,这两个库基本覆盖了 90% 的场景 | [flutter_badges](https://github.com/yadaniyil/flutter_badges) | [corner_decoration](https://github.com/kalaganov/corner_decoration) | | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image33.png) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image34.png) | ## 动画按键 这个可能一般情况下大家都不需要这么花里胡哨的效果,但是万一呢?Material 风格上这种交互还是挺多的,不过国内对 Material 确实不是很感冒。 | [flutter_animated_button](https://github.com/NikhilVadoliya/FlutterAnimatedButton) | [progress_state_button](https://github.com/slm/progress-state-button) | | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image35.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image36.gif) | ## 头像 没想到吧?头像为什么还需要库? 其实就是下面的这个场景,相信这个场景可能大家都不会陌生,有社交需求的时候,经常会存在这样的 UI ,掘金沸点不也有类似 UI 么? | [avatar_stack](https://github.com/cyrax111/avatar_stack) | [overflow_view](https://github.com/letsar/overflow_view) | | | -------------------------------------------------------- | -------------------------------------------------------- | ---- | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image37.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image38.gif) | | ## swipe 卡片 这个需求可能一般人不会需要,推荐它是因为我还记得几年的时候,收了 1000 给人做了这样的一个外包,就是做一个这样的控件。 | [swipe_deck](https://github.com/retroportalstudio/swipe_deck.git) | [swipeable_card_stack](https://github.com/codetoart/cta-flutter-tinder-card-animation) | [appinio_swiper](https://github.com/appinioGmbH/flutter_packages/tree/main/packages/appinio_swiper) | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image39.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image40.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image41.gif) | ## bottom sheet 这其实更多是一个 Route 相关的动画效果,感觉好像国内也不常用到,但是之前确实有好几次咨询有没有类似的实现。 | [we_slide](https://github.com/luciano-work/we_slide) | [sliding_up_panel](https://github.com/akshathjain/sliding_up_panel) | | ---------------------------------------------------- | ------------------------------------------------------------ | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image42.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image43.gif) | ## 时间轴 UI 这是我在群里被问过好多次的一个需求场景,我也不知道为什么那么多应用会需要用到这样的 UI ?不过这类需求自己从头实现确实会比较费事。 | [timeline_tile](https://github.com/JHBitencourt/timeline_tile) | [timelines](https://pub.dev/packages/timelines) | | ------------------------------------------------------------ | ----------------------------------------------- | | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image44.gif) | ![](http://img.cdn.guoshuyu.cn/20220923_Z3/image45.gif) | 好了,关于 Flutter UI 相关的内容推荐就到这里,**本篇主要还是提供给大家如何理解 Flutter 的 UI 布局,并且尽可能去解决嵌套,同时提供一些有意思的第三方 package ,进一步提高大家开发 UI 的生产力**。 最后,如果你还有什么关于 Flutter 工程或者框架的疑问,欢迎留言评论,也许新的素材又有了~