Flutter引导展示插件custom_showcase的使用

Flutter引导展示插件custom_showcase的使用

Custom ShowCase 是一个用于逐步展示或高亮Flutter小部件的插件。它可以帮助开发者在应用中引导用户了解新功能或特定功能。

许可证

MIT 许可证

版权所有 (c) 2021 Simform Solutions

特此免费授予任何获得本软件副本和相关文档文件(“软件”)的人,无限制地处理本软件的权利,
包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件的副本,
并允许被提供本软件的人这样做,但须受以下条件的约束:

上述版权声明和本许可声明必须包含在所有副本或实质性部分中。

本软件按“原样”提供,不附带任何明示或暗示的保证,包括但不限于适销性、特定用途适用性和非侵权的保证。
在任何情况下,作者或版权持有人均不对因使用本软件或与本软件相关的其他行为而产生的任何索赔、损害或其他责任负责。

使用示例

以下是一个完整的示例,演示如何在Flutter应用中使用 custom_showcase 插件来逐步展示应用中的不同组件。

import 'dart:developer';

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

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter ShowCase',
      theme: ThemeData(
        primaryColor: const Color(0xffEE5366),
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: ShowCaseWidget(
          onStart: (index, key) {
            log('onStart: $index, $key');
          },
          onComplete: (index, key) {
            log('onComplete: $index, $key');
            if (index == 4) {
              SystemChrome.setSystemUIOverlayStyle(
                SystemUiOverlayStyle.light.copyWith(
                  statusBarIconBrightness: Brightness.dark,
                  statusBarColor: Colors.white,
                ),
              );
            }
          },
          blurValue: 1,
          builder: Builder(builder: (context) => const MailPage()),
          autoPlayDelay: const Duration(seconds: 3),
        ),
      ),
    );
  }
}

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

  [@override](/user/override)
  _MailPageState createState() => _MailPageState();
}

class _MailPageState extends State<MailPage> {
  final GlobalKey _one = GlobalKey();
  final GlobalKey _two = GlobalKey();
  final GlobalKey _three = GlobalKey();
  final GlobalKey _four = GlobalKey();
  final GlobalKey _five = GlobalKey();
  List<Mail> mails = [];

  final scrollController = ScrollController();

  [@override](/user/override)
  void initState() {
    super.initState();
    // 在当前小部件帧绘制后启动引导视图
    ambiguate(WidgetsBinding.instance)?.addPostFrameCallback(
      (_) => ShowCaseWidget.of(context).startShowCase([_one, _two, _three, _four, _five]),
    );
    mails = [
      Mail(sender: 'Medium', sub: 'Showcase View', msg: 'Check new showcase View', date: '1 May', isUnread: false),
      Mail(sender: 'Quora', sub: 'New Question for you', msg: 'Hi, There is new question for you', date: '2 May', isUnread: true),
      Mail(sender: 'Google', sub: 'Flutter 1.5', msg: 'We have launched Flutter 1.5', date: '3 May', isUnread: false),
      Mail(sender: 'Github', sub: 'Showcase View', msg: 'New star on your showcase view.', date: '4 May ', isUnread: true),
      Mail(sender: 'Simform', sub: 'Credit card Plugin', msg: 'Check out our credit card plugin', date: '5 May', isUnread: false),
      Mail(sender: 'Flutter', sub: 'Flutter is Future', msg: 'Flutter launched for Web', date: '6 May', isUnread: true),
      Mail(sender: 'Medium', sub: 'Showcase View', msg: 'Check new showcase View', date: '7 May ', isUnread: false),
      Mail(sender: 'Simform', sub: 'Credit card Plugin', msg: 'Check out our credit card plugin', date: '8 May', isUnread: true),
      Mail(sender: 'Flutter', sub: 'Flutter is Future', msg: 'Flutter launched for Web', date: '9 May', isUnread: false),
    ];
  }

