よくあるPageViewを実装してみたかった。
一番最後にはIndicatorをボタンに変化させてよくある説明ページみたいなのを実装しています。
今回使用したライブラリーは以下の二つです。
メインのIndicator↓
https://pub.dev/packages/flutter_page_indicator
Indicatorのアニメーション↓
https://pub.dev/packages/transformer_page_view
パッケージの準備
パッケージのインストール
pubspec.yaml
dependencies:
flutter_page_indicator: ^0.0.3
transformer_page_view: ^0.1.6
上記を記載後
packages getを実行
パッケージのインポート
main.dart
import 'package:flutter_page_indicator/flutter_page_indicator.dart';
import "package:transformer_page_view/transformer_page_view.dart";
実装
全体コード
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_page_indicator/flutter_page_indicator.dart';
import "package:transformer_page_view/transformer_page_view.dart";
class MyAppView extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MyHomePage();
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double size = 20.0;
double activeSize = 30.0;
PageController controller;
bool loop = false;
bool _visible1 = true;
var page_count = 1;
void pageNumberChange(page) {
setState(() {
page_count = page + 1;
});
}
@override
void initState() {
controller = new PageController();
super.initState();
}
@override
void didUpdateWidget(MyHomePage oldWidget) {
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
var children = <Widget>[
new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明1アプリの説明1",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
Navigator.pushNamed(context, '/test');
},
child: Text("1枚目"),
),
),
Text(
"アプリの説明1アプリの説明1アプリの説明1アプリの説明1アプリの説明1",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),
new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明2アプリの説明2",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
Navigator.pushNamed(context, '/test');
},
child: Text("2枚目"),
),
),
Text(
"アプリの説明2アプリの説明2アプリの説明2アプリの説明2アプリの説明2",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),
new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明3アプリの説明3",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
},
child: Text("3枚目"),
),
),
Text(
"アプリの説明3アプリの説明3アプリの説明3アプリの説明3アプリの説明3",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明4アプリの説明4",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
},
child: Text("4枚目"),
),
),
Text(
"アプリの説明4アプリの説明4アプリの説明4アプリの説明4アプリの説明4",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),
];
//Widgetを生成
return new Scaffold(
appBar: new AppBar(
title: new Text("アプリ説明ページ"),
),
body: new Column(
children: <Widget>[
new Expanded(
child: new Stack(
children: <Widget>[
loop ? new TransformerPageView.children(
children: children,
pageController: controller,
):
new PageView(
onPageChanged: (page_count){
pageNumberChange(page_count);
if(page_count + 1 == children.length){
_visible1 = false;
}else{
_visible1 = true;
}
},
controller: controller,
children:children ,
),
new Align(
alignment: Alignment.bottomCenter,
child: new Padding(
padding: new EdgeInsets.only(bottom: 20.0),
child: new Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: new Padding(
padding: new EdgeInsets.only(bottom: 20.0),
child: AnimatedOpacity(
opacity: _visible1 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: PageIndicator(
layout: PageIndicatorLayout.NONE,
size: size,
activeSize:activeSize,
controller: controller,
space: 20,
count: 4,
color: Colors.black,
activeColor: Colors.red,
),
),
)
),
Align(
alignment: Alignment.bottomCenter,
child: new Padding(
padding: new EdgeInsets.only(bottom: 20.0),
child: AnimatedOpacity(
opacity: _visible1 ? 0.0 : 1.0,
duration: Duration(milliseconds: 500),
child:SizedBox(
width: 300,
height: 50,
child: RaisedButton(
color: Colors.blue,
onPressed: () {
Navigator.pushNamed(
context,
'/hello'
);
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))
),
child: Text(
"はじめる",
style: TextStyle(
fontSize: 20.0,
color: Colors.white
),
),
),
),
),
),
)
],
)
),
)
],
)
)
],
)
);
}
}
部分別の解説
①説明ページのWidgetを配列にそ追加
children
に4つの説明ページを入れています。
(ここほんとはメソッドでループ回したい…)
main.dart
//省略
@override
Widget build(BuildContext context) {
var children = <Widget>[
new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明1アプリの説明1",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
Navigator.pushNamed(context, '/test');
},
child: Text("1枚目"),
),
),
Text(
"アプリの説明1アプリの説明1アプリの説明1アプリの説明1アプリの説明1",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),
new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明2アプリの説明2",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
Navigator.pushNamed(context, '/test');
},
child: Text("2枚目"),
),
),
Text(
"アプリの説明2アプリの説明2アプリの説明2アプリの説明2アプリの説明2",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),
new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明3アプリの説明3",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
},
child: Text("3枚目"),
),
),
Text(
"アプリの説明3アプリの説明3アプリの説明3アプリの説明3アプリの説明3",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),new Column(
children: <Widget>[
Container(
height: 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"アプリの説明4アプリの説明4",
style: TextStyle(
fontSize: 24.0,
color: Colors.black54
),
),
],
),
),
Container(
width: 300,
height: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
width: 300,
height: 200,
child: FlatButton(
color: Colors.black54,
onPressed: () {
},
child: Text("4枚目"),
),
),
Text(
"アプリの説明4アプリの説明4アプリの説明4アプリの説明4アプリの説明4",
style: TextStyle(
fontSize: 14.0,
color: Colors.black54
),
),
],
),
),
],
),
];
②説明ページの配置と、下部にIndicator、ボタンの配置
PageIndicator
とFlatButton
をStack構造
にして、
PageView
のonPageChanged
メソッドで最終ページにスワイプ時
アニメーションでボタンに切り替えるようにしています。
main.dart
return new Scaffold(
appBar: new AppBar(
title: new Text("アプリ説明ページ"),
),
body: new Column(
children: <Widget>[
new Expanded(
child: new Stack(
children: <Widget>[
loop ? new TransformerPageView.children(
children: children,
pageController: controller,
):
new PageView(
onPageChanged: (page_count){
pageNumberChange(page_count);
if(page_count + 1 == children.length){
_visible1 = false;
}else{
_visible1 = true;
}
},
controller: controller,
children:children ,
),
new Align(
alignment: Alignment.bottomCenter,
child: new Padding(
padding: new EdgeInsets.only(bottom: 20.0),
child: new Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: new Padding(
padding: new EdgeInsets.only(bottom: 20.0),
child: AnimatedOpacity(
opacity: _visible1 ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: PageIndicator(
layout: PageIndicatorLayout.NONE,
size: size,
activeSize:activeSize,
controller: controller,
space: 20,
count: 4,
color: Colors.black,
activeColor: Colors.red,
),
),
)
),
Align(
alignment: Alignment.bottomCenter,
child: new Padding(
padding: new EdgeInsets.only(bottom: 20.0),
child: AnimatedOpacity(
opacity: _visible1 ? 0.0 : 1.0,
duration: Duration(milliseconds: 500),
child:SizedBox(
width: 300,
height: 50,
child: RaisedButton(
color: Colors.blue,
onPressed: () {
Navigator.pushNamed(
context,
'/hello'
);
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5.0))
),
child: Text(
"はじめる",
style: TextStyle(
fontSize: 20.0,
color: Colors.white
),
),
),
),
),
),
)
],
)
),
)
],
)
)
],
)
);
完成したもの
ボタンの遷移のところは、
PageIndicator
のlayout
を変更すればいろんなアニメーションになります。
詳しくはパッケージのドキュメントを参照してください!
最後に
ネストがすごい深くなってしまったので反省しています。
children
のところとか他の部分もいろいろ直せる部分多いと思うので、
ぜひご指摘ください。
Flutter楽しいです。