Flutter引导高亮插件highlighter_coachmark的使用

Flutter引导高亮插件highlighter_coachmark的使用

在用户引导过程中,有多种方式可以向用户展示功能。例如,通过截图展示、叠加方向箭头、Material Design风格的功能发现或教练标记(Coach Mark)。这个教练标记插件会在背景模糊的同时突出显示所需元素。

该插件的特点是它会创建一个模糊的背景,并高亮显示目标元素。以下是一张动图,展示了它的效果:

此外,根据用户体验设计的建议:

  • 一次只展示一个提示,且在适当的时间展示,这样用户更容易理解和学习指令。
  • 教练标记应专注于创新或令人意想不到的功能。

使用方法

查看示例文件夹中的代码。那里有四个教练标记示例,它们都展示在上面的GIF中。

示例代码

// 初始化教练标记对象
CoachMark coachMark = CoachMark();

// 获取目标元素的上下文和矩形区域
RenderBox target = targetGlobalKey.currentContext.findRenderObject();
Rect markRect = target.localToGlobal(Offset.zero) & target.size;
markRect = Rect.fromCircle(center: markRect.center, radius: markRect.longestSide * 0.6);

// 显示教练标记
coachMark.show(
    targetContext: targetGlobalKey.currentContext, // 目标上下文
    markRect: markRect, // 高亮区域
    children: [
      Positioned(
          top: markRect.top + 5.0,
          right: 10.0,
          child: Text("长按按钮以查看更多选项",
              style: const TextStyle(
                fontSize: 24.0,
                fontStyle: FontStyle.italic,
                color: Colors.white,
              )))
    ],
    duration: null, // 持续时间,为null表示无限时长
    onClose: () {
       appState.setCoachMarkIsShown(true); // 关闭回调
    });

类似插件

感谢


完整示例代码

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

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'package:highlighter_coachmark/highlighter_coachmark.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.purple,
        accentColor: const Color(0xFFF850DD),
      ),
      home: FriendsListPage(),
    );
  }
}

class FriendsListPage extends StatefulWidget {
  [@override](/user/override)
  _FriendsListPageState createState() => _FriendsListPageState();
}

class _FriendsListPageState extends State<FriendsListPage> {
  List<Friend> _friends = [];
  ScrollController _scrollController;
  GlobalKey _fabKey = GlobalObjectKey("fab");
  GlobalKey _tileKey = GlobalObjectKey("tile_2");

  [@override](/user/override)
  void initState() {
    super.initState();
    _scrollController = ScrollController();
    _loadFriends();
  }

  Future<void> _loadFriends() async {
    http.Response response = await http.get('https://randomuser.me/api/?results=5');

    setState(() {
      _friends = Friend.allFromResponse(response.body);
    });

    // 启动教练标记教程
    Timer(Duration(seconds: 1), () => showCoachMarkFAB());
  }

  // 显示第一个教练标记
  void showCoachMarkFAB() {
    CoachMark coachMarkFAB = CoachMark();
    RenderBox target = _fabKey.currentContext.findRenderObject();

    Rect markRect = target.localToGlobal(Offset.zero) & target.size;
    markRect = Rect.fromCircle(
        center: markRect.center, radius: markRect.longestSide * 0.6);

    coachMarkFAB.show(
        targetContext: _fabKey.currentContext,
        markRect: markRect,
        children: [
          Center(
              child: Text("点击按钮\n添加朋友",
                  style: const TextStyle(
                    fontSize: 24.0,
                    fontStyle: FontStyle.italic,
                    color: Colors.white,
                  )))
        ],
        duration: null,
        onClose: () {
          Timer(Duration(seconds: 3), () => showCoachMarkTile());
        });
  }

  // 显示第二个教练标记
  void showCoachMarkTile() {
    CoachMark coachMarkTile = CoachMark();
    RenderBox target = _tileKey.currentContext.findRenderObject();

    Rect markRect = target.localToGlobal(Offset.zero) & target.size;
    markRect = markRect.inflate(5.0);

    coachMarkTile.show(
        targetContext: _fabKey.currentContext,
        markRect: markRect,
        markShape: BoxShape.rectangle,
        children: [
          Positioned(
              top: markRect.bottom + 15.0,
              right: 5.0,
              child: Text("点击朋友查看详情",
                  style: const TextStyle(
                    fontSize: 24.0,
                    fontStyle: FontStyle.italic,
                    color: Colors.white,
                  )))
        ],
        duration: Duration(seconds: 3));
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    Widget content;

    if (_friends.isEmpty) {
      content = Center(
        child: CircularProgressIndicator(),
      );
    } else {
      content = ListView.builder(
        itemExtent: 70.0,
        controller: _scrollController,
        itemCount: _friends.length,
        itemBuilder: _buildFriendListTile,
      );
    }

    return Scaffold(
      appBar: AppBar(title: Text('朋友们')),
      body: content,
      floatingActionButton: _friends.isEmpty
          ? null
          : FloatingActionButton(
              key: _fabKey,
              child: Icon(Icons.add),
              onPressed: () async {
                Friend friend = await buildShowDialog(context);
                if (friend != null) {
                  setState(() {
                    _friends.add(friend);
                  });
                  Future.delayed(
                      Duration(milliseconds: 200),
                      () => _scrollController.animateTo(
                          _scrollController.position.maxScrollExtent,
                          duration: Duration(seconds: 1),
                          curve: Curves.easeOut));
                }
              },
            ),
    );
  }

