Flutter遮罩引导插件mask_guide的使用

Flutter遮罩引导插件mask_guide的使用

Masked guide implemented with ColorFilter, 参考自 https://juejin.cn/post/7089087097218269197

Getting started

在项目中引入 mask_guide 插件:

import 'package:mask_guide/mask_guide.dart';

Usage

首先,在 MaterialApp() 中添加 MaskGuideNavigatorObserver()

MaterialApp(
  navigatorObservers: [MaskGuideNavigatorObserver()],
)

使用自定义步骤小部件

如果您需要自定义步骤小部件,可以扩展 StepWidget 并覆盖 preStepnextStepdoneCallBack

创建自定义步骤小部件时,您需要手动计算小部件的位置。

class CustomStepWidget extends StepWidget {

  [@override](/user/override)
  void preStep() {
    super.preStep();
    // TODO: 执行一些操作
  }

  [@override](/user/override)
  void nextStep() {
    super.nextStep();
    // TODO: 执行一些操作
  }

  [@override](/user/override)
  void doneCallBack() {
    super.doneCallBack();
    // TODO: 执行一些操作
  }
}

更多示例请查看 example


示例代码

以下是完整的示例代码,展示了如何使用 mask_guide 插件。

文件结构

lib/
├── main.dart
└── custom_step_widget.dart

main.dart

import 'package:flutter/material.dart';
import 'package:mask_guide/mask_guide.dart';
import 'package:mask_guide_example/custom_step_widget.dart'; // 自定义步骤小部件

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      navigatorObservers: [MaskGuideNavigatorObserver()], // 添加观察器
      home: const FirstPage(),
    );
  }
}

class FirstPage extends StatefulWidget {
  const FirstPage({Key? key}) : super(key: key);

