Flutter - CustomPainter实现功能指引页面在移动应用中,指引页面是帮助用户快速了解新功能的重要手段。
前文
在移动应用中,指引页面是帮助用户快速了解新功能的重要手段。为了不干扰原有功能的布局和操作,我们可以通过创建一个透明背景的路由页面,并使用 CustomPainter
来绘制指引区域的高亮部分。这样不仅可以引导用户,还能保持页面整洁且易于维护。
接下来,我们介绍如何在 Flutter 中实现这种功能。
1. 整体效果展示
当用户进入应用的某个页面时,如果需要显示指引页面,我们可以通过 Navigator
启动透明路由页面。在指引页面中,使用 GuidePainter
来高亮重要功能区域,同时提供引导性提示。

通过这种方式,我们成功实现了一个透明背景的指引页面,用户可以在页面上看到关键的高亮功能区域,并通过点击操作关闭指引或进入相应功能。
2. 使用透明背景的路由页面
首先,指引页面需要覆盖在当前页面之上,并保持背景透明。通过 PageRouteBuilder
,我们可以创建一个透明的路由页面。关键在于设置 opaque: false
,使页面背景变得透明。
Navigator.of(context).push(
PageRouteBuilder(
opaque: false, // 设置为透明背景
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return GuidePage(); // 指引页面
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(opacity: animation, child: child); // 渐隐渐现的页面切换效果
},
),
);
3. 指引页面的实现
接下来,创建指引页面 GuidePage。在这个页面中,我们将背景设置为透明,并通过 Stack 叠加显示指引内容和提示信息。
class GuidePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent, // 透明背景
body: Stack(
children: [
// 使用 CustomPainter 绘制高亮区域
Positioned.fill(
child: GestureDetector(
onTap: () => Navigator.pop(context), // 点击关闭指引页面
child: CustomPaint(
painter: GuidePainter(
highlights: [
// AppGuide.single.guideWidgetLocationMap
// 从注册的部件中计算宽高位置, 进行自定义高亮
Rect.fromLTWH(100, 200, 150, 50), // 自定义的高亮区域
Rect.fromLTWH(50, 400, 200, 100),
],
),
),
),
),
// 添加提示信息
Positioned(
top: 100,
left: 50,
child: Text(
'点击这里进入新功能',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
],
),
);
}
}
在 GuidePage 中,我们使用了 CustomPaint 来绘制页面的半透明背景和高亮区域。高亮区域可以根据需要自定义其位置和大小。
4. 使用 CustomPainter
实现高亮区域
为了使指引页面能够突出特定功能区域,我们使用 CustomPainter
进行自定义绘制。通过 Canvas
和 Paint
,我们可以将高亮区域“抠”出,使其显示背后的内容,而其他部分保持半透明。
class GuidePainter extends CustomPainter {
final List<Rect> highlights; // 高亮区域的坐标和尺寸
GuidePainter({required this.highlights});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.black.withOpacity(0.7) // 半透明背景
..style = PaintingStyle.fill;
// 绘制全屏半透明背景
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
// 清除高亮区域,使其透明
for (final rect in highlights) {
canvas.saveLayer(rect, Paint());
canvas.drawRect(rect, Paint()..blendMode = BlendMode.clear); // 清除背景中高亮部分
canvas.restore();
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
在 GuidePainter
中,我们使用了 BlendMode.clear
来将高亮区域“抠出”,使用户能够看到背后页面的内容,而其他部分则保持半透明状态。
5. 关于高亮区域注册
在实现指引功能时,高亮区域的注册是一个关键步骤。通过为特定功能区域注册高亮信息,我们可以动态获取这些区域的位置信息并在指引页面中进行高亮显示。Flutter 提供了 GlobalKey
来获取部件的位置信息,结合 WidgetsBinding
,我们可以在页面渲染完成后获取准确的坐标和尺寸。
高亮区域注册的核心逻辑
我们需要创建一个 AppGuide
类,用来管理和注册需要高亮的功能部件信息。通过 GlobalKey
,我们可以捕获每个部件的坐标和尺寸,将其存储起来,供指引页面绘制时使用。
以下是实现高亮区域注册的核心代码:
class AppGuide {
final Map<String, Map<String, GuideWidgetLocation>> _guideWidgetLocationMap = {};
Map<String, Map<String, GuideWidgetLocation>> get guideWidgetLocationMap => _guideWidgetLocationMap;
/// 注册指引功能位置
/// - routeName: 注册的路由页面
/// - widgetUniqueName: 注册部件的唯一标识符
/// - key: 部件的 GlobalKey,用于获取部件的位置和尺寸
void registerGuideWidgetLocation({
required String routeName,
required String widgetUniqueName,
required GlobalKey key
}) {
WidgetsBinding.instance.addPostFrameCallback((_) {
// 获取部件的位置和尺寸
final location = GuideWidgetLocation(
widgetUniqueName: widgetUniqueName,
position: key.position,
size: key.size
);
// 将位置信息存储到 Map 中
(_guideWidgetLocationMap[routeName] ??= {})[widgetUniqueName] = location;
});
}
}
代码解析
-
数据结构:
_guideWidgetLocationMap
:这是一个存储所有高亮区域位置信息的 Map,其中每个路由页面有一个对应的子 Map,子 Map 存储该页面中每个部件的唯一标识符及其对应的位置信息。
-
注册高亮区域:
registerGuideWidgetLocation
方法负责将需要高亮的部件信息注册到AppGuide
中。此方法通过GlobalKey
获取指定部件的位置信息,并在页面渲染后(addPostFrameCallback
)存储其位置和尺寸。
-
GlobalKey 的使用:
GlobalKey
允许我们获取任意 Flutter 部件在屏幕上的位置及其尺寸。在注册过程中,position
和size
用于记录高亮区域的具体位置和大小。
使用示例
假设我们有一个主页 HomePage
,其中某个按钮需要在指引页面中被高亮显示。我们可以通过以下步骤进行注册:
class HomePage extends StatelessWidget {
final GlobalKey buttonKey = GlobalKey(); // 为按钮创建 GlobalKey
@override
Widget build(BuildContext context) {
// 在页面中注册该按钮的高亮位置
AppGuide().registerGuideWidgetLocation(
routeName: '/home',
widgetUniqueName: 'highlightButton',
key: buttonKey,
);
return Scaffold(
appBar: AppBar(title: Text('Home Page')),
body: Center(
child: ElevatedButton(
key: buttonKey, // 将 GlobalKey 赋予需要高亮的部件
onPressed: () {},
child: Text('点击我'),
),
),
);
}
}
在上面的例子中,我们为一个按钮分配了一个 GlobalKey
,并将其注册为需要高亮显示的区域。当指引页面显示时,它将根据注册信息在该按钮区域绘制高亮效果。
结语
通过上述简单步骤,我们能够轻松在 Flutter 应用中实现一个独立的指引功能页面,确保每次发布新版本时为用户提供清晰的功能指引,同时不与原有页面的逻辑产生冲突。这种方式可以提高应用的可维护性,方便未来的迭代开发。
撰写不易,请给个赞👍吧
转载自:https://juejin.cn/post/7419132517380931636