  Widget _buildFriendListTile(BuildContext context, int index) {
    var friend = _friends[index];
    GlobalKey key = index == 2 ? _tileKey : null;

    return ListTile(
      key: key,
      onTap: () => _navigateToFriendDetails(friend, index),
      leading: Hero(
        tag: index,
        child: CircleAvatar(
          backgroundImage: NetworkImage(friend.avatar),
        ),
      ),
      title: Text(friend.name),
      subtitle: Text(friend.email),
    );
  }

  void _navigateToFriendDetails(Friend friend, Object avatarTag) {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (c) {
          return FriendDetailsPage(friend, avatarTag: avatarTag);
        },
      ),
    );
  }

  Future<Friend> buildShowDialog(BuildContext context) {
    return showDialog<Friend>(
        context: context,
        builder: (BuildContext context) {
          return FutureBuilder<Friend>(
              future: _loadRandomFriend(),
              builder: (BuildContext context, AsyncSnapshot<Friend> snapshot) {
                if (snapshot.connectionState == ConnectionState.done) {
                  return SimpleDialog(
                      title: _buildDialogAddFriend(snapshot.data));
                } else {
                  return SimpleDialog(
                      title: Container(
                          height: 100.0,
                          width: 200.0,
                          alignment: Alignment.center,
                          color: Colors.white,
                          child: CircularProgressIndicator()));
                }
              });
        });
  }

  Future<Friend> _loadRandomFriend() async {
    http.Response response = await http.get('https://randomuser.me/api/?results=1');
    var friends = Friend.allFromResponse(response.body);
    return friends.first;
  }

  Widget _buildDialogAddFriend(Friend friend) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        ListTile(
          leading: CircleAvatar(
            backgroundImage: NetworkImage(friend.avatar),
          ),
          title: Text(friend.name),
          subtitle: Text(friend.email),
        ),
        Padding(
            padding: EdgeInsets.all(10.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                SimpleDialogOption(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: const Text('取消'),
                ),
                SimpleDialogOption(
                  onPressed: () {
                    Navigator.pop(context, friend);
                  },
                  child: const Text('保存'),
                ),
              ],
            )),
      ],
    );
  }
}

class Friend {
  final String name;
  final String email;
  final String avatar;

  Friend({this.name, this.email, this.avatar});

  static List<Friend> allFromResponse(String responseBody) {
    // 解析JSON数据并返回Friend列表
    // 这里省略具体实现
    return [];
  }
}

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

1 回复

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


highlighter_coachmark 是一个用于在 Flutter 应用中创建引导高亮效果的插件。它可以帮助你突出显示应用中的特定部件,并显示引导信息,以帮助用户更好地理解应用的功能。

安装

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

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

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

基本用法

  1. 导入包

    import 'package:highlighter_coachmark/highlighter_coachmark.dart';
    
  2. 创建高亮引导

    你可以使用 Coachmark 类来创建一个高亮引导。通常,你需要在 Widgetbuild 方法中使用它。

    class MyHomePage extends StatelessWidget {
      final GlobalKey _buttonKey = GlobalKey();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Highlighter Coachmark Example'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ElevatedButton(
                  key: _buttonKey,
                  onPressed: () {
                    // 显示引导高亮
                    Coachmark(
                      target: _buttonKey,
                      text: 'This is a button. Tap it to perform an action.',
                      onClose: () {
                        print('Coachmark closed');
                      },
                    ).show(context);
                  },
                  child: Text('Show Coachmark'),
                ),
              ],
            ),
          ),
        );
      }
    }
    
  3. 配置引导高亮

    Coachmark 类提供了多个配置选项,例如:

    • target: 需要高亮的部件的 GlobalKey
    • text: 显示的引导文本。
    • onClose: 引导关闭时的回调函数。
    • shape: 高亮的形状(例如圆形、矩形等)。
    • padding: 高亮区域的内边距。
    • color: 高亮的背景颜色。
    • textStyle: 引导文本的样式。

    例如:

    Coachmark(
      target: _buttonKey,
      text: 'This is a button. Tap it to perform an action.',
      shape: ShapeLightFocus.RRect,
      padding: 10,
      color: Colors.blue.withOpacity(0.5),
      textStyle: TextStyle(color: Colors.white, fontSize: 16),
      onClose: () {
        print('Coachmark closed');
      },
    ).show(context);
    

高级用法

你可以通过组合多个 Coachmark 来创建复杂的引导流程。例如,你可以在用户完成一个引导后,自动显示下一个引导。

void showCoachmarkSequence(BuildContext context) {
  Coachmark(
    target: _buttonKey1,
    text: 'This is the first button.',
    onClose: () {
      Coachmark(
        target: _buttonKey2,
        text: 'This is the second button.',
        onClose: () {
          Coachmark(
            target: _buttonKey3,
            text: 'This is the third button.',
          ).show(context);
        },
      ).show(context);
    },
  ).show(context);
}
回到顶部