独自のトランジションを効かせたダイアログの作り方
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の効果が少し違うようです。
この記事が気に入ったらサポートをしてみませんか?