Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added parameter to allow for an indefinite pager indicator behavior #28

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## NEXT
* Decimal places of the current position removed in the example
* Updated compatible dart sdk version
* Added animation parameters to animate changes in dot styling
* Added parameter to allow for an indefinite pager indicator behavior

## 3.0.0
* Changed position to int + fixed sample (Thanks to [PR#24](https://github.com/Pyozer/dots_indicator/pull/24))

Expand Down
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:lints/recommended.yaml
2 changes: 1 addition & 1 deletion example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.dots_indicator_example"
minSdkVersion 16
minSdkVersion flutter.minSdkVersion
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
2 changes: 1 addition & 1 deletion example/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
15 changes: 13 additions & 2 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:math';

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

void main() => runApp(MyApp());

Expand Down Expand Up @@ -35,7 +35,7 @@ class _MyAppState extends State<MyApp> {
}

String getPrettyCurrPosition() {
return (_currentPosition + 1.0).toStringAsPrecision(3);
return (_currentPosition + 1).toString();
}

@override
Expand Down Expand Up @@ -202,6 +202,17 @@ class _MyAppState extends State<MyApp> {
decorator: decorator,
),
]),
_buildRow([
const Text('Indefinite Pager Indicator'),
DotsIndicator(
dotsCount: _totalDots,
position: _currentPosition,
decorator: decorator,
fadeOutLastDot: true,
fadeOutDistance: 2,
animate: true,
),
]),
],
),
),
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version: 1.0.0
publish_to: 'none'

environment:
sdk: ">=2.12.0 <3.0.0"
sdk: ">=2.12.0 <4.0.0"

dependencies:
flutter:
Expand Down
18 changes: 18 additions & 0 deletions lib/src/dots_decorator.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';

const Size kDefaultSize = Size.square(9.0);
const Size kDefaultFadeOutSize = Size.square(6.0);
const EdgeInsets kDefaultSpacing = EdgeInsets.all(6.0);
const ShapeBorder kDefaultShape = CircleBorder();

