多屏幕下使用Flutter Screenshot包解决重复Global Key错误方案
解决Flutter Screenshot包的“Duplicate Global Keys”错误
问题根源
你当前使用全局单例的ScreenshotController,当多个页面/ScaffoldWrapper实例同时存在时,多个Screenshot组件会共用同一个控制器,导致内部生成的GlobalKey重复,触发"Duplicate Global Keys"错误。
解决方案:让每个ScaffoldWrapper持有独立控制器,并对外暴露截图能力
无需为每个页面单独写控制器逻辑,只需要修改ScaffoldWrapper,让它内部维护自己的控制器,并提供外部可调用的截图方法,同时通过GlobalKey让页面获取Wrapper的状态来触发截图。
步骤1:修改ScaffoldWrapper代码
移除对全局控制器的依赖,在State内部创建本地控制器,并添加截图方法:
import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:screenshot/screenshot.dart'; class ScaffoldWrapper extends StatefulWidget { final Widget scaffold; Function()? testFunction1; ScaffoldWrapper({super.key, required this.scaffold, this.testFunction1}); static const redColor = Color.fromARGB(255, 213, 115, 57); static const textColor = Color(0xff483538); static const buttonColor = Color(0xff43060f); static const bgColor = Color(0xff0A0B10); static const bgGradientColor = Color.fromARGB(255, 213, 115, 57); @override State<ScaffoldWrapper> createState() => _ScaffoldWrapperState(); } class _ScaffoldWrapperState extends State<ScaffoldWrapper> { // 本地控制器:每个Wrapper实例拥有独立的控制器 final ScreenshotController _localScreenshotController = ScreenshotController(); // 对外暴露的截图方法 Future<Uint8List?> captureScreenshot() async { return await _localScreenshotController.capture(); } @override void initState() { super.initState(); } bool devMode = true; @override Widget build(BuildContext context) { return Screenshot( controller: _localScreenshotController, // 使用本地控制器 child: Scaffold( backgroundColor: ScaffoldWrapper.bgColor, appBar: devMode ? AppBar(actions: [ ElevatedButton( onPressed: () => widget.testFunction1!(), child: const Text("Test Button 1")), ElevatedButton( onPressed: () { Hive.box("USER_PREFERENCES").clear(); Hive.box("USER_DATA").clear(); }, child: const Text("Clear Data")), ElevatedButton( onPressed: () {Navigator.push( context, MaterialPageRoute( builder: (context) => const ShowScreenshot()));}, child: const Text("Trigger Screenshot")) ]) : null, extendBody: true, body: Stack( children: [ dradient(-1.0, -1.0), dradient(1, 0), Positioned( child: widget.scaffold, ) ], ), ), ); } Container dradient(double x, double y) => Container( decoration: BoxDecoration( gradient: RadialGradient( colors: [ ScaffoldWrapper.bgGradientColor.withOpacity(0.2), ScaffoldWrapper.bgGradientColor.withOpacity(0) ], radius: (0.8), stops: const <double>[0, 1], center: Alignment(x, y)), ), ); }
步骤2:移除全局控制器
删除常量文件中的全局ScreenshotController定义:
// 删掉这一行 // ScreenshotController screenshotController = ScreenshotController();
步骤3:修改页面代码,通过GlobalKey调用截图
在使用ScaffoldWrapper的页面,通过GlobalKey获取Wrapper的State,调用内部的截图方法:
import 'package:flutter/material.dart'; import 'package:teacher_app_design/widgets/custom_scaffold_wrapper.dart'; class ShowScreenshot extends StatefulWidget { const ShowScreenshot({super.key}); @override State<ShowScreenshot> createState() => _ShowScreenshotState(); } class _ShowScreenshotState extends State<ShowScreenshot> { // 创建GlobalKey用于获取ScaffoldWrapper的State final GlobalKey<_ScaffoldWrapperState> _wrapperKey = GlobalKey(); Uint8List? screenshot; @override Widget build(BuildContext context) { return ScaffoldWrapper( key: _wrapperKey, // 绑定GlobalKey到Wrapper scaffold: Scaffold( body: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ const Center( child: Text("Take Picture of this Page"), ), ElevatedButton( onPressed: () async { // 通过GlobalKey调用Wrapper的截图方法 screenshot = await _wrapperKey.currentState?.captureScreenshot(); setState(() {}); // 更新本地状态 showGeneralDialog( context: context, pageBuilder: (context, a1, a2) => Scaffold( body: Center( child: screenshot != null ? InteractiveViewer( child: Image.memory(screenshot!)) : const Text("未获取到截图")), )); }, child: const Text('Take Screenshot')) ]), ), ); } }
额外优化(可选):通过Provider统一管理截图逻辑
如果希望用Provider统一处理截图结果,可以在Wrapper的captureScreenshot方法中,将结果同步到Provider的状态中,而不是让页面自己持有:
- 创建一个截图状态管理Provider,包含
Uint8List? screenshot和更新方法 - 在
_ScaffoldWrapperState的captureScreenshot方法中,调用Provider的更新方法保存截图 - 页面直接从Provider中获取截图数据显示
这种方式可以避免页面持有截图状态,更符合状态管理的规范,但实现稍复杂,适合需要全局共享截图结果的场景。
内容的提问来源于stack exchange,提问作者Ankere
相关产品推荐
相关产品推荐

