You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/web/filipino_cuisine/lib/flutter_page_indicator.dart

347 lines
9.6 KiB

// Package flutter_page_indicator:
// https://pub.dartlang.org/packages/flutter_page_indicator
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class WarmPainter extends BasePainter {
WarmPainter(PageIndicator widget, double page, int index, Paint paint)
: super(widget, page, index, paint);
@override
void draw(Canvas canvas, double space, double size, double radius) {
double progress = page - index;
double distance = size + space;
double start = index * (size + space);
if (progress > 0.5) {
double right = start + size + distance;
//progress=>0.5-1.0
//left:0.0=>distance
double left = index * distance + distance * (progress - 0.5) * 2;
canvas.drawRRect(
RRect.fromLTRBR(left, 0.0, right, size, Radius.circular(radius)),
_paint);
} else {
double right = start + size + distance * progress * 2;
canvas.drawRRect(
RRect.fromLTRBR(start, 0.0, right, size, Radius.circular(radius)),
_paint);
}
}
}
class DropPainter extends BasePainter {
DropPainter(PageIndicator widget, double page, int index, Paint paint)
: super(widget, page, index, paint);
@override
void draw(Canvas canvas, double space, double size, double radius) {
double progress = page - index;
double dropHeight = widget.dropHeight;
double rate = (0.5 - progress).abs() * 2;
double scale = widget.scale;
//lerp(begin, end, progress)
canvas.drawCircle(
Offset(radius + ((page) * (size + space)),
radius - dropHeight * (1 - rate)),
radius * (scale + rate * (1.0 - scale)),
_paint);
}
}
class NonePainter extends BasePainter {
NonePainter(PageIndicator widget, double page, int index, Paint paint)
: super(widget, page, index, paint);
@override
void draw(Canvas canvas, double space, double size, double radius) {
double progress = page - index;
double secondOffset = index == widget.count - 1
? radius
: radius + ((index + 1) * (size + space));
if (progress > 0.5) {
canvas.drawCircle(Offset(secondOffset, radius), radius, _paint);
} else {
canvas.drawCircle(
Offset(radius + (index * (size + space)), radius), radius, _paint);
}
}
}
class SlidePainter extends BasePainter {
SlidePainter(PageIndicator widget, double page, int index, Paint paint)
: super(widget, page, index, paint);
@override
void draw(Canvas canvas, double space, double size, double radius) {
canvas.drawCircle(
Offset(radius + (page * (size + space)), radius), radius, _paint);
}
}
class ScalePainter extends BasePainter {
ScalePainter(PageIndicator widget, double page, int index, Paint paint)
: super(widget, page, index, paint);
// 连续的两个点,含有最后一个和第一个
@override
bool _shouldSkip(int index) {
if (super.index == widget.count - 1) {
return index == 0 || index == super.index;
}
return (index == super.index || index == super.index + 1);
}
@override
void paint(Canvas canvas, Size size) {
_paint.color = widget.color;
double space = widget.space;
double size = widget.size;
double radius = size / 2;
for (int i = 0, c = widget.count; i < c; ++i) {
if (_shouldSkip(i)) {
continue;
}
canvas.drawCircle(Offset(i * (size + space) + radius, radius),
radius * widget.scale, _paint);
}
_paint.color = widget.activeColor;
draw(canvas, space, size, radius);
}
@override
void draw(Canvas canvas, double space, double size, double radius) {
double secondOffset = index == widget.count - 1
? radius
: radius + ((index + 1) * (size + space));
double progress = page - index;
_paint.color = Color.lerp(widget.activeColor, widget.color, progress);
//last
canvas.drawCircle(Offset(radius + (index * (size + space)), radius),
lerp(radius, radius * widget.scale, progress), _paint);
//first
_paint.color = Color.lerp(widget.color, widget.activeColor, progress);
canvas.drawCircle(Offset(secondOffset, radius),
lerp(radius * widget.scale, radius, progress), _paint);
}
}
class ColorPainter extends BasePainter {
ColorPainter(PageIndicator widget, double page, int index, Paint paint)
: super(widget, page, index, paint);
// 连续的两个点,含有最后一个和第一个
@override
bool _shouldSkip(int index) {
if (super.index == widget.count - 1) {
return index == 0 || index == super.index;
}
return (index == super.index || index == super.index + 1);
}
@override
void draw(Canvas canvas, double space, double size, double radius) {
double progress = page - index;
double secondOffset = index == widget.count - 1
? radius
: radius + ((index + 1) * (size + space));
_paint.color = Color.lerp(widget.activeColor, widget.color, progress);
//left
canvas.drawCircle(
Offset(radius + (index * (size + space)), radius), radius, _paint);
//right
_paint.color = Color.lerp(widget.color, widget.activeColor, progress);
canvas.drawCircle(Offset(secondOffset, radius), radius, _paint);
}
}
abstract class BasePainter extends CustomPainter {
final PageIndicator widget;
final double page;
final int index;
final Paint _paint;
double lerp(double begin, double end, double progress) {
return begin + (end - begin) * progress;
}
BasePainter(this.widget, this.page, this.index, this._paint);
void draw(Canvas canvas, double space, double size, double radius);
bool _shouldSkip(int index) {
return false;
}
//double secondOffset = index == widget.count-1 ? radius : radius + ((index + 1) * (size + space));
@override
void paint(Canvas canvas, Size size) {
_paint.color = widget.color;
double space = widget.space;
double size = widget.size;
double radius = size / 2;
for (int i = 0, c = widget.count; i < c; ++i) {
if (_shouldSkip(i)) {
continue;
}
canvas.drawCircle(
Offset(i * (size + space) + radius, radius), radius, _paint);
}
double page = this.page;
if (page < index) {
page = 0.0;
}
_paint.color = widget.activeColor;
draw(canvas, space, size, radius);
}
@override
bool shouldRepaint(BasePainter oldDelegate) {
return oldDelegate.page != page;
}
}
class _PageIndicatorState extends State<PageIndicator> {
int index = 0;
final Paint _paint = Paint();
BasePainter _createPainer() {
switch (widget.layout) {
case PageIndicatorLayout.NONE:
return NonePainter(
widget, widget.controller.page ?? 0.0, index, _paint);
case PageIndicatorLayout.SLIDE:
return SlidePainter(
widget, widget.controller.page ?? 0.0, index, _paint);
case PageIndicatorLayout.WARM:
return WarmPainter(
widget, widget.controller.page ?? 0.0, index, _paint);
case PageIndicatorLayout.COLOR:
return ColorPainter(
widget, widget.controller.page ?? 0.0, index, _paint);
case PageIndicatorLayout.SCALE:
return ScalePainter(
widget, widget.controller.page ?? 0.0, index, _paint);
case PageIndicatorLayout.DROP:
return DropPainter(
widget, widget.controller.page ?? 0.0, index, _paint);
default:
throw Exception("Not a valid layout");
}
}
@override
Widget build(BuildContext context) {
Widget child = SizedBox(
width: widget.count * widget.size + (widget.count - 1) * widget.space,
height: widget.size,
child: CustomPaint(
painter: _createPainer(),
),
);
if (widget.layout == PageIndicatorLayout.SCALE ||
widget.layout == PageIndicatorLayout.COLOR) {
child = ClipRect(
child: child,
);
}
return IgnorePointer(
child: child,
);
}
void _onController() {
double page = widget.controller.page ?? 0.0;
index = page.floor();
setState(() {});
}
@override
void initState() {
widget.controller.addListener(_onController);
super.initState();
}
@override
void didUpdateWidget(PageIndicator oldWidget) {
if (widget.controller != oldWidget.controller) {
oldWidget.controller.removeListener(_onController);
widget.controller.addListener(_onController);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
widget.controller.removeListener(_onController);
super.dispose();
}
}
// ignore: constant_identifier_names
enum PageIndicatorLayout { NONE, SLIDE, WARM, COLOR, SCALE, DROP }
class PageIndicator extends StatefulWidget {
/// size of the dots
final double size;
/// space between dots.
final double space;
/// count of dots
final int count;
/// active color
final Color activeColor;
/// normal color
final Color color;
/// layout of the dots,default is [PageIndicatorLayout.SLIDE]
final PageIndicatorLayout layout;
// Only valid when layout==PageIndicatorLayout.scale
final double scale;
// Only valid when layout==PageIndicatorLayout.drop
final double dropHeight;
final PageController controller;
final double activeSize;
const PageIndicator(
{Key key,
this.size = 20.0,
this.space = 5.0,
@required this.count,
this.activeSize = 20.0,
@required this.controller,
this.color = Colors.white30,
this.layout = PageIndicatorLayout.SLIDE,
this.activeColor = Colors.white,
this.scale = 0.6,
this.dropHeight = 20.0})
: assert(count != null),
assert(controller != null),
super(key: key);
@override
State<StatefulWidget> createState() {
return _PageIndicatorState();
}
}