アプリ解説のアニメーションをtutorial_coach_markで作る
🤔やってみたいこと
皆さんアプリを初めてアプリストアからインストールして起動すると画面が暗くなって、ボタンや特定のエリアだけ光って解説が表示される機能を見たことはありませんか?
あれは実はパッケージを使えば簡単に再現することができます。
知らなかった💦
友人のFlutterエンジニアのいせりゅーさんに教えていただきました。彼はパッケージに詳しい!
- flutter version: 3.27.0
tutorial_coach_markを使用してデモアプリを作っていこうと思います。
といってもプロジェクトを作成されたときのカウンターアプリに追加するだけですが。公式のサンプルのソースコードが長くて読みづらく💦
これを作る
🚀やってみたこと
公式を翻訳して解説してみました。
使用方法
このプラグインを使用するには、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