赞
踩
虽然 Flutter
中提供的组件众多,但并非所有组件都是复杂的。大部分是 StatelessWidget
和 StatefulWidget
的派生类,在面对这些组件时,我们要清楚地认识一点:
它们的核心功能是基于
已有组件
来封装构建逻辑
,完成特定的功能,简化使用。
比如下面的 BackButtonIcon
组件,继承自 StatelessWidget
。在 build
方法中封装构建逻辑,其中使用 Icon
组件,根据不同的平台,展示不同的图标,如下所示:
android/linux/windows | iOS/macOS |
---|---|
![]() | ![]() |
---->[BackButtonIcon 源码]---- class BackButtonIcon extends StatelessWidget { const BackButtonIcon({ super.key }); static IconData _getIconData(TargetPlatform platform) { switch (platform) { case TargetPlatform.android: case TargetPlatform.fuchsia: case TargetPlatform.linux: case TargetPlatform.windows: return Icons.arrow_back; case TargetPlatform.iOS: case TargetPlatform.macOS: return Icons.arrow_back_ios; } } @override Widget build(BuildContext context) => Icon(_getIconData(Theme.of(context).platform)); }
可能有人会疑惑 :
为什么这么简单的逻辑,还要通过一个组件来完成。会不会有些小题大做?
这难道不是导致 Flutter 组件数量庞大的 “元凶” 吗?
对于编程者来说,写重复代码是很反感的。试想一下,如果不用 BackButtonIcon
来封装这个构建逻辑。那么 每次
想要实现不同平台展示不同返回按钮时,就需要编程者自己处理构建逻辑。如果需要修改构建逻辑,就要一处处去修改。
所以 封装
的魅力在于:集中逻辑处理,统一使用的形式,便于复用。 如果一个构建逻辑相对固定,有使用的场景,就可以通过 StatelessWidget
或 StatefulWidget
进行封装,简化使用。反过来,源码中封装的组件,也一定有其目的或使用场景,我们可以通过这个线索来思考组件的源码设计者的考量。
BackButton
组件继承自 StatelessWidget
,在 build
构建逻辑中使用 IconButton
组件触发点击事件,如果未提供 onPressed
参数,则会触发 Navigator.maybePop
返回。 显示的内容组件为 BackButtonIcon
,说明其会根据平台来决定图标样式。
另外,可以通过 color
入参设置返回按钮的颜色。通过 源码可以知道,本质上这个颜色属性是传入到 IconButton
组件构造方法中的。
class BackButton extends StatelessWidget { const BackButton({ super.key, this.color, this.onPressed }); final Color? color; final VoidCallback? onPressed; @override Widget build(BuildContext context) { assert(debugCheckHasMaterialLocalizations(context)); return IconButton( icon: const BackButtonIcon(), color: color, tooltip: MaterialLocalizations.of(context).backButtonTooltip, onPressed: () { if (onPressed != null) { onPressed!(); } else { Navigator.maybePop(context); } }, ); } }
通过已存在的 IconButton
和 BackButtonIcon
组件,组合拼装成更复杂的,具有某种使用场景的组件。这就是封装后可复用的魅力。如果想对一个组件从根源上进行了解,查看它的构建逻辑即可。从中你可以知其然,知其所以然,当你知道一件事物的构成机理,那它的任何表现都不会脱离你的控制,在使用时就是 “降维打击”
。
IconButton
是一个具有圆形水波纹点击效果的组件,必须传入一个子组件 icon
和回调函数 onPressed
。
说实话,国内的应用软件基本上不喜欢用 material
风格。对我个人来说,水波纹能给用户一个交互的反馈,本身是比较好的,但一个小小的图标按钮有水波纹,感觉怪怪的。这不得不让图标按钮的占位区域扩大,当多个 IconButton
排列时,如下所示,默认情况下,水波纹区域太大,又会显得拥挤:
不过可以通过 splashRadius
来控制水波纹的扩散半径。但在小区域中,当长按时展示水波扩散的动画效果时,手指几乎占据了整个区域,所以整个动画效果呈现的收益并不明显。
IconButton(
onPressed: (){},
color: Colors.blue ,
icon: Icon(Icons.add ),
splashRadius: 20,
),
如下,是启用 Material3
的效果,感觉这种水波纹要比 Material2
的好看一些,对于 IconButton
而言,会根据图标颜色显示背景色,长按时也不再是扩散的水波纹,而是背景色的变化。
MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true
),
home: const MyHomePage(),
);
下面来简单看一下 IconButton
组件的源码实现,首先,它继承自 StatelessWidget
,表明它是基于已有组件封装构建逻辑,从而形成的新组件。 从构造方法中可以到有大量的可配置属性:
如下是非 useMaterial3
时的主要构建逻辑,主题部分使用 ConstrainedBox
、Padding
、SizedBox
、Align
、IconTheme
组合构建:
tooltip
非空时会包裹 Tooltip
组件实现长按提示信息的功能,事件响应通过 InkResponse
组件实现。
最后说一下 useMaterial3
的处理, 在 IconButton#build
方法中,通过 Theme
数据的 useMaterial3
属性校验是否启用 Material3
:
通过启用 Material3
,会返回 _SelectableIconButton
完成构建逻辑。如下所示,像 selectedIcon
、isSelected
、style
、minSize
、maxWidth
是为 Material3
风格新加的属性。
---->[IconButton#build]---- if (theme.useMaterial3) { final Size? minSize = constraints == null ? null : Size(constraints!.minWidth, constraints!.minHeight); final Size? maxSize = constraints == null ? null : Size(constraints!.maxWidth, constraints!.maxHeight); // 略... if (style != null) { adjustedStyle = style!.merge(adjustedStyle); } Widget effectiveIcon = icon; if ((isSelected ?? false) && selectedIcon != null) { effectiveIcon = selectedIcon!; } Widget iconButton = IconTheme.merge( data: IconThemeData( size: effectiveIconSize, ), child: effectiveIcon, ); // 略... return _SelectableIconButton( // tag1 style: adjustedStyle, onPressed: onPressed, autofocus: autofocus, focusNode: focusNode, isSelected: isSelected, child: iconButton, ); }
从这里可以看出,通过 IconButton
组件封装构建逻辑,也有利于功能拓展。想要适应新的主题,只要优化构建逻辑即可,这对于使用者是没有任何影响的,且可以通过 Theme
决定启用的主题。组件封装的是 构建流程
,它更是一种对 功能的允诺
,通过它你能完成什么样的表现,是一个组件最重要的事。
FloatingActionButton
一般来说是使用在 Scaffold
的 floatingActionButton
属性中。因为 Scaffold
在构建逻辑中有一些和 FloatingActionButton
联动的效果,比如浮动按钮方位、动画等。不过 FloatingActionButton
本身只不过是一个圆形样式的 RawMaterialButton
而已。
它有如下四个构造,用来创建不同类型的浮动按钮,构造中主要为私有的 _FloatingActionButtonType
成员赋值:
enum _FloatingActionButtonType {
regular,
small,
large,
extended,
}
regular | small | large |
---|---|---|
![]() | ![]() | ![]() |
其中 extended
是 material3
中的风格,是圆角按钮,可以在官网的 extended-fab 中查看详情,也可以在该网站中看一下其他 material3
的风格:
它继承自 StatelessWidget
,表明它是基于已有组件封装构建逻辑,从而形成的新组件。 如下是 FloatingActionButton
的构造方法,其中的属性虽然挺多的,但基本上都是常规属性。其中比较特殊的是 heroTag
属性,默认值是一个 const
常量 _DefaultHeroTag
:
class _DefaultHeroTag {
const _DefaultHeroTag();
@override
String toString() => '<default FloatingActionButton tag>';
}
如果 heroTag
非空,会在构建逻辑中套上 Hero
组件:
这就有一个问题:在界面跳转时同一界面中不能有两个相同 tag
的 Hero
。这也是一个界面中使用两个 FloatingActionButton
常出现的问题,解决方案也很简单,将手动指定 heroTag
参数即可。
最终,FloatingActionButton
是依赖 RawMaterialButton
组件完成展示效果的:
从这四个相对简单的 StatelessWidget
组件可以看出,它们本质上是依赖其他已有组件,完成构建逻辑的封装,来满足一些特殊的使用场景。并且通过组件类成员属性的配置,让组件在表现上可以更加灵活。这个就是将构建逻辑分离成组件进行封装的主要优势。
可能有人会疑惑,使用函数不是也能封装组件吗,通过函数参数也能控制构建的表现,它和分离组件有什么区别呢?其实两者在本质上并没有什么区别,目的是一致的:封装特点创建中的构建逻辑
。 这个问题等价于在问: 类封装和函数封装的区别
。
类中可以定义成员变量和成员方法,封装能力更强,更像一个独立的 个体
,通过类封装相当于加入了 Widget
家族的正规军;通过函数封装,会显得比较零散,不利于分离和管理,但形式的比较灵活,相当于 游击队
。至于使用函数还是使用类封装构建逻辑,并没有严格的标准,自己结合场景考量。一般有 复用价值
或后期需要 拓展、修改
的构建逻辑,可以独立封装为类,一些临时构建的逻辑,可以通过函数来处理。
更多 Flutter 内置组件介绍,欢迎关注 《Flutter 组件集录》 专栏。
作者:张风捷特烈
链接:https://juejin.cn/post/7155644433948475406
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程
1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》
从一个膜拜大神的 Demo 开始
Kotlin 写 Gradle 脚本是一种什么体验?
Kotlin 编程的三重境界
Kotlin 高阶函数
Kotlin 泛型
Kotlin 扩展
Kotlin 委托
协程“不为人知”的调试技巧
图解协程:suspend
1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)
…
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。