-
Star
(116)
You must be signed in to star a gist -
Fork
(25)
You must be signed in to fork a gist
-
-
Save collinjackson/4fddbfa2830ea3ac033e34622f278824 to your computer and use it in GitHub Desktop.
| // 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, | |
| ); | |
| }, | |
| ), | |
| ), | |
| ), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| } |
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)),
...
)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(),
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);
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!