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

feat: rounded rectangle page indicator and size transition #831

Merged
merged 2 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 27 additions & 2 deletions example/lib/pages/page_indicator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class _PageIndicatorPageState extends State<PageIndicatorPage> {

@override
Widget build(BuildContext context) {
const duration = Duration(milliseconds: 250);

return YaruScrollViewUndershoot.builder(
builder: (context, controller) {
return ListView(
Expand All @@ -37,7 +39,7 @@ class _PageIndicatorPageState extends State<PageIndicatorPage> {
size: Size.square(
index == selectedIndex ? _dotSize + 8 : _dotSize,
),
animationDuration: const Duration(milliseconds: 250),
animationDuration: duration,
),
),
const SizedBox(height: 15),
Expand All @@ -55,7 +57,30 @@ class _PageIndicatorPageState extends State<PageIndicatorPage> {
size: Size.square(
index <= selectedIndex ? _dotSize + 8 : _dotSize,
),
animationDuration: const Duration(milliseconds: 250),
animationDuration: duration,
),
),
const SizedBox(height: 20),
YaruPageIndicator.builder(
length: _length,
page: _page,
onTap: (page) => setState(() => _page = page),
itemSizeBuilder: (index, selectedIndex, length) =>
index == selectedIndex
? Size(_dotSize * 3, _dotSize)
: Size.square(_dotSize),
layoutDelegate: YaruPageIndicatorSteppedDelegate(
baseItemSpacing: _dotSpacing,
),
animationDuration: duration,
itemBuilder: (index, selectedIndex, length) =>
YaruPageIndicatorItem(
selected: index == selectedIndex,
size: index == selectedIndex
? Size(_dotSize * 3, _dotSize)
: Size.square(_dotSize),
animationDuration: duration,
borderRadius: BorderRadius.circular(24),
),
),
const SizedBox(height: 15),
Expand Down
45 changes: 43 additions & 2 deletions lib/src/widgets/yaru_page_indicator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class YaruPageIndicator extends StatelessWidget {
this.textStyle,
double? dotSize,
double? dotSpacing,
this.animationDuration,
this.animationCurve,
}) : assert(page >= 0 && page <= length - 1),
itemBuilder = null {
itemSizeBuilder =
Expand All @@ -57,6 +59,8 @@ class YaruPageIndicator extends StatelessWidget {
this.textBuilder,
this.textStyle,
this.layoutDelegate,
this.animationDuration,
this.animationCurve,
}) : assert(page >= 0 && page <= length - 1);

/// Determine the number of pages.
Expand Down Expand Up @@ -103,6 +107,17 @@ class YaruPageIndicator extends StatelessWidget {
/// Defaults to [YaruPageIndicatorSteppedDelegate].
late final YaruPageIndicatorLayoutDelegate? layoutDelegate;

/// Duration of a size transition between two items.
/// Use [Duration.zero] to disable transition.
///
/// Defaults to [Duration.zero].
final Duration? animationDuration;

/// Curve used in a size transition between two items.
///
/// Defaults to [Curves.linear].
final Curve? animationCurve;

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
Expand Down Expand Up @@ -168,7 +183,7 @@ class YaruPageIndicator extends StatelessWidget {
padding: EdgeInsetsDirectional.only(
start: index != 0 ? itemSpacing : 0,
),
child: SizedBox(
child: _buildSizedContainer(
width: itemSizes[index].width,
height: itemSizes[index].height,
child: Center(
Expand All @@ -187,6 +202,28 @@ class YaruPageIndicator extends StatelessWidget {
},
);
}

Widget _buildSizedContainer({
required double width,
required double height,
required Widget child,
}) {
final animationDuration = this.animationDuration ?? Duration.zero;
final animationCurve = this.animationCurve ?? Curves.linear;
return animationDuration != Duration.zero
? AnimatedContainer(
duration: animationDuration,
curve: animationCurve,
width: width,
height: height,
child: child,
)
: SizedBox(
width: width,
height: height,
child: child,
);
}
}

/// Default item used in [YaruPageIndicator.itemBuilder].
Expand All @@ -200,6 +237,7 @@ class YaruPageIndicatorItem extends StatelessWidget {
this.size,
this.animationDuration,
this.animationCurve,
this.borderRadius,
});

/// Define if this is a selected item.
Expand All @@ -219,6 +257,8 @@ class YaruPageIndicatorItem extends StatelessWidget {
/// Defaults to [Curves.linear].
final Curve? animationCurve;

final BorderRadiusGeometry? borderRadius;

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
Expand All @@ -227,7 +267,8 @@ class YaruPageIndicatorItem extends StatelessWidget {
color: selected
? theme.colorScheme.primary
: theme.colorScheme.onSurface.withOpacity(.3),
shape: BoxShape.circle,
shape: borderRadius == null ? BoxShape.circle : BoxShape.rectangle,
borderRadius: borderRadius,
);
final animationDuration = this.animationDuration ?? Duration.zero;
final animationCurve = this.animationCurve ?? Curves.linear;
Expand Down