Instantly share code, notes, and snippets.

Embed
What would you like to do?
PageView example with dots indicator
// Copyright 2017, the Flutter project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
/// An indicator showing the currently selected page of a PageController
class DotsIndicator extends AnimatedWidget {
DotsIndicator({
this.controller,
this.itemCount,
this.onPageSelected,
this.color: Colors.white,
}) : super(listenable: controller);
/// The PageController that this DotsIndicator is representing.
final PageController controller;
/// The number of items managed by the PageController
final int itemCount;
/// Called when a dot is tapped
final ValueChanged<int> onPageSelected;
/// The color of the dots.
///
/// Defaults to `Colors.white`.
final Color color;
// The base size of the dots
static const double _kDotSize = 8.0;
// The increase in the size of the selected dot
static const double _kMaxZoom = 2.0;
// The distance between the center of each dot
static const double _kDotSpacing = 25.0;
Widget _buildDot(int index) {
double selectedness = Curves.easeOut.transform(
max(
0.0,
1.0 - ((controller.page ?? controller.initialPage) - index).abs(),
),
);
double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
return new Container(
width: _kDotSpacing,
child: new Center(
child: new Material(
color: color,
type: MaterialType.circle,
child: new Container(
width: _kDotSize * zoom,
height: _kDotSize * zoom,
child: new InkWell(
onTap: () => onPageSelected(index),
),
),
),
),
);
}
Widget build(BuildContext context) {
return new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: new List<Widget>.generate(itemCount, _buildDot),
);
}
}
class MyHomePage extends StatefulWidget {
@override
State createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
final _controller = new PageController();
static const _kDuration = const Duration(milliseconds: 300);
static const _kCurve = Curves.ease;
final _kArrowColor = Colors.black.withOpacity(0.8);
final List<Widget> _pages = <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlutterLogo(colors: Colors.blue),
),
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlutterLogo(style: FlutterLogoStyle.stacked, colors: Colors.red),
),
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlutterLogo(style: FlutterLogoStyle.horizontal, colors: Colors.green),
),
];
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new IconTheme(
data: new IconThemeData(color: _kArrowColor),
child: new Stack(
children: <Widget>[
new PageView.builder(
physics: new AlwaysScrollableScrollPhysics(),
controller: _controller,
itemBuilder: (BuildContext context, int index) {
return _pages[index % _pages.length];
},
),
new Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: new Container(
color: Colors.grey[800].withOpacity(0.5),
padding: const EdgeInsets.all(20.0),
child: new Center(
child: new DotsIndicator(
controller: _controller,
itemCount: _pages.length,
onPageSelected: (int page) {
_controller.animateToPage(
page,
duration: _kDuration,
curve: _kCurve,
);
},
),
),
),
),
],
),
),
);
}
}
@collinjackson

This comment has been minimized.

Owner

collinjackson commented May 30, 2017

It looks like this.
screencast

@Hixie

This comment has been minimized.

Hixie commented May 30, 2017

@collinjackson you should chat with @HansMuller about putting this in our samples.

@manujbahl

This comment has been minimized.

manujbahl commented Apr 14, 2018

need to add itemCount: _pages.length, to the PageView,builder else it does an infinite scroll on the right.

@manujbahl

This comment has been minimized.

manujbahl commented Apr 14, 2018

Also found an issue with probably with PageViewBuilder. If I add a onTap to the page content there is one situation where the tap event does not fire. I set the viewport size to 0.5 so that the right and left elements show partly. Happens when you are on page 0 and tap on page 1. In every other case Tap events are fired for any of the pages.

Below is the code I used.

@manujbahl

This comment has been minimized.

