如何在我的 Flutter 应用程序中显示可关闭的教程叠加层?

How to display a dismissable tutorial overlay in my Flutter app?

我想在我的 Flutter 应用程序的屏幕上显示教程叠加层。我什至找到了一个很好的候选者,它几乎可以满足我的要求:那就是 https://pub.dev/packages/overlay_tutorial/ plugin. The user would press a help button on the screen, the overlay would pop up with the cut outs. I don't want anything to receive clicks though (https://github.com/TabooSun/overlay_tutorial/issues/26),但是如果用户单击屏幕上的任意位置,叠加层应该会消失。

我尝试使用 AbsorbPointer and it successfully intercepts the clicks and there's no more click through and things happening bellow the overlay. See my fork: https://github.com/CsabaConsulting/overlay_tutorial/commit/9d809d51bcf55b9c8044d07d151c696bdb55abe6 但是现在也无法点击覆盖层。

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        AbsorbPointer(
          absorbing: widget.enabled && widget.absorbPointer,
          ignoringSemantics: true,
          child: _OverlayTutorialBackbone(
            overlayColor: widget.overlayColor,
            enabled: widget.enabled,
            overlayTutorialHoles: _overlayTutorialHoles,
            onEntryRectCalculated: () {
              _updateChildren();
            },
            child: widget.child,
          ),
        ),
        if (widget.enabled) ...[
          ..._overlayTutorialHoles.entries
              .map((entry) {

我需要的是 AbsorbPointer 的 onTap 或 onClick 处理程序。我可以将内容包装到 GestureDetector 中,但那样会一直拦截点击和手势。这里有什么好的解决方案?

*这个答案来自我的评论

如果将此代码粘贴到 DartPad,您会看到当一个 GestureDetector 在小部件树中位于另一个上方时,只有下方的 onTap 被执行。但是,如果较低的 GestureDetector 包裹在 AbsorbPointerabsorbing: true 中,那么 parent GestureDetector 才会被调用。因此,您应该能够按照我在评论中的建议进行操作。即使这不是真的,您仍然可以有条件地将 parent GestureDetectoronTap 设置为 null.

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  bool absorbing = false;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        GestureDetector(
          onTap: () {
            print('GestureDetector 1');
          },
          child: AbsorbPointer(
            absorbing: absorbing,
            child: GestureDetector(
              onTap: () {
                print('GestureDetector 2');
              },
              child: Container(
                height: 200.0,
                width: 200.0,
                color: Colors.red,
                alignment: Alignment.center,
                child: Text(
                  'Press me and check console',
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
        ),
        GestureDetector(
          onTap: () {
            setState(() {
              absorbing = !absorbing;
            });
          },
          child: Container(
            height: 200.0,
            width: 200.0,
            color: Colors.blue,
            alignment: Alignment.center,
            child: Text(
              'Change absorb\n(absorbing: $absorbing)',
              textAlign: TextAlign.center,
              style: TextStyle(
                color: Colors.white,
              ),
            ),
          ),
        ),
      ],
    );
  }
}