  [@override](/user/override)
  State<FirstPage> createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  bool autoStart = false;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('First Page')),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              onPressed: () {
                Navigator.of(context).push(MaterialPageRoute(
                    builder: (_) => MyHomePage(autoStart: autoStart)));
              },
              child: const Text('Start'),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text('自动显示:'),
                Switch(
                  value: autoStart,
                  onChanged: (value) {
                    setState(() {
                      autoStart = value;
                    });
                  },
                ),
              ],
            )
          ],
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.autoStart});

  final bool autoStart;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final GlobalKey redKey = GlobalKey();
  final GlobalKey yellowKey = GlobalKey();
  final GlobalKey blueKey = GlobalKey();
  final GlobalKey greenKey = GlobalKey();

  final MaskGuide maskGuide = MaskGuide();

  ScrollController scrollController = ScrollController();
  bool canPopOut = true;

  Future<void> scrollDown() async {
    await scrollController.animateTo(
      scrollController.position.maxScrollExtent,
      duration: const Duration(milliseconds: 300),
      curve: Curves.linear,
    );
  }

  Future<void> scrollUp() async {
    await scrollController.animateTo(
      0,
      duration: const Duration(milliseconds: 300),
      curve: Curves.linear,
    );
  }

  void showMaskGuide({required bool canPop, required bool canDismiss}) {
    setState(() {
      canPopOut = canPop;
    });
    maskGuide.showMaskGuide(
      context: context,
      keys: [redKey, yellowKey, blueKey, greenKey], // 指定目标元素
      guideTexts: [
        '这是第一个', // 提示文本
        '这是第二个',
        '这是第三个',
        '这是第四个',
      ],
      canPop: canPop, // 是否允许返回键退出
      canDismiss: canDismiss, // 是否允许点击遮罩层退出
      doneCallBack: () {
        setState(() {
          canPopOut = true;
        });
        print('默认完成回调');
      },
      dismissCallBack: () {
        setState(() {
          canPopOut = true;
        });
        print('默认取消回调');
      },
      nextStepCallBacks: [
        () {},
        () {},
        () => scrollDown(), // 滚动到下一屏
        () {},
      ],
      preStepCallBacks: [
        () {},
        () {},
        () {},
        () => scrollUp(), // 滚动到上一屏
      ],
    );
  }

  void showCustomMaskGuide({required bool canPop, required bool canDismiss}) {
    setState(() {
      canPopOut = canPop;
    });
    maskGuide.showMaskGuide(
      context: context,
      keys: [redKey, yellowKey, blueKey, greenKey],
      customStepWidget: CustomStepWidget(
        keys: [redKey, yellowKey, blueKey, greenKey],
        scrollController: scrollController,
      ), // 自定义步骤小部件
      canPop: canPop,
      canDismiss: canDismiss,
      doneCallBack: () {
        setState(() {
          canPopOut = true;
        });
        print('自定义完成回调');
      },
      dismissCallBack: () {
        setState(() {
          canPopOut = true;
        });
        print('自定义取消回调');
      },
    );
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    if (widget.autoStart) {
      WidgetsBinding.instance.addPostFrameCallback(
          (_) => showMaskGuide(canPop: false, canDismiss: false));
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return PopScope(
      canPop: canPopOut,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('主页'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  ElevatedButton(
                    onPressed: () {
                      showMaskGuide(canPop: false, canDismiss: false);
                    },
                    child: const Text("默认模式,不能取消,不能返回"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showMaskGuide(canPop: true, canDismiss: false);
                    },
                    child: const Text("默认模式,不能取消,可以返回"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showMaskGuide(canPop: false, canDismiss: true);
                    },
                    child: const Text("默认模式,可以取消,不能返回"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showMaskGuide(canPop: true, canDismiss: true);
                    },
                    child: const Text("默认模式,可以取消,可以返回"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showCustomMaskGuide(canPop: false, canDismiss: false);
                    },
                    child: const Text("自定义模式,不能取消,不能返回"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showCustomMaskGuide(canPop: true, canDismiss: false);
                    },
                    child: const Text("自定义模式,不能取消,可以返回"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showCustomMaskGuide(canPop: false, canDismiss: true);
                    },
                    child: const Text("自定义模式,可以取消,不能返回"),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showCustomMaskGuide(canPop: true, canDismiss: true);
                    },
                    child: const Text("自定义模式,可以取消,可以返回"),
                  ),
                ],
              ),
              const VerticalDivider(),
              SingleChildScrollView(
                controller: scrollController,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: [
                    Container(
                      key: redKey,
                      width: 50,
                      height: 50,
                      color: Colors.red,
                    ),
                    const SizedBox(height: 200),
                    Container(
                      key: yellowKey,
                      width: 50,
                      height: 50,
                      color: Colors.yellow,
                    ),
                    const SizedBox(height: 200),
                    Container(
                      key: blueKey,
                      width: 50,
                      height: 50,
                      color: Colors.blue,
                    ),
                    const SizedBox(height: 200),
                    Container(
                      key: greenKey,
                      width: 50,
                      height: 50,
                      color: Colors.green,
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

custom_step_widget.dart

import 'package:flutter/material.dart';
import 'package:mask_guide/mask_guide.dart';

class CustomStepWidget extends StepWidget {
  final List<GlobalKey> keys;
  final ScrollController scrollController;

  CustomStepWidget({
    required this.keys,
    required this.scrollController,
  });

  [@override](/user/override)
  void preStep() {
    super.preStep();
    // TODO: 执行一些操作
  }

  [@override](/user/override)
  void nextStep() {
    super.nextStep();
    // TODO: 执行一些操作
  }

  [@override](/user/override)
  void doneCallBack() {
    super.doneCallBack();
    // TODO: 执行一些操作
  }
}

更多关于Flutter遮罩引导插件mask_guide的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter遮罩引导插件mask_guide的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


mask_guide 是一个用于在 Flutter 应用中实现遮罩引导的插件。它可以帮助你在应用中创建一个带有遮罩的引导层,突出显示特定区域,并提供引导信息。以下是使用 mask_guide 插件的基本步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 mask_guide 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  mask_guide: ^0.1.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 mask_guide 插件:

import 'package:mask_guide/mask_guide.dart';

3. 使用 MaskGuide 组件

MaskGuide 组件可以包裹在你的应用中的任何部分,用于创建遮罩引导。以下是一个简单的示例:

import 'package:flutter/material.dart';
import 'package:mask_guide/mask_guide.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Mask Guide Example'),
        ),
        body: Center(
          child: MaskGuide(
            maskColor: Colors.black.withOpacity(0.5),
            highlightShape: HighlightShape.circle,
            highlightRadius: 50,
            highlightOffset: Offset(100, 100),
            child: ElevatedButton(
              onPressed: () {
                print('Button Pressed');
              },
              child: Text('Click Me'),
            ),
            onMaskTap: () {
              print('Mask Tapped');
            },
          ),
        ),
      ),
    );
  }
}

