🥝
【Dart/Flutter】画面遷移時の簡単なアニメーション土台(go_routerサンプルに追加)
【Dart/Flutter】画面遷移時の簡単なアニメーション土台(go_routerサンプルに追加)
-
実行環境
-
DartPadやAndroid Studio等で実行
- Based on Flutter 3.0.3 Dart SDK 2.17.5
-
DartPadやAndroid Studio等で実行
-
参考
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
static const String title = 'GoRouter Example: Declarative Routes';
Widget build(BuildContext context) => MaterialApp.router(
routeInformationProvider: _router.routeInformationProvider,
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
title: title,
);
final GoRouter _router = GoRouter(
routes: <GoRoute>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) =>
const Page1Screen(),
),
GoRoute(
path: '/page2',
pageBuilder: (context, state) => buildMyTransition(
child: const Page2Screen(),
color: Colors.red,
),
routes: [
GoRoute(
path: 'page3',
pageBuilder: (context, state) => buildMyTransition(
child: const Page3Screen(),
color: Colors.green,
),
)
],
),
],
);
}
class Page1Screen extends StatelessWidget {
const Page1Screen({Key? key}) : super(key: key);
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
),
],
),
),
);
}
class Page2Screen extends StatelessWidget {
const Page2Screen({Key? key}) : super(key: key);
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () => context.go('/page2/page3'),
child: const Text('Go to page 3'),
),
],
),
),
);
}
class Page3Screen extends StatelessWidget {
const Page3Screen({Key? key}) : super(key: key);
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => context.go('/'),
child: const Text('Go to home page'),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () => GoRouter.of(context).pop(),
child: const Text('Back'),
),
],
),
),
);
}
CustomTransitionPage<T> buildMyTransition<T>({
required Widget child,
required Color color,
String? name,
Object? arguments,
String? restorationId,
LocalKey? key,
}) {
return CustomTransitionPage<T>(
child: child,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return _MyReveal(
animation: animation,
color: color,
child: child,
);
},
key: key,
name: name,
arguments: arguments,
restorationId: restorationId,
transitionDuration: const Duration(milliseconds: 700),
);
}
class _MyReveal extends StatefulWidget {
final Widget child;
final Animation<double> animation;
final Color color;
const _MyReveal({
required this.child,
required this.animation,
required this.color,
});
State<_MyReveal> createState() => _MyRevealState();
}
class _MyRevealState extends State<_MyReveal> {
bool _finished = false;
final _tween = Tween(begin: const Offset(0, -1), end: Offset.zero);
void initState() {
super.initState();
widget.animation.addStatusListener(_statusListener);
}
void didUpdateWidget(covariant _MyReveal oldWidget) {
if (oldWidget.animation != widget.animation) {
oldWidget.animation.removeStatusListener(_statusListener);
widget.animation.addStatusListener(_statusListener);
}
super.didUpdateWidget(oldWidget);
}
void dispose() {
widget.animation.removeStatusListener(_statusListener);
super.dispose();
}
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: [
SlideTransition(
position: _tween.animate(
CurvedAnimation(
parent: widget.animation,
curve: Curves.easeOutCubic,
reverseCurve: Curves.easeOutCubic,
),
),
child: Container(
color: widget.color,
),
),
AnimatedOpacity(
opacity: _finished ? 1 : 0,
duration: const Duration(milliseconds: 300),
child: widget.child,
),
],
);
}
void _statusListener(AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
setState(() {
_finished = true;
});
break;
case AnimationStatus.forward:
case AnimationStatus.dismissed:
case AnimationStatus.reverse:
setState(() {
_finished = false;
});
break;
}
}
}
実行結果
Discussion