見出し画像

独自のトランジションを効かせたダイアログの作り方

FlutterにはshowDialog()という関数が用意されていて、かんたんにダイアログを表示できます。Fadeアニメーションで背景が透過の黒になり、ダイアログ表示されるアレです。トランジションをカスタマイズしたい、独自のダイアログを作成したい時どうするのでしょうか?
ちょっと古めの記事ですが、以下を参考にさせてもらいました。

結論

私も同じ結論ですが😁、PopupRoute という抽象クラスを継承して独自のダイアログを作成します。

PopupRouteの使い方

前述の記事の通り、showDialog()はグローバル関数ですが、中身は通常の画面遷移と同様に、Navigator.of(context).push()を使っています。同様にPopupRouteも以下のようにNavigator.of(context).push()で画面にポップアップします。

Navigator.of(context).push(FrostRoute(SampleDialog())));

独自のRouteクラスを作成する

では、PopupRouteを継承した独自のFrostRouteクラスを作成します。以下のようなトランジションを与えることにします。

・背景をBlur(すりガラス)にする
・ダイアログが徐々に大きくなる
・showDialog()と同じく、Durationは150ミリ秒にする
・showDialog()と同じく、アニメーションはCurves.easeOutにする
class FrostRoute extends PopupRoute {
 FrostRoute(this.widget);

 Widget widget;
 static const double frostAnimationStartValue = 0.0;
 static const double frostAnimationEndValue = 10.0;

 @override
 Color get barrierColor => Colors.black54;

 @override
 bool get barrierDismissible => true;

 @override
 String get barrierLabel => null;

 @override
 Duration get transitionDuration => const Duration(milliseconds: 150);

 @override
 Widget buildTransitions(BuildContext context, Animation<double> animation,
         Animation<double> secondaryAnimation, Widget child) =>
     FrostTransition(
       animation: Tween<double>(
               begin: frostAnimationStartValue, end: frostAnimationEndValue)
           .chain(CurveTween(curve: Curves.easeOut))
           .animate(animation),
       child: ScaleTransition(
           scale: animation.drive(Tween(begin: 0.6, end: 1.0)
               .chain(CurveTween(curve: Curves.easeOut))),
           child: child),
     );

 @override
 Widget buildPage(BuildContext context, Animation<double> animation,
         Animation<double> secondaryAnimation) =>
    widget;

}

FrostTransitionクラスはAnimatedWidgetを継承した独自のトランジションです。BackdropFilterで背景をblurにします。Filterの度合いをアニメーションで変化させます。

class FrostTransition extends AnimatedWidget {
 final Widget child;
 final Animation<double> animation;

 FrostTransition({this.animation, this.child}) : super(listenable: animation);

 @override
 Widget build(BuildContext context) => BackdropFilter(
     filter: ImageFilter.blur(
       sigmaX: animation.value,
       sigmaY: animation.value,
     ),
     child: Container(child: child));
}

ダイアログ本体

ダイアログ本体は適当です。widthとheightを画面サイズより小さくしてCenter()で中心に表示する事以外は普通のWidgetと同じでしょう。通常の画面遷移なので、ダイアログを閉じる方法は、Navigator.of(context).pop()で可能です。または、PopupRouteのbarrierDismissible => trueを@overrideすると背景タップで閉じることができます。

class SampleDialog extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Center(
     child: Container(
       width: 200,
       height: 100,
       padding: const EdgeInsets.all(16),
       decoration: BoxDecoration(
         color: const Color(0xfff3f3f3),
         borderRadius: BorderRadius.circular(16),
       ),
       child: Center(
         child: Text(
           'Test',
           style: TextStyle(
             fontSize: 22,
             fontWeight: FontWeight.bold,
             color: Color(0xff333333),
             decoration: TextDecoration.none,
           ),
         ),
       ),
     ),
   );
 }
}

動作確認

DartPadで見るWeb版とAndroidエミュレータとでは、ImageFilter.blurの効果が少し違うようです。


この記事が気に入ったらサポートをしてみませんか?