  [@override](/user/override)
  void dispose() {
    scrollController.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: SafeArea(
        bottom: false,
        child: Column(
          children: <Widget>[
            const SizedBox(height: 20),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Row(
                  children: [
                    Expanded(
                      child: Container(
                        padding: const EdgeInsets.only(left: 10, right: 8),
                        child: Container(
                          padding: const EdgeInsets.all(12),
                          decoration: BoxDecoration(
                            color: const Color(0xffF9F9F9),
                            border: Border.all(color: const Color(0xffF3F3F3), width: 2),
                            borderRadius: BorderRadius.circular(8),
                          ),
                          child: Row(
                            children: <Widget>[
                              Expanded(
                                child: Row(
                                  children: <Widget>[
                                    Showcase(
                                      key: _one,
                                      isTop: true,
                                      radius: 50,
                                      description: 'Tap to see menu options',
                                      onBarrierClick: () => debugPrint('Barrier clicked'),
                                      child: GestureDetector(
                                        onTap: () => debugPrint('menu button clicked'),
                                        child: Icon(Icons.menu, color: Theme.of(context).primaryColor),
                                      ),
                                    ),
                                    const SizedBox(width: 10),
                                    const Text(
                                      'Search email',
                                      style: TextStyle(color: Colors.black45, fontSize: 16, letterSpacing: 0.4),
                                    ),
                                    const Spacer(),
                                    const Icon(Icons.search, color: Color(0xffADADAD)),
                                  ],
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                    Showcase(
                      targetPadding: const EdgeInsets.all(5),
                      key: _two,
                      isTop: true,
                      radius: 50,
                      title: 'Profile',
                      description: "Tap to see profile which contains user's name, profile picture, mobile number and country",
                      tooltipBackgroundColor: Theme.of(context).primaryColor,
                      textColor: Colors.white,
                      targetShapeBorder: const CircleBorder(),
                      child: Container(
                        padding: const EdgeInsets.all(5),
                        width: 45,
                        height: 45,
                        decoration: BoxDecoration(
                          shape: BoxShape.circle,
                          color: Theme.of(context).primaryColor,
                        ),
                        child: Image.asset('assets/simform.png'),
                      ),
                    ),
                    const SizedBox(width: 12),
                  ],
                ),
                const SizedBox(height: 10),
                Container(
                  padding: const EdgeInsets.only(left: 16, top: 4),
                  child: const Text(
                    'PRIMARY',
                    style: TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            ),
            const Padding(padding: EdgeInsets.only(top: 8)),
            Expanded(
              child: ListView.builder(
                controller: scrollController,
                physics: const BouncingScrollPhysics(),
                itemBuilder: (context, index) {
                  if (index == 0) {
                    return showcaseMailTile(_three, true, context, mails.first);
                  }
                  return MailTile(mail: mails[index % mails.length]);
                },
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: Showcase(
        key: _five,
        isTop: true,
        radius: 50,
        title: 'Compose Mail',
        description: 'Click here to compose mail',
        targetShapeBorder: const CircleBorder(),
        child: FloatingActionButton(
          backgroundColor: Theme.of(context).primaryColor,
          onPressed: () {
            setState(() {
              // 重置ListView以确保展示的小部件当前已渲染,展示键可用
              scrollController.jumpTo(0);
              ShowCaseWidget.of(context).startShowCase([_one, _two, _three, _four, _five]);
            });
          },
          child: const Icon(Icons.add),
        ),
      ),
    );
  }

  GestureDetector showcaseMailTile(GlobalKey<State<StatefulWidget>> key, bool showCaseDetail, BuildContext context, Mail mail) {
    return GestureDetector(
      onTap: () {
        Navigator.push<void>(
          context,
          MaterialPageRoute<void>(builder: (_) => const Detail()),
        );
      },
      child: Container(
        padding: const EdgeInsets.symmetric(vertical: 8),
        child: Showcase(
            key: key,
            isTop: true,
            radius: 50,
            description: 'Tap to check mail',
            tooltipPosition: TooltipPosition.top,
            disposeOnTap: true,
            onTargetClick: () {
              Navigator.push<void>(
                context,
                MaterialPageRoute<void>(builder: (_) => const Detail()),
              ).then((_) {
                setState(() {
                  ShowCaseWidget.of(context).startShowCase([_four, _five]);
                });
              });
            },
            child: MailTile(mail: mail, showCaseKey: _four, showCaseDetail: showCaseDetail)),
      ),
    );
  }
}

class SAvatarExampleChild extends StatelessWidget {
  const SAvatarExampleChild({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(10),
      child: Container(
        width: 45,
        height: 45,
        decoration: const BoxDecoration(
          shape: BoxShape.circle,
          color: Color(0xffFCD8DC),
        ),
        child: Center(
          child: Text(
            'S',
            style: TextStyle(
              color: Theme.of(context).primaryColor,
              fontWeight: FontWeight.bold,
              fontSize: 16,
            ),
          ),
        ),
      ),
    );
  }
}

class Mail {
  Mail({
    required this.sender,
    required this.sub,
    required this.msg,
    required this.date,
    required this.isUnread,
  });

  String sender;
  String sub;
  String msg;
  String date;
  bool isUnread;
}

class MailTile extends StatelessWidget {
  const MailTile(
      {required this.mail,
      this.showCaseDetail = false,
      this.showCaseKey,
      Key? key})
      : super(key: key);
  final bool showCaseDetail;
  final GlobalKey<State<StatefulWidget>>? showCaseKey;
  final Mail mail;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 8),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Expanded(
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                if (showCaseDetail)
                  Showcase.withWidget(
                    key: showCaseKey!,
                    isTop: true,
                    radius: 50,
                    height: 50,
                    width: 140,
                    targetShapeBorder: const CircleBorder(),
                    targetBorderRadius: const BorderRadius.all(Radius.circular(150)),
                    container: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Container(
                          width: 45,
                          height: 45,
                          decoration: const BoxDecoration(
                            shape: BoxShape.circle,
                            color: Color(0xffFCD8DC),
                          ),
                          child: Center(
                            child: Text(
                              'S',
                              style: TextStyle(
                                color: Theme.of(context).primaryColor,
                                fontWeight: FontWeight.bold,
                                fontSize: 16,
                              ),
                            ),
                          ),
                        ),
                        const SizedBox(height: 10),
                        const Text("Your sender's profile ", style: TextStyle(color: Colors.white)),
                      ],
                    ),
                    child: const SAvatarExampleChild(),
                  )
                else
                  const SAvatarExampleChild(),
                const Padding(padding: EdgeInsets.only(left: 8)),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        mail.sender,
                        overflow: TextOverflow.ellipsis,
                        style: TextStyle(
                          fontWeight: mail.isUnread ? FontWeight.bold : FontWeight.normal,
                          fontSize: 17,
                        ),
                      ),
                      Text(
                        mail.sub,
                        overflow: TextOverflow.ellipsis,
                        style: const TextStyle(fontWeight: FontWeight.normal, fontSize: 16),
                      ),
                      Text(
                        mail.msg,
                        overflow: TextOverflow.ellipsis,
                        style: TextStyle(
                          fontWeight: FontWeight.normal,
                          color: mail.isUnread ? Theme.of(context).primaryColor : Colors.black,
                          fontSize: 15,
                        ),
                      ),
                    ],
                  ),
                )
              ],
            ),
          ),
          SizedBox(
            width: 50,
            child: Column(
              children: <Widget>[
                const SizedBox(height: 5),
                Text(
                  mail.date,
                  style: const TextStyle(fontWeight: FontWeight.normal, fontSize: 12, color: Colors.grey),
                ),
                const SizedBox(height: 10),
                Icon(
                  mail.isUnread ? Icons.star : Icons.star_border,
                  color: mail.isUnread ? const Color(0xffFBC800) : Colors.grey,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

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

1 回复

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


custom_showcase 是一个用于在 Flutter 应用中创建引导展示(Showcase)的插件。它可以帮助你在应用中高亮显示特定的 UI 元素,并通过逐步引导的方式向用户介绍应用的功能。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 custom_showcase 依赖:

dependencies:
  flutter:
    sdk: flutter
  custom_showcase: ^1.0.0  # 请检查最新版本

然后运行 flutter pub get 来安装依赖。

2. 基本用法

custom_showcase 的主要组件是 CustomShowcase,你可以将它包裹在你想要高亮的 Widget 周围。以下是一个简单的示例:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ShowcaseScreen(),
    );
  }
}

class ShowcaseScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Custom Showcase Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CustomShowcase(
              title: 'First Widget',
              description: 'This is the first widget in the showcase.',
              child: ElevatedButton(
                onPressed: () {},
                child: Text('Button 1'),
              ),
            ),
            SizedBox(height: 20),
            CustomShowcase(
              title: 'Second Widget',
              description: 'This is the second widget in the showcase.',
              child: ElevatedButton(
                onPressed: () {},
                child: Text('Button 2'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

3. 自定义配置

CustomShowcase 提供了多种配置选项,你可以根据需要自定义展示的外观和行为。以下是一些常用的配置:

  • title: 展示的标题。
  • description: 展示的描述。
  • child: 需要高亮的子 Widget。
  • shapeBorder: 高亮区域形状,如圆形、矩形等。
  • blurValue: 背景模糊程度。
  • showcaseBackgroundColor: 展示背景颜色。
  • textColor: 文本颜色。
  • titleTextStyle: 标题文本样式。
  • descriptionTextStyle: 描述文本样式。

示例:

CustomShowcase(
  title: 'Custom Shape',
  description: 'This showcases a custom shape.',
  shapeBorder: CircleBorder(),
  blurValue: 5.0,
  showcaseBackgroundColor: Colors.blue.withOpacity(0.5),
  textColor: Colors.white,
  titleTextStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
  descriptionTextStyle: TextStyle(fontSize: 16),
  child: ElevatedButton(
    onPressed: () {},
    child: Text('Custom Button'),
  ),
);

4. 控制展示顺序

如果你有多个 CustomShowcase,你可能希望它们按顺序展示。你可以使用 ShowCaseWidget 来管理展示的顺序:

ShowCaseWidget(
  builder: Builder(
    builder: (context) => Scaffold(
      appBar: AppBar(
        title: Text('Custom Showcase Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CustomShowcase(
              title: 'First Widget',
              description: 'This is the first widget in the showcase.',
              child: ElevatedButton(
                onPressed: () {
                  ShowCaseWidget.of(context).next();
                },
                child: Text('Button 1'),
              ),
            ),
            SizedBox(height: 20),
            CustomShowcase(
              title: 'Second Widget',
              description: 'This is the second widget in the showcase.',
              child: ElevatedButton(
                onPressed: () {
                  ShowCaseWidget.of(context).next();
                },
                child: Text('Button 2'),
              ),
            ),
          ],
        ),
      ),
    ),
  ),
);
回到顶部