Skip to content
This repository was archived by the owner on Dec 5, 2023. It is now read-only.
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
##[4.0.0] - 15 June 2023

* Added different button layout for text buttons.
* ButtonBuilder now provides an extra paramter with the button type.

##[3.0.0] - 2 June 2023

* Remove `rive` dependency
Expand Down
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class MyApp extends StatelessWidget {
buttonMode: IntroductionScreenButtonMode.text,
indicatorMode: IndicatorMode.dash,
skippable: true,
buttonBuilder: (context, onPressed, child) =>
buttonBuilder: (context, onPressed, child, buttonType) =>
ElevatedButton(onPressed: onPressed, child: child),
),
onComplete: () {
Expand Down
23 changes: 22 additions & 1 deletion lib/src/config/introduction.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ enum IntroductionDisplayMode {
multiPageHorizontal
}

enum IntroductionControlMode {
previousNextButton,
singleButton,
}

enum IntroductionButtonType {
next,
previous,
finish,
skip,
}

class IntroductionPage {
final Widget? title;
final Widget? text;
Expand Down Expand Up @@ -98,9 +110,17 @@ class IntroductionOptions {
///
final IntroductionDisplayMode displayMode;

/// When [IntroductionDisplayMode.multiPageHorizontal] is selected multiple controlMode can be selected.
/// [IntroductionControlMode.previousNextButton] shows two buttons at the bottom of the screen to return or proceed. The skip button is placed at the top left of the screen.
/// [IntroductionControlMode.singleButton] contains one button at the bottom of the screen to proceed. Underneath is clickable text to skip if the current page is the first page. If the current page is any different it return to the previous screen.
///
final IntroductionControlMode controlMode;

/// A builder that can be used to replace the default text buttons when
/// [IntroductionScreenButtonMode.text] is provided to [buttonMode]
final Widget Function(BuildContext, VoidCallback, Widget)? buttonBuilder;
final Widget Function(
BuildContext, VoidCallback, Widget, IntroductionButtonType)?
buttonBuilder;

/// The translations for all buttons on the introductionpages
///
Expand All @@ -125,6 +145,7 @@ class IntroductionOptions {
this.displayMode = IntroductionDisplayMode.multiPageHorizontal,
this.skippable = false,
this.buttonBuilder,
this.controlMode = IntroductionControlMode.previousNextButton,
}) : assert(
!(identical(indicatorMode, IndicatorMode.custom) &&
indicatorBuilder == null),
Expand Down
206 changes: 171 additions & 35 deletions lib/src/types/page_introduction.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,28 +101,34 @@ class _MultiPageIntroductionScreenState
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: SizedBox(
height: 64,
child: AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (widget.options.skippable && !_isLast(pages)) ...[
TextButton(
onPressed: widget.onComplete,
child: Text(translations.skipButton),
),
if (widget.options.controlMode ==
IntroductionControlMode.previousNextButton) ...[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: SizedBox(
height: 64,
child: AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (widget.options.skippable &&
!_isLast(pages)) ...[
TextButton(
onPressed: widget.onComplete,
child: Text(translations.skipButton),
),
],
],
],
);
},
);
},
),
),
),
),
] else ...[
const SizedBox.shrink()
],
Align(
alignment: Alignment.bottomCenter,
child: Column(
Expand All @@ -141,20 +147,40 @@ class _MultiPageIntroductionScreenState
padding: const EdgeInsets.all(32),
child: AnimatedBuilder(
animation: _controller,
builder: (context, _) => IntroductionButtons(
controller: _controller,
next: _isNext(pages),
previous: _isPrevious,
last: _isLast(pages),
options: widget.options,
onFinish: widget.onComplete,
onNext: () {
widget.onNext?.call(pages[_currentPage.value]);
},
onPrevious: () {
widget.onNext?.call(pages[_currentPage.value]);
},
),
builder: (context, _) {
if (widget.options.controlMode ==
IntroductionControlMode.singleButton) {
return IntroductionOneButton(
controller: _controller,
next: _isNext(pages),
previous: _isPrevious,
last: _isLast(pages),
options: widget.options,
onFinish: widget.onComplete,
onNext: () {
widget.onNext?.call(pages[_currentPage.value]);
},
onPrevious: () {
widget.onNext?.call(pages[_currentPage.value]);
},
);
}

return IntroductionTwoButtons(
controller: _controller,
next: _isNext(pages),
previous: _isPrevious,
last: _isLast(pages),
options: widget.options,
onFinish: widget.onComplete,
onNext: () {
widget.onNext?.call(pages[_currentPage.value]);
},
onPrevious: () {
widget.onNext?.call(pages[_currentPage.value]);
},
);
},
),
),
],
Expand Down Expand Up @@ -261,8 +287,8 @@ class ExplainerPage extends StatelessWidget {
}
}

