Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜


theme: cyanosis

1. 前言

今天看 Flutter 源码,偶然发现 Magnifier 组件,这单词不就是 放大镜 嘛! 再结合新版 Flutter 中输入文本的放大镜效果,直觉告诉我这玩意应该可以放大任何组件。如下所示,背景是一张图片,使用 RawMagnifier 实现了点击拖拽局部放大的效果,看起来还是蛮酷的:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第1张图片

另外,也可以自定义放大镜的形状,如下的五角星:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第2张图片

该组件已收录入 FlutterUnit ,可以在应用中查看相关源码:

| 桌面端 | 移动端 | | --- | --- | | Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第3张图片 | Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第4张图片 |


2. RawMagnifier 组件的简单使用

下面来简单使用一下:案例中通过 Stack 将 Image 和 RawMagnifier 叠放在一起,并且居中对齐。可以看到 RawMagnifier 组件的展示内容是对应图片位置的局部放大图。是不是用起来非常简单,就能实现很酷的效果:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第5张图片

```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, ); } } ```

强调一点,它可以放大和其叠放的 任何组件 ,比如其下放置一个文本组件,展示 张风捷特烈

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第6张图片


3. RawMagnifier 组件的构造函数

了解了简单使用,下面瞄一眼 RawMagnifier 组件源码中定义的入参,它继承自 StatelessWidget ,看起来并不是很复杂。

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第7张图片

| 属性名 | 类型 | 介绍 | 默认值 | --- | --- |--- |--- | | child | Widget? | 子组件 | null | | decoration | MagnifierDecoration | 装饰对象 | MagnifierDecoration() | | magnificationScale | double | 放大倍数 | 1 | | size | Size | 放大镜尺寸 | required | | focalPointOffset | Size | 中心偏移量 | Offset |


其中尺寸和放大倍数非常好理解,如下 size 改成 150*150、放大倍数 magnificationScale 改成 8 倍 :

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第8张图片

focalPointOffset 表示放大中心的偏移量,如下所示偏移量设为 Offset(-10,0), 效果上来看局部区域显示的靠左一点的内容 :

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第9张图片


decoration 是一个比较重要的属性,类型为 MagnifierDecoration 。可以配置装饰效果:从源码来看,可以定义放大镜的透明度、阴影和形状:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第10张图片

如下所示,0.9 的透明度可以看出一点底部的图案,去掉了边线。添加阴影:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第11张图片

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在手,天下我有》 一文中详细介绍了该类型的使用。比如下面自定义一个五角星的形状:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第12张图片

```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 组件控制放大镜的位置:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第13张图片

最后在手势交互中更新两个状态数据即可:

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 组件便于使用,从源码中可以看出它在构造函数在给了默认的参数:

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第14张图片

从构建逻辑中可以看出 Magnifier 组件只是借用了 RawMagnifier 组件,提供一个圆角矩形的装饰形状而言,没有什么非常特别的。

Flutter 组件集录 | RawMagnifier 组件 - 拿起你的八倍镜_第15张图片

至于 RawMagnifier 内部的实现原理,有机会再单独分析一下。那本文就到这里,谢谢观看~

你可能感兴趣的:(flutter)