4. 配置 MaskGuide 参数

MaskGuide 组件提供了多个参数来定制遮罩引导的外观和行为:

  • maskColor: 遮罩的颜色,通常使用半透明的黑色或白色。
  • highlightShape: 高亮区域的形状,可以是 HighlightShape.circleHighlightShape.rectangle
  • highlightRadius: 如果高亮形状是圆形,则指定圆形的半径。
  • highlightOffset: 高亮区域的偏移量,用于定位高亮区域。
  • onMaskTap: 当用户点击遮罩时触发的回调函数。

5. 动态显示和隐藏引导

你可以通过控制 MaskGuide 的可见性来动态显示和隐藏引导。例如,使用 Visibility 组件或 AnimatedOpacity 组件来控制 MaskGuide 的显示和隐藏。

bool showGuide = true;

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Mask Guide Example'),
    ),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Visibility(
            visible: showGuide,
            child: MaskGuide(
              maskColor: Colors.black.withOpacity(0.5),
              highlightShape: HighlightShape.circle,
              highlightRadius: 50,
              highlightOffset: Offset(100, 100),
              child: ElevatedButton(
                onPressed: () {
                  setState(() {
                    showGuide = false;
                  });
                },
                child: Text('Click Me'),
              ),
              onMaskTap: () {
                setState(() {
                  showGuide = false;
                });
              },
            ),
          ),
          ElevatedButton(
            onPressed: () {
              setState(() {
                showGuide = true;
              });
            },
            child: Text('Show Guide'),
          ),
        ],
      ),
    ),
  );
}

6. 自定义引导内容

你可以在 MaskGuide 中添加自定义的引导内容,例如文本、图标等。通过 overlayBuilder 参数,你可以自定义引导层的内容。

MaskGuide(
  maskColor: Colors.black.withOpacity(0.5),
  highlightShape: HighlightShape.circle,
  highlightRadius: 50,
  highlightOffset: Offset(100, 100),
  child: ElevatedButton(
    onPressed: () {
      print('Button Pressed');
    },
    child: Text('Click Me'),
  ),
  overlayBuilder: (context) {
    return Positioned(
      top: 150,
      left: 50,
      child: Text(
        'This is a guide!',
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
    );
  },
  onMaskTap: () {
    print('Mask Tapped');
  },
);

7. 处理多个引导步骤

如果你需要处理多个引导步骤,可以使用 MaskGuideController 来控制引导的显示顺序。你可以通过 MaskGuideController 来动态切换引导步骤。

MaskGuideController controller = MaskGuideController();

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Mask Guide Example'),
    ),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          MaskGuide(
            controller: controller,
            maskColor: Colors.black.withOpacity(0.5),
            highlightShape: HighlightShape.circle,
            highlightRadius: 50,
            highlightOffset: Offset(100, 100),
            child: ElevatedButton(
              onPressed: () {
                controller.next();
              },
              child: Text('Step 1'),
            ),
            overlayBuilder: (context) {
              return Positioned(
                top: 150,
                left: 50,
                child: Text(
                  'Step 1: Click this button',
                  style: TextStyle(color: Colors.white, fontSize: 20),
                ),
              );
            },
            onMaskTap: () {
              controller.next();
            },
          ),
          MaskGuide(
            controller: controller,
            maskColor: Colors.black.withOpacity(0.5),
            highlightShape: HighlightShape.rectangle,
            highlightSize: Size(100, 50),
            highlightOffset: Offset(100, 200),
            child: ElevatedButton(
              onPressed: () {
                controller.next();
              },
              child: Text('Step 2'),
            ),
            overlayBuilder: (context) {
              return Positioned(
                top: 250,
                left: 50,
                child: Text(
                  'Step 2: Click this button',
                  style: TextStyle(color: Colors.white, fontSize: 20),
                ),
              );
            },
            onMaskTap: () {
              controller.next();
            },
          ),
        ],
      ),
    ),
  );
}

8. 处理引导结束

当所有引导步骤完成后,你可以通过 MaskGuideControlleronComplete 回调来处理引导结束的逻辑。

controller.onComplete = () {
  print('All steps completed');
};
回到顶部