Skip to content

Instantly share code, notes, and snippets.

@collinjackson
Last active August 17, 2023 20:06
Show Gist options
  • Save collinjackson/4fddbfa2830ea3ac033e34622f278824 to your computer and use it in GitHub Desktop.
Save collinjackson/4fddbfa2830ea3ac033e34622f278824 to your computer and use it in GitHub Desktop.
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,
);
},
),
),
),
),
],
),
),
);
}
}
@DK15
Copy link

DK15 commented May 8, 2019

I want to move to next screen on button tap instead of swipe. For this, I disabled physics: NeverScrollableScrollPhysics inside PageView.builder, but this leads to dots indicator not showing up on other screens. It would be great if you could let me know how do I achieve page transition on button tap and also have dots indicator show up and highlight correct dot according to current page. @collinjackson

@xihuny
Copy link

xihuny commented May 30, 2019

Dot indicator zoom is not working during pageview infinite scroll. Suppose, if I have 3 items in the list and when it scrolls to page 4 (which basically will show page 1) dot indicator is not working. Any workarounds?

@adrianvintu
Copy link

I want to move to next screen on button tap instead of swipe. For this, I disabled physics: NeverScrollableScrollPhysics inside PageView.builder, but this leads to dots indicator not showing up on other screens. It would be great if you could let me know how do I achieve page transition on button tap and also have dots indicator show up and highlight correct dot according to current page. @collinjackson

Also need an answer here.

@agueroveraalvaro
Copy link

How to rotate DOTS to VERTICAL (180º degrees) ?? Thanks!

@adrianvintu
Copy link

Is there a way to hide the dots when the keyboard is up? the dots render on top of the keyboard and it looks awful.

Thank you for your great word!

@jaggedaz
Copy link

jaggedaz commented Feb 14, 2020

Thank you for the great example!

One small issue I saw was that the dots seem to move while animating. I believe they're moving because the height of the container is changing as the dots are animating smaller/larger. I solved this by setting an explicit height on the dot container:

return new Container(
  width: _kDotSpacing,
  height: _kDotSize * _kMaxZoom,
  ...
)

I also played around with animating the opacity of the dots and this small change enabled that:

child: Material(
  color: Color.fromRGBO(color.red, color.green, color.blue, max(selectedness, 0.5)),
  ...
)

@kustraslawomir
Copy link

Dot indicator zoom is not working during pageview infinite scroll. Suppose, if I have 3 items in the list and when it scrolls to page 4 (which basically will show page 1) dot indicator is not working. Any workarounds?

You should add itemCount to PageView.builder.

new PageView.builder( itemCount: _pages.length, physics: new AlwaysScrollableScrollPhysics(),

@juskek
Copy link

juskek commented Jul 10, 2021

Thanks for the great example!
Was having null safety issues, which I fixed by adding the required modifier before the following parameters:

DotsIndicator({
    required this.controller,
    required this.itemCount,
    required this.onPageSelected,
    this.color: Colors.white,
  }) : super(listenable: controller);

(see https://stackoverflow.com/questions/64560461/the-parameter-cant-have-a-value-of-null-because-of-its-type-in-dart)

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