本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
《Flutter TolyUI 框架》系列前言:
TolyUI 是 张风捷特烈 打造的 Fluter 全平台应用开发 UI 框架。具备 全平台 、组件化 、源码开放 、响应式 四大特点。可以帮助开发者迅速构建具有响应式全平台应用软件:
该系列将详细介绍 TolyUI 框架使用方式、框架开发过程中的技术知识、设计理念、难题解决等。本篇将介绍 TolyUI 反馈模块 中的两个重要组件,它们的视图职能如下视频所示:
一、Tooltip 的再设计
TolyUI 框架中组件的设计原则之一是:
不轻易增加组件,每个 TolyUI 中被设计的组件,都应有它的设计动机。
组件的设计动机包括:
[1]
. 提供 Flutter 中不存在的常用交互组件。[2]
. 对于 Flutter 已有但支持比较弱的组件, TolyUI 将基于源码,进行改造,以此拓展功能。
1.Tooltip 设计动机
Flutter 虽然内置了 Tooltip 组件,但它只能进行简单展示提示信息,效果如下:
这种视觉效果并不能满足通用场景:比如提示框的对齐方式
,或者气泡包裹
、自适应边界
、灵活自定义内容
等。这就是Tooltip 在 TolyUI 中再设计动机,如下通过 TolyTooltip 组件达到的效果:
2. tolyui_feedback 模块
TolyUI 的模块化,将相对独立的功能 单独分包 。功能上 Tooltip 在一个组件基础上,展开提示信息。属于一种交互的反馈,反馈内容是静态信息,不参与交互。反馈相关的组件将分离 【tolyui_feedback】 包提供功能:
同样,使用者可以 tolyui_feedback 独立模块包,或者使用 tolyui 全家桶来享用 TolyTooltip 组件。
yaml
# 仅使用反馈模块
dependencies:
tolyui_feedback: ^last_version
# 使用 tolyui 全家桶
dependencies:
tolyui: ^last_version
3. TolyTooltip 的功能
Tooltip在的设计语义是: 具有辅助反馈的功能 提示浮层。它会在目标组件 child 为基础,弹出用于展示的文字浮窗。这种浮窗是非侵扰性,一般不会响应事件,也不会消费目标组件的点击事件。在鼠标悬浮/手势长按事件中动画展开浮层。
有道 | 飞书 |
---|---|
[1].
动画展示/隐藏浮层弹框。[2].
支持 12 种弹框与目标组件的对齐方式。[3].
支持气泡框和非气泡框,填充与边模式线的弹框。[4].
支持边界溢出检测,并自动适应偏移功能。
二 、TolyTooltip 用法
对于桌面端和 web 平台来说,悬浮展示提示信息是一个非常常用的功能。下面介绍一下 TolyTooltip 的用法,感受一下它所带来的便利性和强大功能。
TolyTooltip 的使用案例介绍可以网站访问 TolyUI 的 web 版 Flutter 应用。
组件/反馈组件/popover: toly1994.com/ui/#/widget...
1. 浮窗的对齐方式
如下所示,TolyTooltip 提供了 12 种浮窗对齐方式,分为上下左右四组,每组有三种对齐方式。另外,默认提示框 支持气泡框,而且气泡的尖角位置会随着对其方式发生变化:
使用方式非常简单,直接通过 TolyTooltip
组件嵌套在目标组件之上即可。其中:
textStyle
参数可以指定浮窗内文字样式。padding
参数设置浮层弹框内边距。placement
参数设置浮层弹框和目标组件的对其方式。gap
参数设置浮层弹框和目标组件的距离。message
参数设置浮层弹框文字内容。
dart
TolyTooltip(
textStyle: TextStyle(fontSize: 13,color: Colors.white),
padding: EdgeInsets.symmetric(horizontal: 12,vertical: 8),
placement: TooltipPlacement.bottom,
gap: 12,
message: /// 提示信息,
child: ///目标组件
),
提示框摆放的位置,通过 TooltipPlacement
枚举表示。有如下 12 种:
dart
enum TooltipPlacement {
top,
topStart,
topEnd,
bottom,
bottomStart,
bottomEnd,
left,
leftStart,
leftEnd,
right,
rightStart,
rightEnd
}
2. 展示富文本
可以通过 richText
参数设置 InlineSpan
可以展示富文本。包括使用 WidgetSpan
在文字中嵌入组件。效果如下: 注 :如果不知道 InlineSpan 是什么,可以阅读 《RichText》 组件的相关文章。
dart
const InlineSpan span = TextSpan(children: [
TextSpan(text: '请通过此邮箱联系我们\n '),
TextSpan(
style: TextStyle(color: Colors.blue),
text: '1981462002@qq.com ',
)
]);
TolyTooltip(
richMessage: span,
placement: TooltipPlacement.top,
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
gap: 12,
child: ///目标组件
);
3. 边界溢出自适应
边界溢出检测,并自动适应偏移功能,是花费我很大心力实现的。相比于绝对遵从对其方式而是提示框溢出边界,只能展示一部分,边界溢出自适应更加合理。如下所示:
- Flutter 介绍 按钮的 Tooltip 对齐方式设置为 top,但当上方展示的区域不足时,自动转换为 bottom。
- TolyUI 链接对齐方式设置为 left ,当上方展示的区域不足时,自动转换为 leftStart。
4. 样式设置
TolyTooltip 提供了很多可配置的选项,比如背景色、填充模式等,让使用者可以更灵活地展示信息。另外通过设置最大高度,可以在弹框高度过高时允许滑动。效果如下:
上面的第一个案例是取消气泡框效果: 将 decorationConfig
参数的 isBubble
置为 false 即可。另外,该配置参数中还可以设置背景色、填充模式、文字颜色:
dart
Widget buildTooltipDisplay1(){
String message = '...';
return TolyTooltip(
message: message,
placement: TooltipPlacement.top,
textStyle: const TextStyle(fontFamily: '宋体',fontSize: 12,fontWeight: FontWeight.bold),
decorationConfig: const DecorationConfig(
isBubble: false,
textColor: Colors.white,
),
maxWidth: 250,
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
gap: 12,
child: const TolyLink(
text: '张风捷特烈', href: 'https://juejin.cn/user/149189281194766', onTap: jumpUrl,
),
);
}
三、Popover 的设计动机
当点击组件时,有时需要打开一个浮层弹框,并且在面板中进行交互。虽然 PopMenuButton 等组件可以展开浮层菜单栏,但是一方面浮层弹框的自定义灵活性很差,另一方面,仅展示一个浮层面板,并不是很符合菜单的语义。
比如下面微信和飞书中展开的面板,严格意义上来说不能称之为 菜单,但也不是提示信息。这就是 Popover 的设计动机:
通过交互,展开一个 支持交互 的浮层面板,并允许外界控制展示和隐藏
微信头像点击 | 飞书文档 |
---|---|
可能有人会觉得 Tooltip 和 Popover 在功能上差不多。但它们在界面交互中 行使的职能 是有区别的。Tooltip 浮层弹框 只在于展示信息
,目标组件的依旧可以形式自己的点击事件。比如AndroidStudio 中鼠标悬浮文件名时,展示详细的路径信息,属于 Tooltip 功能:
而 Popover 会可能会消耗目标组件的点击事件,弹出浮层弹框;另外该浮层 可以有消费事件 的需求。这两点是它和 Tooltip 的差异所在。比如 Photoshop 工具悬浮时展示的浮层面板,可以通过 Popover 展示:
四、Popover 的使用
Popover 的使用案例介绍可以网站访问 TolyUI 的 web 版 Flutter 应用。
组件/反馈组件/popover: toly1994.com/ui/#/widget...
1. TolyPopover 的基本使用
TolyPopover 通过 overlay
属性展示浮层面板;builder
方法可以回调控制器,控制器可以主动打开或关闭浮层。如下所示:
左侧案例通过 MouseRegion
监听鼠标进入事件,通过控制器打开浮层:
dart
TolyPopover(
placement: Placement.top,
maxWidth: 200,
overlay: const DisplayPanel(),
builder: (_, ctrl, __) {
return MouseRegion(
onEnter: (_)=> ctrl.open(),
child: const DebugDisplayButton( info: 'Hover Me'),
);
},
);
中间的案例通过按钮的点击回调事件 onPressed
方法,通过控制器打开浮层:
dart
TolyPopover(
placement: Placement.top,
maxWidth: 200,
overlay: const DisplayPanel(),
builder: (_, ctrl, __) {
return DebugDisplayButton( info: 'Click Me', onPressed: ctrl.open);
},
);
右侧的案例通过 GestureDetector 的长按回调事件 onLongPress
方法,通过控制器打开浮层。除此之外,双击事件,按下事件、抬起事件,甚至是自定义的交互手势,都可以通过控制器来打开或关闭浮层:
dart
TolyPopover(
placement: Placement.top,
maxWidth: 200,
overlay: const DisplayPanel(),
builder: (_, ctrl, __) {
return GestureDetector(
onLongPress: ctrl.open, child: const Text('LongPress Me'));
},
);
2. 浮层弹框中控制关闭
有时需要在浮层中控制浮层自身的关闭,而关闭浮层的关键在于控制器。也就是说,只要让浮层弹框感知到控制器,即可进行操作。此时可以将 overlay
入参升级为 overlayBuilder
,来感知控制器:
左侧案例的删除弹框,点击确定或取消后关闭浮层面板。弹框的内容由 DeletePanel 构建,将动画控制器传入其中即可,在点击按键时通过 ctrl
关闭浮层:
dart
TolyPopover(
placement: Placement.top,
maxWidth: 200,
overlayBuilder: (_,ctrl) => DeletePanel( ctrl: ctrl),
builder: (_, ctrl, __) {
return DebugDisplayButton(
info: 'Delete',
onPressed: ctrl.open,
);
},
);
右侧案例的添加按钮打开菜单也是类似,通过 DisplayMenu
展示菜单内容,构造时传入控制器。另外,和 TolytTooltip 一样,也可以通过 decorationConfig
来配置气泡框的装饰效果:
dart
TolyPopover(
placement: Placement.bottomEnd,
maxWidth: 180,
overlayBuilder: (_, ctrl) => DisplayMenu(ctrl),
decorationConfig: const DecorationConfig(),
builder: (_, ctrl, __) {
return GestureDetector(
onTap: ctrl.open,
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0),
child: Icon(Icons.add_circle_outline, color: Color(0xff666666)),
),
);
},
);
3. 自定义装饰和偏移
如下所示 TolyPopover 可以给使用者足够的发挥空间,来自定义面板内容以及装饰样式。其中通过 overlayDecorationBuilder
函数构造展示,目的是回调 PopoverDecoration 提供一些必要的参数,你可以根据它来更好的自定义装饰。offsetCalculator
也是如此,可以实现如下效果:
左侧案例中,点击头像时展示面板信息。通过 overlayDecorationBuilder 自定义非气泡框的普通装饰;并通过 offsetCalculator
计算偏移量,让弹框左上角和头像的中心对齐:
dart
TolyPopover(
placement: Placement.rightStart,
maxWidth: 260,
overlay: const _DisplayPanel(),
overlayDecorationBuilder: decorationBuilder,
offsetCalculator: calculatorOffset,
builder: (_, ctrl, __) {
return GestureDetector(
onTap: ctrl.open,
child: const CircleAvatar(
backgroundImage: AssetImage('assets/images/icon_head.webp'),
),
);
},
);
calculatorOffset
负责基于 Calculator
数据计算偏移量。它承载着目标组件尺寸、间隔、对齐方式、浮框尺寸四个数据。这里向下移动目标组件尺寸高度的一半,向左移动间隔加上目标组件尺寸宽度一半,即可让浮层左上角和目标组件中心对齐:
dart
//// 计算偏移
Offset calculatorOffset(Calculator calculator){
double x = calculator.boxSize.width / 2 + calculator.gap;
double y = calculator.boxSize.height / 2;
return Offset(-x, y);
}
decorationBuilder 负责自定义装饰,PopoverDecoration
会回调组件尺寸、对齐方式、气泡偏移量三个值。如果你需要自己定义气泡装饰,这些数据将会非常有用。不过这里使用的是普通的 BoxDecoration ,这些数据就没有什么作用了。
dart
/// 自定义装饰
Decoration decorationBuilder(PopoverDecoration decoration){
return BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.black.withOpacity(0.1)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
offset: const Offset(0, 2),
blurRadius: 6,
spreadRadius: 0,
)
],
borderRadius: BorderRadius.circular(4),
);
}
4. Popover 的 对齐方式
Popover 的 12 种浮窗对齐方式和 TolyTooltip 一样;另外指定的对齐方式在溢出边界后也可以自适应转变:
dart
TolyPopover(
maxWidth: 200,
placement: Placement.bottom, /// 指定对齐方式
gap: 12,
overlay: _DisplayPanel(title: info,),
builder: (_,ctrl,__)=>
DebugDisplayButton(info: buttonText ,onPressed: ctrl.open,),
),
尾声
这就是 TolyUI 带来的反馈组件的第一波,也是继响应式布局之后,推出的第二个模块。随着 tolyui 的逐步迭代,越来越多的新功能将会支持。感谢你关注 tolyui 的成长,如果喜欢,也希望你能在 github 中点赞支持~
github 开源地址: github.com/TolyFx/toly...
TolyUI 官方案例演示网站:toly1994.com/ui
TolyUI 的下一步会继续设计打造反馈组件,更多的精彩内容,敬请期待 ~