manujbahl commented Apr 14, 2018

        // Copyright 2017, the Flutter project authors.  Please see the AUTHORS file
        // for details. All rights reserved. Use of this source code is governed by a
        // BSD-style license that can be found in the LICENSE file.

        import 'dart:math';
        import 'package:flutter/material.dart';

        void main() {
          runApp(new MyApp());
        }

        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return new MaterialApp(
              title: 'Flutter Demo',
              home: new MyHomePage(),
              debugShowCheckedModeBanner: false,
            );
          }
        }

        /// An indicator showing the currently selected page of a PageController
        class DotsIndicator extends AnimatedWidget {
          DotsIndicator({
            this.controller,
            this.itemCount,
            this.onPageSelected,
            this.color: Colors.white,
          }) : super(listenable: controller);

          /// The PageController that this DotsIndicator is representing.
          final PageController controller;

          /// The number of items managed by the PageController
          final int itemCount;

          /// Called when a dot is tapped
          final ValueChanged<int> onPageSelected;

          /// The color of the dots.
          ///
          /// Defaults to `Colors.white`.
          final Color color;

          // The base size of the dots
          static const double _kDotSize = 8.0;

          // The increase in the size of the selected dot
          static const double _kMaxZoom = 2.0;

          // The distance between the center of each dot
          static const double _kDotSpacing = 25.0;

          Widget _buildDot(int index) {
            double selectedness = Curves.easeOut.transform(
              max(
                0.0,
                1.0 - ((controller.page ?? controller.initialPage) - index).abs(),
              ),
            );
            double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
            return new Container(
              width: _kDotSpacing,
              child: new Center(
                child: new Material(
                  color: color,
                  type: MaterialType.circle,
                  child: new Container(
                    width: _kDotSize * zoom,
                    height: _kDotSize * zoom,
                    child: new InkWell(
                      onTap: () => onPageSelected(index),
                    ),
                  ),
                ),
              ),
            );
          }

          Widget build(BuildContext context) {
            return new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: new List<Widget>.generate(itemCount, _buildDot),
            );
          }
        }

        class MyHomePage extends StatefulWidget {
          @override
          State createState() => new MyHomePageState();
        }

        class MyHomePageState extends State<MyHomePage> {

          final _controller = new PageController(viewportFraction: 0.5);

          static const _kDuration = const Duration(milliseconds: 300);

          static const _kCurve = Curves.ease;

          final _kArrowColor = Colors.black.withOpacity(0.8);

          static onTap(index) {
            print("$index selected.");
          }

          final List<Widget> _pages = <Widget>[
            new FlutterLogo(colors: Colors.blue),
            new FlutterLogo(style: FlutterLogoStyle.stacked, colors: Colors.red),
            new FlutterLogo(style: FlutterLogoStyle.horizontal, colors: Colors.green),
          ];

            Widget _buildPageItem(BuildContext context, int index) {
              return new Page(page: _pages[index], idx: index);
            }

          @override
          Widget build(BuildContext context) {
            return new Scaffold(
              body: new IconTheme(
                data: new IconThemeData(color: _kArrowColor),
                child: new Stack(
                  children: <Widget>[
                    new PageView.builder(
                      physics: new AlwaysScrollableScrollPhysics(),
                      controller: _controller,
                      itemCount: _pages.length,
                      itemBuilder: (BuildContext context, int index) {
                        return _buildPageItem(context, index % _pages.length);
                      },
                    ),
                    new Positioned(
                      bottom: 0.0,
                      left: 0.0,
                      right: 0.0,
                      child: new Container(
                        color: Colors.grey[800].withOpacity(0.5),
                        padding: const EdgeInsets.all(20.0),
                        child: new Center(
                          child: new DotsIndicator(
                            controller: _controller,
                            itemCount: _pages.length,
                            onPageSelected: (int page) {
                              _controller.animateToPage(
                                page,
                                duration: _kDuration,
                                curve: _kCurve,
                              );
                            },
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            );
          }
        }

        class Page extends StatelessWidget {
          final page;
          final idx;

          Page({
            @required this.page,
            @required this.idx,
          });

          onTap() {
            print("${this.idx} selected.");
          }

          @override
          Widget build(BuildContext context) {
            return new Padding(
              padding: const EdgeInsets.all(8.0),
              child: new Column(
                mainAxisSize: MainAxisSize.min,
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  new Container(
                      height: 200.0,
                    child: new Card(
                      child: new Stack(
                        fit: StackFit.expand,
                        children: <Widget>[
                          this.page,
                          new Material(
                            type: MaterialType.transparency,
                            child: new InkWell(onTap: this.onTap),
                          ),
                        ],
                      ),
                    ),
                ],
              ),
            );
          }
        }
@biniama

This comment has been minimized.

biniama commented May 17, 2018

Bug fixed code for the ^^ above code by manujbahl

// Copyright 2017, the Flutter project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new MyHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

/// An indicator showing the currently selected page of a PageController
class DotsIndicator extends AnimatedWidget {
  DotsIndicator({
    this.controller,
    this.itemCount,
    this.onPageSelected,
    this.color: Colors.white,
  }) : super(listenable: controller);

  /// The PageController that this DotsIndicator is representing.
  final PageController controller;

  /// The number of items managed by the PageController
  final int itemCount;

  /// Called when a dot is tapped
  final ValueChanged<int> onPageSelected;

  /// The color of the dots.
  ///
  /// Defaults to `Colors.white`.
  final Color color;

  // The base size of the dots
  static const double _kDotSize = 8.0;

  // The increase in the size of the selected dot
  static const double _kMaxZoom = 2.0;

  // The distance between the center of each dot
  static const double _kDotSpacing = 25.0;

  Widget _buildDot(int index) {
    double selectedness = Curves.easeOut.transform(
      max(
        0.0,
        1.0 - ((controller.page ?? controller.initialPage) - index).abs(),
      ),
    );
    double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
    return new Container(
      width: _kDotSpacing,
      child: new Center(
        child: new Material(
          color: color,
          type: MaterialType.circle,
          child: new Container(
            width: _kDotSize * zoom,
            height: _kDotSize * zoom,
            child: new InkWell(
              onTap: () => onPageSelected(index),
            ),
          ),
        ),
      ),
    );
  }

  Widget build(BuildContext context) {
    return new Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: new List<Widget>.generate(itemCount, _buildDot),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  final _controller = new PageController(viewportFraction: 0.5);

  static const _kDuration = const Duration(milliseconds: 300);

  static const _kCurve = Curves.ease;

  final _kArrowColor = Colors.black.withOpacity(0.8);

  static onTap(index) {
    print("$index selected.");
  }

  final List<Widget> _pages = <Widget>[
    new FlutterLogo(colors: Colors.blue),
    new FlutterLogo(style: FlutterLogoStyle.stacked, colors: Colors.red),
    new FlutterLogo(style: FlutterLogoStyle.horizontal, colors: Colors.green),
  ];

  Widget _buildPageItem(BuildContext context, int index) {
    return new Page(page: _pages[index], idx: index);
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new IconTheme(
        data: new IconThemeData(color: _kArrowColor),
        child: new Stack(
          children: <Widget>[
            new PageView.builder(
              physics: new AlwaysScrollableScrollPhysics(),
              controller: _controller,
              itemCount: _pages.length,
              itemBuilder: (BuildContext context, int index) {
                return _buildPageItem(context, index % _pages.length);
              },
            ),
            new Positioned(
              bottom: 0.0,
              left: 0.0,
              right: 0.0,
              child: new Container(
                color: Colors.grey[800].withOpacity(0.5),
                padding: const EdgeInsets.all(20.0),
                child: new Center(
                  child: new DotsIndicator(
                    controller: _controller,
                    itemCount: _pages.length,
                    onPageSelected: (int page) {
                      _controller.animateToPage(
                        page,
                        duration: _kDuration,
                        curve: _kCurve,
                      );
                    },
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class Page extends StatelessWidget {
  final page;
  final idx;

  Page({
    @required this.page,
    @required this.idx,
  });

  onTap() {
    print("${this.idx} selected.");
  }

  @override
  Widget build(BuildContext context) {
    return new Padding(
      padding: const EdgeInsets.all(8.0),
      child: new Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          new Container(
            height: 200.0,
            child: new Card(
              child: new Stack(
                fit: StackFit.expand,
                children: <Widget>[
                  this.page,
                  new Material(
                    type: MaterialType.transparency,
                    child: new InkWell(onTap: this.onTap),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
@tanphathuynh

This comment has been minimized.

tanphathuynh commented Jul 13, 2018

Hi, how do I click on the PageView and make it auto load through the whole list of image widgets?

@luke-z

This comment has been minimized.

luke-z commented Sep 18, 2018

Amazing, thank you!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment