はじめに
Flutterはクロスプラットフォームなフレームワークですが、Flutterでモバイルアプリを開発していて、Android・iOSという二つのOSで画面遷移時の処理を統一するのに少し工夫が必要だったため共有します。
本記事ではAndroidにおいてiOS風の画面がスライドするようなトランジションエフェクトと、スワイプによる画面バック処理の実装について紹介します。
Androidバックキー押下時に何か処理を実装していた場合、そのままではスワイプによる画面バック動作が無効になってしまうので、その解決を試みました。
目標
AndroidでiOS風のトランジションと「スワイプで前の画面に戻る」動作を共通で実装させる。
実装
実装の内容は大まかに以下となります。
1. AndroidでiOS風の画面遷移をさせる
2. バックキーに処理を設定する
3. バックキー押下時の処理とスワイプによるバック動作を併存させる
1.AndroidでiOS風の画面遷移をさせる
Flutterのコードを読むと、以下のようにOSによって異なる画面遷移テーマが設定されていることがわかります。
static const Map<TargetPlatform, PageTransitionsBuilder> _defaultBuilders = <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: ZoomPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
};
ここから、AndroidでiOS風の画面遷移を実現するためには、Androidに対してもiOSと同様のテーマであるCupertinoPageTransitionsBuilder
をすれば良いとわかります。
よってMaterialAppのbuildThema内で、たとえば次のように実装します。
//main.dart内を抜粋
return MaterialApp(
theme: ThemeData(
pageTransitionsTheme: const PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: CupertinoPageTransitionsBuilder(),//Androidの遷移もIOS風に指定している
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
},
),
title: 'SAMPLE',
locale: localeJP,
);
これでAndroidでiOSと同じ画面遷移を行うことができました。
続いてAndroidにおけるバックキーの動作を実装していきましょう。
2. バックキーに処理を設定する
今回、Androidのバックキーを押したときに特定の処理を実行させたかったのでこちらを実装していきます。
デフォルトではバックキーを押下したとき画面をバックする動作が実行されるだけなので、任意の処理を実行させるため、WillPopScope
というウィジェットを使用します。
WillPopScope
はchildプロパティに指定したウィジェットに対して、画面が戻る動きを検知して、そのタイミングで実行されるコールバックを設定できるウィジェットになります。
(正確には画面が破棄されることを拒否するコールバックを登録できると公式のドキュメントに説明されています
次のようにWillPopScope
でウィジェットをラップすることで、Androidのバックキー押下時に実行されるコールバック処理を登録することができます。
WillPopScope(
onWillPop: () async { //コールバックはFuture<bool>で設定する
/*
バックキー押下時に実行させたい処理
*/
return true; //trueなら処理実行後画面を閉じる
},
child: Scaffold(
body: //画面を構成するWidget
),
)
WillPopScope
は画面をバックさせたくないときにも使用できますが、今回はAndroidのバックキー押下時の処理を設定するために利用します。
3. バックキー押下時の処理とスワイプによるバック動作を併存させる
ここまででAndroidにおけるiOS風の画面処理と、バックキー押下時に実行される処理の設定を行いました。
この状態では、実はスワイプによる画面バック機能が無効になっています。
その理由はFlutterにおける画面遷移の基礎となっているModalRoute
クラスにあります。
Flutterのソースコード上を見ていくとModalRoute
クラスにhasScopedWillPopCallback
というgetterが存在することがわかります。
こちらのコメントを読むと、
This method is used to disable the horizontal swipe pop gesture supported
by [MaterialPageRoute] for [TargetPlatform.iOS] and
disabled.
とあり、WillPopScopeCallbackが複数個あるとき、スワイプでバックするジェスチャが非活性化されていることがわかります。
ここで、Flutterにおける最もシンプルな画面遷移処理の例を次に示します。
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
このMaterialPageRoute
クラスは、先ほど言及したModalRoute
クラスを継承しているため、MaterialPageRoute
のサブクラスを作ればhasScopedWillPopCallback
をオーバーライドすることができます。
したがって、スワイプによるバック動作の非活性化を防ぐためには次のようにカスタムしたクラスを定義し、使用します。
class CustomMaterialPageRoute extends MaterialPageRoute {
CustomMaterialPageRoute(
{
required WidgetBuilder builder,
required RouteSettings settings,
}): super(builder: builder, settings: settings);
@override
bool get hasScopedWillPopCallback => false; //WillPopScopeがあるときもスワイプによるバックを許容する
}
MaterialApp
内での使用例を次に示します。
return MaterialApp(
theme: ThemeData(
pageTransitionsTheme: const PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: CupertinoPageTransitionsBuilder(),//Androidの遷移もIOS風に指定している
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
},
),
title: 'SAMPLE',
locale: localeJP,
onGenerateRoute: (RouteSettings settings) {
swich(settings.name){
case: '/SecondPage'{
return CustomMaterialPageRoute(
builder: ((context) => SecondPage()),
settings: settings,
);
}
//以下ルート名別に処理を設定する
},
);
);
これでWillPopScope
で設定したコールバックとスワイプによる画面バック動作を併存させることができました。
おわりに
今回はFlutterで
- Android上でもiOS風の画面遷移を実現する
- スワイプによる画面バック動作を保持したままバックキー押下時に処理を設定する
という二つの処理についてご紹介しました。
閲覧ありがとうございました。