☺️

アプリ解説のアニメーションをtutorial_coach_markで作る 

2024/12/19に公開

🤔やってみたいこと

皆さんアプリを初めてアプリストアからインストールして起動すると画面が暗くなって、ボタンや特定のエリアだけ光って解説が表示される機能を見たことはありませんか?

あれは実はパッケージを使えば簡単に再現することができます。

知らなかった💦

友人のFlutterエンジニアのいせりゅーさんに教えていただきました。彼はパッケージに詳しい!
https://qiita.com/isekiryu

  • flutter version: 3.27.0
    tutorial_coach_markを使用してデモアプリを作っていこうと思います。

といってもプロジェクトを作成されたときのカウンターアプリに追加するだけですが。公式のサンプルのソースコードが長くて読みづらく💦

これを作る

https://youtube.com/shorts/S_IYf9ns9Xc

🚀やってみたこと

公式を翻訳して解説してみました。

使用方法

このプラグインを使用するには、pubspec.yaml ファイルに依存関係として tutorial_coach_mark を追加します。

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

void showTutorial() {
    TutorialCoachMark tutorial = TutorialCoachMark(
      targets: targets, // List<TargetFocus>
      colorShadow: Colors.red, // DEFAULT Colors.black
       // alignSkip: Alignment.bottomRight,
       // textSkip: "SKIP",
       // paddingFocus: 10,
       // focusAnimationDuration: Duration(milliseconds: 500),
       // unFocusAnimationDuration: Duration(milliseconds: 500),
       // pulseAnimationDuration: Duration(milliseconds: 500),
       // pulseVariation: Tween(begin: 1.0, end: 0.99),
       // showSkipInLastTarget: true,
       // imageFilter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
       // initialFocus: 0,
       // useSafeArea: true,
      onFinish: (){
        print("finish");
      },
      onClickTargetWithTapPosition: (target, tapDetails) {
        print("target: $target");
        print("clicked at position local: ${tapDetails.localPosition} - global: ${tapDetails.globalPosition}");
      },
      onClickTarget: (target){
        print(target);
      },
      onSkip: (){
        print("skip");
        return true;
      }
    )..show(context:context);

    // tutorial.skip();
    // tutorial.finish();
    // tutorial.next(); // call next target programmatically
    // tutorial.previous(); // call previous target programmatically
    // tutorial.goTo(3); // call target programmatically by index
  }

このコードはFlutterアプリケーションでチュートリアルを表示するためのtutorial_coach_markパッケージの使用例ですね。主要なパラメータとコールバックについて解説します。

基本的なパラメータ:

  • targets: チュートリアルのフォーカスを当てる対象となるウィジェットのリスト(List<TargetFocus>)
  • colorShadow: フォーカス外の領域の色。デフォルトは黒

コメントアウトされているオプショナルなパラメータ:

  • alignSkip: スキップボタンの配置位置
  • textSkip: スキップボタンのテキスト
  • paddingFocus: フォーカス領域のパディング
  • focusAnimationDuration: フォーカスのアニメーション時間
  • unFocusAnimationDuration: フォーカス解除のアニメーション時間
  • pulseAnimationDuration: パルスエフェクトのアニメーション時間
  • pulseVariation: パルスエフェクトの変化量
  • showSkipInLastTarget: 最後のターゲットでスキップボタンを表示するか
  • imageFilter: 背景のぼかし効果
  • initialFocus: 初期フォーカスのインデックス
  • useSafeArea: SafeAreaを使用するか

コールバック関数:

  • onFinish: チュートリアルが完了したときの処理
  • onClickTargetWithTapPosition: ターゲットがタップされたときの処理(タップ位置の情報付き)
  • onClickTarget: ターゲットがタップされたときの処理
  • onSkip: スキップボタンが押されたときの処理

プログラムによる制御メソッド(コメントアウト):

  • skip(): チュートリアルをスキップ
  • finish(): チュートリアルを終了
  • next(): 次のターゲットに進む
  • previous(): 前のターゲットに戻る
  • goTo(index): 指定したインデックスのターゲットに移動

使用例では、インスタンス化後にshow()メソッドを呼び出してチュートリアルを表示しています。このメソッドにはcontextパラメータが必要です。

ターゲットの作成(TargetFocus)

TargetFocusは、フォーカスされるウィジェットを表すクラスで、フォーカス後に表示される内容を設定します。