Expand Down Expand Up @@ -38,6 +39,17 @@ class DotsDecorator {
/// @Default `Value of size parameter applied to each dot`
final List<Size> sizes;

/// Fade out dot size
///
/// @Default `Size.square(6.0)`
final Size fadeOutSize;

/// List of fade out dot size
/// One size by dot
///
/// @Default `Value of activeSize parameter applied to each dot`
final List<Size> fadeOutSizes;

/// Active dot size
///
/// @Default `Size.square(9.0)`
Expand Down Expand Up @@ -88,6 +100,8 @@ class DotsDecorator {
this.activeColors = const [],
this.size = kDefaultSize,
this.sizes = const [],
this.fadeOutSize = kDefaultFadeOutSize,
this.fadeOutSizes = const [],
this.activeSize = kDefaultSize,
this.activeSizes = const [],
this.shape = kDefaultShape,
Expand All @@ -106,6 +120,10 @@ class DotsDecorator {
return colors.isNotEmpty ? colors[index] : color;
}

Size getFadeOutSize(int index) {
return fadeOutSizes.isNotEmpty ? fadeOutSizes[index] : fadeOutSize;
}

Size getActiveSize(int index) {
return activeSizes.isNotEmpty ? activeSizes[index] : activeSize;
}
Expand Down
97 changes: 78 additions & 19 deletions lib/src/dots_indicator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,31 @@ import 'dart:math';
import 'package:dots_indicator/src/dots_decorator.dart';
import 'package:flutter/material.dart';

typedef void OnTap(int position);
typedef OnTap = void Function(int position);

class DotsIndicator extends StatelessWidget {
final int dotsCount, position;

/// If true, the last dot will fade out when the position is at the last dot.
final bool fadeOutLastDot;

/// Distance from currently selected dot to the one that fades out.
final int fadeOutDistance;
final DotsDecorator decorator;
final Axis axis;
final bool reversed;
final OnTap? onTap;
final MainAxisSize mainAxisSize;
final MainAxisAlignment mainAxisAlignment;

/// If true, the dots will animate when the position changes.
final bool animate;

/// Duration of the animation when the position changes.
final Duration animationDuration;

DotsIndicator({
Key? key,
super.key,
required this.dotsCount,
this.position = 0,
this.decorator = const DotsDecorator(),
Expand All @@ -26,6 +38,10 @@ class DotsIndicator extends StatelessWidget {
this.mainAxisSize = MainAxisSize.min,
this.mainAxisAlignment = MainAxisAlignment.center,
this.onTap,
this.fadeOutLastDot = false,
this.fadeOutDistance = 0,
this.animate = false,
this.animationDuration = const Duration(milliseconds: 200),
}) : assert(dotsCount > 0, 'dotsCount must be superior to zero'),
assert(position >= 0, 'position must be superior or equals to zero'),
assert(
Expand Down Expand Up @@ -59,7 +75,14 @@ class DotsIndicator extends StatelessWidget {
decorator.activeShapes.length == dotsCount,
"activeShapes param in decorator must empty or have same length as dotsCount parameter",
),
super(key: key);
assert(
fadeOutLastDot == false || fadeOutDistance > 0,
"fadeOutDistace must be superior to zero when fadeOutLastDot is true",
),
assert(
fadeOutDistance < dotsCount,
"fadeOutDistace must be inferior to dotsCount",
);

Widget _wrapInkwell(Widget dot, int index) {
return InkWell(
Expand All @@ -72,30 +95,66 @@ class DotsIndicator extends StatelessWidget {
}

Widget _buildDot(BuildContext context, int index) {
final double lerpValue = min(1, (position - index).abs()).toDouble();
final int absPositionIndexRelation = (position - index).abs();
final bool isCurrentlyVisible = absPositionIndexRelation <= fadeOutDistance;

final size = Size.lerp(
final double lerpValue = min(1, absPositionIndexRelation).toDouble();

Size size = Size.lerp(
decorator.getActiveSize(index),
decorator.getSize(index),
lerpValue,
)!;

if (fadeOutLastDot && absPositionIndexRelation >= fadeOutDistance) {
size = Size.lerp(
decorator.getSize(index),
decorator.getFadeOutSize(index),
absPositionIndexRelation == fadeOutDistance ? 1 : 0.0,
)!;
}

final dot = Container(
width: size.width,
height: size.height,
margin: decorator.spacing,
decoration: ShapeDecoration(
color: Color.lerp(
decorator.getActiveColor(index) ?? Theme.of(context).primaryColor,
decorator.getColor(index),
lerpValue,
height: fadeOutLastDot && isCurrentlyVisible
? max(
max(decorator.getActiveSize(index).height,
decorator.getSize(index).height),
decorator.getFadeOutSize(index).height,
) +
(axis == Axis.horizontal
? decorator.spacing.vertical
: decorator.spacing.horizontal)
: null,
child: Center(
child: AnimatedOpacity(
duration: animate ? animationDuration : Duration.zero,
opacity:
!fadeOutLastDot || absPositionIndexRelation <= fadeOutDistance
? 1.0
: 0.0,
child: AnimatedContainer(
duration: animate ? animationDuration : Duration.zero,
width: size.width,
height: size.height,
margin: fadeOutLastDot && !isCurrentlyVisible
? EdgeInsets.all(0)
: decorator.spacing,
decoration: ShapeDecoration(
color: Color.lerp(
decorator.getActiveColor(index) ??
Theme.of(context).primaryColor,
decorator.getColor(index),
lerpValue,
),
shape: ShapeBorder.lerp(
decorator.getActiveShape(index),
decorator.getShape(index),
lerpValue,
)!,
shadows: decorator.shadows,
),
),
),
shape: ShapeBorder.lerp(
decorator.getActiveShape(index),
decorator.getShape(index),
lerpValue,
)!,
shadows: decorator.shadows,
),
);
return onTap == null ? dot : _wrapInkwell(dot, index);
Expand Down
4 changes: 3 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ version: 3.0.0
homepage: https://github.com/pyozer/dots_indicator

environment:
sdk: '>=2.12.0 <3.0.0'
sdk: '>=2.12.0 <4.0.0'

dependencies:
flutter:
sdk: flutter
dev_dependencies:
lints: ^4.0.0