You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多屏幕下使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:47:21