こんにちは。
スマホアプリをメインに開発しているロッキーカナイです。
前回、Flutterでカードめくりアニメーションの紹介をしましたが、続編ということで、今回はPageViewでスライド時に、いい感じにアニメーションさせる実装を紹介したいと思います。
やること
- PageViewでトランプ画像をスライドさせる時に拡大アニメーションをさせる。
- メインのトランプ以外は元の大きさに戻す
- ページインジケータを実装する。
- 移動時に該当トランプの詳細を出す。
今回はこれらを行なってみたいと思います。
コード
まず、1と2の実装を紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
import 'package:flutter/material.dart'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: SlidePage(), ), ); } } class SlidePage extends StatefulWidget { @override _SlideshowState createState() => _SlideshowState(); } class _SlideshowState extends State<SlidePage> { // ページコントローラ final PageController controller = PageController(viewportFraction: 0.8); // ページインデックス int currentPage = 0; // データ List<String> _imageList = [ "images/card_back.png", "images/card_j.png", "images/card_q.png", "images/card_k.png", ]; @override void initState() { super.initState(); // ページコントローラのページ遷移を監視しページ数を丸める controller.addListener(() { int next = controller.page.round(); if (currentPage != next) { setState(() { currentPage = next; }); } }); } /* * アニメーションカード生成 */ AnimatedContainer _createCardAnimate(String imagePath, bool active) { // アクティブと非アクティブのアニメーション設定値 final double top = active ? 100 : 200; final double side = active ? 0 : 40; return AnimatedContainer( duration: Duration(milliseconds: 500), curve: Curves.easeOutQuint, margin: EdgeInsets.only(top: top, bottom: 50.0, right: side, left: side), decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.fitWidth, image: Image.asset(imagePath).image, ), ), ); } @override Widget build(BuildContext context) { return PageView.builder( controller: controller, itemCount: _imageList.length, itemBuilder: (context, int currentIndex) { // アクティブ値 bool active = currentIndex == currentPage; // カードの生成して返す return _createCardAnimate( _imageList[currentIndex], active, ); }, ); } } |
素材はこちらから拝借致しました。
いい感じにできました。
PageViewの個々のViewを生成時にmarginを変更しアニメーションをさせてます。
1 2 3 4 5 6 7 8 9 10 11 |
return AnimatedContainer( duration: Duration(milliseconds: 500), curve: Curves.easeOutQuint, margin: EdgeInsets.only(top: top, bottom: 50.0, right: side, left: side), decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.fitWidth, image: Image.asset(imagePath).image, ), ), ); |
上記が該当コードになります。
次に3と4のコードを紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
import 'package:flutter/material.dart'; import 'package:page_view_indicators/circle_page_indicator.dart'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Introduction(), ), ); } } class Introduction extends StatefulWidget { @override _IntroductionState createState() => _IntroductionState(); } class _IntroductionState extends State<Introduction> { // ページコントローラ final PageController controller = PageController(viewportFraction: 0.8); // ページインデックス final _currentPageNotifier = ValueNotifier<int>(0); // データ List<String> _imageList = [ "images/card_back.png", "images/card_j.png", "images/card_q.png", "images/card_k.png", ]; List<String> _textList = [ "トランプ背面", "ジョーカー", "クイーン", "キング", ]; @override void initState() { super.initState(); // ページコントローラのページ遷移を監視しページ数を丸める controller.addListener(() { int next = controller.page.round(); if (_currentPageNotifier.value != next) { setState(() { _currentPageNotifier.value = next; }); } }); } /* * アニメーションカード生成 */ AnimatedContainer _createCardAnimate(String imagePath, bool active) { // アクティブと非アクティブのアニメーション設定値 final double top = active ? 100 : 200; final double side = active ? 0 : 40; return AnimatedContainer( duration: Duration(milliseconds: 500), curve: Curves.easeOutQuint, margin: EdgeInsets.only(top: top, bottom: 50.0, right: side, left: side), decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.fitWidth, image: Image.asset(imagePath).image, ), ), ); } @override Widget build(BuildContext context) { return SafeArea( child: Column( children: <Widget>[ /* * ページ */ Expanded( child: PageView.builder( controller: controller, itemCount: _imageList.length, itemBuilder: (context, int currentIndex) { // アクティブ値 bool active = currentIndex == _currentPageNotifier.value; // カードの生成して返す return _createCardAnimate( _imageList[currentIndex], active, ); }, ), ), /* * ページインジケータ */ Container( height: 30.0, child: CirclePageIndicator( itemCount: _imageList.length, currentPageNotifier: _currentPageNotifier, ), ), /* * 説明エリア */ Container( height: 80.0, padding: EdgeInsets.all(10.0), child: Container( width: double.infinity, padding: EdgeInsets.all(10.0), decoration: BoxDecoration( border: Border.all( color: Colors.blue, width: 4.0, ), borderRadius: BorderRadius.circular(10), ), child: Center( child: Text( _textList[_currentPageNotifier.value], style: TextStyle( fontSize: 20.0, fontWeight: FontWeight.bold, ), ), ), ), ), ], ), ); } } |
インジケータはcircle_page_indicatorのライブラリを使用してます。
特に特殊なことはしておらず、ページ変更がありsetStateされると該当のアイテムリストを参照しテキストを表示させている流れです。
さいごに
flutterでアニメーションは結構簡単に実装できます。
今回紹介したものか簡易的なものですが、より凝ったものも実装可能です。
なによりもアニメーションがあるUIだと、ちょっとかっこよくなりますよね。今後そういうものも記事にできたらと思います。
ABOUT ME