class IntroductionButtons extends StatelessWidget {
const IntroductionButtons({
class IntroductionTwoButtons extends StatelessWidget {
const IntroductionTwoButtons({
required this.options,
required this.controller,
required this.next,
Expand Down Expand Up @@ -306,6 +332,7 @@ class IntroductionButtons extends StatelessWidget {
context,
_previous,
Text(translations.previousButton),
IntroductionButtonType.previous,
) ??
TextButton(
onPressed: _previous,
Expand All @@ -318,6 +345,7 @@ class IntroductionButtons extends StatelessWidget {
context,
_next,
Text(translations.nextButton),
IntroductionButtonType.next,
) ??
TextButton(
onPressed: _next,
Expand All @@ -330,6 +358,7 @@ class IntroductionButtons extends StatelessWidget {
onFinish?.call();
},
Text(translations.finishButton),
IntroductionButtonType.finish,
) ??
TextButton(
onPressed: () {
Expand All @@ -356,6 +385,7 @@ class IntroductionButtons extends StatelessWidget {
onFinish?.call();
},
Text(translations.finishButton),
IntroductionButtonType.finish,
) ??
ElevatedButton(
onPressed: () {
Expand All @@ -381,6 +411,112 @@ class IntroductionButtons extends StatelessWidget {
}
}

class IntroductionOneButton extends StatelessWidget {
const IntroductionOneButton({
required this.options,
required this.controller,
required this.next,
required this.last,
required this.previous,
required this.onFinish,
required this.onNext,
required this.onPrevious,
Key? key,
}) : super(key: key);

final IntroductionOptions options;
final PageController controller;
final VoidCallback? onFinish;
final VoidCallback? onNext;
final VoidCallback? onPrevious;

final bool previous;
final bool next;
final bool last;

void _previous() {
controller.previousPage(
duration: kAnimationDuration,
curve: Curves.easeInOut,
);
onPrevious?.call();
}

@override
Widget build(BuildContext context) {
var translations = options.introductionTranslations;

return Column(
children: [
if (options.buttonMode == IntroductionScreenButtonMode.text) ...[
if (last) ...[
options.buttonBuilder?.call(
context,
() {
onFinish?.call();
},
Text(translations.finishButton),
IntroductionButtonType.finish,
) ??
TextButton(
onPressed: () {
onFinish?.call();
},
child: Text(translations.finishButton),
),
] else ...[
options.buttonBuilder?.call(
context,
_next,
Text(translations.nextButton),
IntroductionButtonType.next,
) ??
TextButton(
onPressed: _next,
child: Text(translations.nextButton),
),
],
if (previous) ...[
options.buttonBuilder?.call(
context,
_previous,
Text(translations.previousButton),
IntroductionButtonType.previous,
) ??
TextButton(
onPressed: _previous,
child: Text(translations.previousButton),
),
] else ...[
options.buttonBuilder?.call(
context,
() {
onFinish?.call();
},
Text(translations.finishButton),
IntroductionButtonType.skip,
) ??
TextButton(
onPressed: () {
onFinish?.call();
},
child: Text(translations.finishButton),
),
],
],
],
);
}

_next() {
controller.nextPage(
duration: kAnimationDuration,
curve: Curves.easeInOut,
);
onNext?.call();
}
}

class IntroductionIconButtons extends StatelessWidget {
const IntroductionIconButtons({
required this.options,
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: flutter_introduction_widget
description: Flutter Introduction Widget for showing a list of introduction pages on a single scrollable page or horizontal pageview
version: 3.0.0
version: 4.0.0
homepage: https://github.com/Iconica-Development/flutter_introduction_widget

environment:
Expand Down