theme: cyanosis
1. 前言
今天看 Flutter 源码,偶然发现 Magnifier
组件,这单词不就是 放大镜
嘛! 再结合新版 Flutter 中输入文本的放大镜效果,直觉告诉我这玩意应该可以放大任何组件。如下所示,背景是一张图片,使用 RawMagnifier
实现了点击拖拽局部放大的效果,看起来还是蛮酷的:
另外,也可以自定义放大镜的形状,如下的五角星:
该组件已收录入 FlutterUnit ,可以在应用中查看相关源码:
| 桌面端 | 移动端 | | --- | --- | | | |
2. RawMagnifier 组件的简单使用
下面来简单使用一下:案例中通过 Stack 将 Image 和 RawMagnifier 叠放在一起,并且居中对齐。可以看到 RawMagnifier
组件的展示内容是对应图片位置的局部放大图。是不是用起来非常简单,就能实现很酷的效果:
```dart class MagnifierExampleApp extends StatelessWidget{ final Size magnifierSize = const Size(120, 120);
const MagnifierExampleApp({super.key});
@override Widget build(BuildContext context) { return Scaffold( body: Center( child: Stack( alignment: Alignment.center, children: [ Image.asset('assets/images/sabar_bar.webp'), _buildMagnifier(), ], ), ), ); }
Widget _buildMagnifier(){ return RawMagnifier( decoration: const MagnifierDecoration( shape: CircleBorder( side: BorderSide(color: Colors.blue, width: 2), ), ), size: magnifierSize, magnificationScale: 3, ); } } ```
强调一点,它可以放大和其叠放的 任何组件
,比如其下放置一个文本组件,展示 张风捷特烈
:
3. RawMagnifier 组件的构造函数
了解了简单使用,下面瞄一眼 RawMagnifier 组件源码中定义的入参,它继承自 StatelessWidget
,看起来并不是很复杂。
| 属性名 | 类型 | 介绍 | 默认值 | --- | --- |--- |--- | | child | Widget? | 子组件 | null | | decoration | MagnifierDecoration | 装饰对象 | MagnifierDecoration() | | magnificationScale | double | 放大倍数 | 1 | | size | Size | 放大镜尺寸 | required | | focalPointOffset | Size | 中心偏移量 | Offset |
其中尺寸和放大倍数非常好理解,如下 size 改成 150*150
、放大倍数 magnificationScale 改成 8 倍 :
focalPointOffset 表示放大中心的偏移量,如下所示偏移量设为 Offset(-10,0)
, 效果上来看局部区域显示的靠左一点的内容 :
decoration 是一个比较重要的属性,类型为 MagnifierDecoration 。可以配置装饰效果:从源码来看,可以定义放大镜的透明度、阴影和形状:
如下所示,0.9 的透明度可以看出一点底部的图案,去掉了边线。添加阴影:
dart Widget _buildMagnifier(){ return RawMagnifier( decoration: MagnifierDecoration( opacity: 0.9, shadows: [ BoxShadow( offset: Offset(1,1), blurRadius: 4, spreadRadius: 6, color: Colors.black.withOpacity(0.1) ) ], shape: CircleBorder(), ), size: magnifierSize, focalPointOffset: Offset(-10, 0), magnificationScale: 3, ); }
除此之外, MagnifierDecoration 还有一个 shape 属性,类型为 ShapeBorder
,可以设置放大镜的形状。在 《【Flutter高级玩法-shape】Path在手,天下我有》 一文中详细介绍了该类型的使用。比如下面自定义一个五角星的形状:
```dart class _StarShapeBorder extends ShapeBorder { final Path _path = Path();
@override EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
@override Path getInnerPath(Rect rect, {TextDirection? textDirection}) { return Path(); }
@override Path getOuterPath(Rect rect, {TextDirection? textDirection}) => nStarPath(5, rect.height / 2, rect.height / 2 * 0.5, dx: rect.width / 2, dy: rect.height / 2);
@override void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { Paint paint = Paint()..style=PaintingStyle.stroke..color=Colors.blue..strokeWidth =2; canvas.drawPath(getOuterPath(rect), paint); }
Path nStarPath(int num, double R, double r, {dx = 0, dy = 0}) { double perRad = 2 * pi / num; double radA = perRad / 2 / 2; double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA; _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy); for (int i = 0; i < num; i++) { _path.lineTo( cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy); _path.lineTo( cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy); } _path.close(); return _path; }
@override ShapeBorder scale(double t) => this; } ```
4. 手势交互
上面就是 RawMagnifier 组件的使用方式,那如何实现按下展示放大镜、拖拽更新位置、抬起取消呢?答案很简单:监听手势事件。首先,由于需要在手势交互中更新位置和显示信息,所以需要 StatefulWidget
进行处理;然后添加如下两个状态数据用于表示放大镜位置和是否显示:
dart Offset _dragGesturePosition = Offset.zero; bool _show = false;
然后通过 GestureDetector 组件监听拖拽事件、通过 Positioned 组件控制放大镜的位置:
最后在手势交互中更新两个状态数据即可:
dart void _onPanDown(DragDownDetails details) { _dragGesturePosition = details.localPosition-Offset(magnifierSize.width/2,magnifierSize.height/2); _show = true; setState(() { }); } void _onPanEnd(DragEndDetails details) { setState(() => _show = false); } void _onPanUpdate(DragUpdateDetails details) { _dragGesturePosition = details.localPosition-Offset(magnifierSize.width/2,magnifierSize.height/2); setState(() { }); } void _onPanCancel() { setState(() => _show = false); }
5. Magnifier 组件
总的来说 RawMagnifier 的使用方式还是比较简单的,表现效果却非常炫酷。另外,基于 RawMagnifier 组件,官方还提供了一个 Magnifier
组件便于使用,从源码中可以看出它在构造函数在给了默认的参数:
从构建逻辑中可以看出 Magnifier 组件只是借用了 RawMagnifier 组件,提供一个圆角矩形的装饰形状而言,没有什么非常特别的。
至于 RawMagnifier 内部的实现原理,有机会再单独分析一下。那本文就到这里,谢谢观看~