属性は次のとおりです:

Attribute Type Description 解説
identify dynamic free for identification use 識別のために自由に使用できる値
keyTarget GlobalKey GlobalKey widget that wants to be focused フォーカスしたいウィジェットのGlobalKey
targetPosition TargetPosition If you do not want to use GlobalKey, you can create a TargetPosition to determine where to focus GlobalKeyを使用しない場合、フォーカス位置を決めるためのTargetPosition
contents ContentTarget[] Content list you want to display after focusing widget フォーカス後に表示するコンテンツのリスト
shape ShapeLightFocus ShapeLightFocus.Circle or ShapeLightFocus.RRect フォーカスの形状(円形または角丸四角形)
radius double Use when shape = ShapeLightFocus.RRect 角丸四角形の場合の角の丸みの半径
borderSide BorderSide ボーダーの設定
color Color Custom color to target ターゲットのカスタム色
enableOverlayTab bool enable click in all screen to call next step 画面全体をタップして次のステップに進める設定
enableTargetTab bool enable click in target to call next step ターゲット領域をタップして次のステップに進める設定
alignSkip Alignment use to align the skip in the target スキップボタンの配置位置
paddingFocus Alignment settings padding of the focus in target フォーカス領域のパディング設定
focusAnimationDuration Duration override the widget's global focus animation duration フォーカスアニメーションの時間をオーバーライド
unFocusAnimationDuration Duration override the widget's global unfocus animation duration フォーカス解除アニメーションの時間をオーバーライド
pulseVariation Tween override interval pulse animation パルスアニメーションの間隔をオーバーライド

コンテンツの作成 (ContentTarget)

ContentTargetは、ウィジェットにフォーカスを当てた後、何が表示され、どのように表示されるかを決定する責任を持つクラスです。

属性です:

Attribute Type Description 解説
align AlignContent With this attribute you determine in which region to display the content in relation to the focused widget (top,bottom,left,right) フォーカスされたウィジェットに対して、コンテンツをどの位置(上、下、左、右)に表示するかを決定する属性
padding EdgeInsets Padding of the content コンテンツの余白設定
child Widget Content you want to be displayed 表示したいコンテンツのウィジェット
builder Widget Content you want to be displayed 表示したいコンテンツのウィジェット(ビルダー方式)
customPosition CustomTargetContentPosition Add custom position when align is AlignContent.custom alignがAlignContent.customの場合のカスタム位置設定

Example

Dart

カウンターのサンプル
import 'package:flutter/material.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late TutorialCoachMark tutorialCoachMark;
  final counterTextKey = GlobalKey();
  final incrementButtonKey = GlobalKey();

  
  void initState() {
    super.initState();
    createTutorial();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      showTutorial();
    });
  }

  void createTutorial() {
    tutorialCoachMark = TutorialCoachMark(
      targets: [
        // 1. カウンターボタンの説明
        TargetFocus(
          identify: "floating_action_button",
          keyTarget: incrementButtonKey,
          contents: [
            TargetContent(
              align: ContentAlign.top,
              builder: (context, controller) {
                return Container(
                  padding: const EdgeInsets.all(15),
                  child: const Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text(
                        "プラスボタン",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 20,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      Text(
                        "画面右下の丸いボタンです。\nタップするたびにカウンターの数字が1つ増えます。",
                        style: TextStyle(
                          color: Colors.white,
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
          ],
        ),
        // 2. カウンター表示の説明
        TargetFocus(
          identify: "counter_text",
          keyTarget: counterTextKey,
          contents: [
            TargetContent(
              align: ContentAlign.bottom,
              builder: (context, controller) {
                return Container(
                  padding: const EdgeInsets.all(15),
                  child: const Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text(
                        "カウンター表示",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 20,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      Text(
                        "ここにカウントの値が表示されます",
                        style: TextStyle(
                          color: Colors.white,
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
          ],
        ),
      ],
    );
  }

  void showTutorial() {
    tutorialCoachMark.show(context: context);
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Tutorial Demo"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Container(
              key: counterTextKey,
              child: Column(
                children: [
                  Text(
                    '$_counter',
                    style: Theme.of(context).textTheme.headlineMedium,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        key: incrementButtonKey,
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

🙂最後に

どうでしたか皆さん。アプリの解説をするアニメーションをパッケージを使用すれば簡単に解説することができました。是非是非活用してみてください。

Discussion