Flutter: New Material Motion

The motion system
https://material.io/design/motion/the-motion-system.html
Material Design
Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.
material.io
Material Design 의 motion system 은 app 의 navigation 동작을 사용자들이 이해하기 쉽도록 도와주는 몇개의 transition pattern 의 정의를 모아놓은 것이다.
transition patterns:
-
Container transform
-
Shared axis
-
Fade through
-
Fade
animations package
https://pub.dev/packages/animations
위 4개의 transition patterns 를 구현해 놓은 flutter package 이다.
이에 대해 잘 설명한 블로그 글이 있다:
A Deep Dive into the Flutter Animations package
A Deep Dive Into Flutter Animations | Very Good Ventures
An overview of Google’s new Material Design motion system, plus examples of the new Flutter animations package, presented by the Very Good Ventures Flutter development team.
verygood.ventures
Container transform
OpenContainer widget 로 구현되었다.
closedBuilder 와 openBuilder 사이의 transition 을 만든다.
Navigator 와 함께 쓰여야 한다.
ContainerTransitionType _transitionType = ContainerTransitionType.fade;
body: ListView(
padding: const EdgeInsets.all(8.0),
children: <Widget>[
_OpenContainerWrapper(
transitionType: _transitionType,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return _ExampleCard(openContainer: openContainer);
},
),
class _OpenContainerWrapper extends StatelessWidget {
const _OpenContainerWrapper({
this.closedBuilder,
this.transitionType,
});
final OpenContainerBuilder closedBuilder;
final ContainerTransitionType transitionType;
@override
Widget build(BuildContext context) {
return OpenContainer(
transitionType: transitionType,
openBuilder: (BuildContext context, VoidCallback _) {
return _DetailsPage();
},
tappable: false,
closedBuilder: closedBuilder,
);
}
}
_ExampleCard() 와 _DetailsPage() 사이의 navigation animation 이 만들어 진다.
Shared axis
SharedAxisTransition widget 으로 구현되었다.
SharedAxisTransitionType _transitionType = SharedAxisTransitionType.horizontal;
SharedAxisTransitionType 값 horizontal, vertical, scaled 에 따라 page transition 의 축(axis) 를 다르게 한다.
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: PageTransitionSwitcher(
duration: const Duration(milliseconds: 300),
reverse: !_isLoggedIn,
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: _transitionType,
);
},
child: _isLoggedIn ? _CoursePage() : _SignInPage(),
),
),
PageTransitionSwitcher.transitionBuilder 을 사용해서 animation 을 컨트롤 한다.
PageTransitionsTheme 과 SharedAxisPageTransitionsBuilder 를 사용하는 방법도 있다.
MaterialApp(
theme: ThemeData(
pageTransitionsTheme: PageTransitionsTheme(
builders: {
TargetPlatform.android: SharedAxisPageTransitionsBuilder(
transitionType: SharedAxisTransitionType.horizontal,
),
TargetPlatform.iOS: SharedAxisPageTransitionsBuilder(
transitionType: SharedAxisTransitionType.horizontal,
),
},
),
),
routes: {
'/': (BuildContext context) {
return Container(
);
},
'/a' : (BuildContext context) {
return Container(
// ...
);
},
},
);
Fade through
FadeThroughTransition widget 으로 구현되었다.
body: PageTransitionSwitcher(
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
child: pageList[pageIndex],
),
SharedAxisTransition 와 사용방식이 유사하다.
Fade
FadeScaleTransition widget 으로 구현되었다.
AnimationController _controller;
@override
void initState() {
_controller = AnimationController(
value: 0.0,
duration: const Duration(milliseconds: 150),
reverseDuration: const Duration(milliseconds: 75),
vsync: this,
)..addStatusListener((AnimationStatus status) {
setState(() {
});
});
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Fade')),
floatingActionButton: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return FadeScaleTransition(
animation: _controller,
child: child,
);
},
child: Visibility(
visible: _controller.status != AnimationStatus.dismissed,
child: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {},
),
),
),