Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

FlutterでPageViewにPageIndicatorを実装してみた

よくあるPageViewを実装してみたかった。

↓こういうやつ
スクリーンショット 2019-11-06 13.44.28.png

一番最後には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、ボタンの配置

PageIndicatorFlatButtonStack構造にして、
PageViewonPageChangedメソッドで最終ページにスワイプ時
アニメーションでボタンに切り替えるようにしています。

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
                                          ),
                                        ),
                                      ),
                                    ),
                                  ),
                              ),
                            )
                          ],
                        )
                      ),
                    )
                  ],
                )
            )
          ],
        )
    );

完成したもの

simulator-compressor.gif

ボタンの遷移のところは、
PageIndicatorlayoutを変更すればいろんなアニメーションになります。
詳しくはパッケージのドキュメントを参照してください!

最後に

ネストがすごい深くなってしまったので反省しています。
childrenのところとか他の部分もいろいろ直せる部分多いと思うので、
ぜひご指摘ください。

Flutter楽しいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
8
Help us understand the problem. What are the problem?