Compare commits

..

No commits in common. "master" and "2.0.0" have entirely different histories.

48 changed files with 243 additions and 737 deletions

View file

@ -1,14 +0,0 @@
name: Iconica Standard Component Documentation Workflow
# Workflow Caller version: 1.0.0
on:
release:
types: [published]
workflow_dispatch:
jobs:
call-iconica-component-documentation-workflow:
uses: Iconica-Development/.github/.github/workflows/component-documentation.yml@master
secrets: inherit
permissions: write-all

5
.gitignore vendored
View file

@ -31,6 +31,7 @@ build/
.metadata .metadata
pubspec.lock pubspec.lock
pubspec_overrides.yaml pubspec_overrides.yaml
@ -40,7 +41,3 @@ example/web
example/android example/android
example/linux example/linux
example/macos example/macos
# FVM Version Cache
.fvm/
.fvmrc

View file

@ -1,29 +1,3 @@
## 5.0.0
* Changed color of indicators
* Changed default button text
## 4.0.0
* Added Buildcontext to the pages parameter.
* Added `dotColor` so the default can be changed.
* Changed the default `pages` to include theme.
## 3.1.0
* Introduction now uses `IntroductionScreenMode` to determine how often the introductions should be shown
* Added `dotSize` and `dotSpacing` to `IntroductionOptions` to allow for customization of the dots for the introduction
## 3.0.0
* Update default styling
* Add default introduction
## 2.1.0
* Upgrade dependencies
* Add introductionButtonTextstyles option to IntroductionOptions for styling the introduction button text
## 2.0.0 ## 2.0.0
* Initial release of working flutter_introduction mono project. * Initial release of working flutter_introduction mono project.

View file

@ -32,7 +32,7 @@ Please file any issues, bugs or feature request as an issue on our [GitHub](http
## Want to contribute ## Want to contribute
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_introduction/pulls). If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](../CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_introduction/pulls).
## Author ## Author

View file

@ -19,9 +19,6 @@ scripts:
upgrade: upgrade:
run: melos exec -c 1 -- "flutter pub upgrade" run: melos exec -c 1 -- "flutter pub upgrade"
upgrade-major:
run: melos exec -c 1 -- "flutter pub upgrade --major-versions"
create: create:
# run create in the example folder of flutter_introduction, flutter_introduction_firebase # run create in the example folder of flutter_introduction, flutter_introduction_firebase

View file

@ -1 +0,0 @@
../../CHANGELOG.md

View file

@ -1 +0,0 @@
../../LICENSE

View file

@ -1 +0,0 @@
../../README.md

View file

@ -38,21 +38,21 @@ class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
body: Introduction( body: Introduction(
options: IntroductionOptions( options: IntroductionOptions(
pages: (context) => [ pages: [
const IntroductionPage( IntroductionPage(
title: Text('First page'), title: const Text('First page'),
text: Text('Wow a page'), text: const Text('Wow a page'),
graphic: FlutterLogo(), graphic: const FlutterLogo(),
), ),
const IntroductionPage( IntroductionPage(
title: Text('Second page'), title: const Text('Second page'),
text: Text('Another page'), text: const Text('Another page'),
graphic: FlutterLogo(), graphic: const FlutterLogo(),
), ),
const IntroductionPage( IntroductionPage(
title: Text('Third page'), title: const Text('Third page'),
text: Text('The final page of this app'), text: const Text('The final page of this app'),
graphic: FlutterLogo(), graphic: const FlutterLogo(),
), ),
], ],
introductionTranslations: const IntroductionTranslations( introductionTranslations: const IntroductionTranslations(

View file

@ -17,11 +17,15 @@ dependencies:
flutter_introduction: flutter_introduction:
path: ../ path: ../
flutter_introduction_shared_preferences: flutter_introduction_shared_preferences:
path: ../../flutter_introduction_shared_preferences git:
url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.0.0
path: packages/flutter_introduction_shared_preferences
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis

View file

@ -19,19 +19,10 @@ class Introduction extends StatefulWidget {
super.key, super.key,
}); });
/// Callback function to navigate to the next screen.
final VoidCallback navigateTo; final VoidCallback navigateTo;
/// The introduction service to use.
final IntroductionService? service; final IntroductionService? service;
/// Options for configuring the introduction screen.
final IntroductionOptions options; final IntroductionOptions options;
/// The scrolling physics for the introduction screen.
final ScrollPhysics? physics; final ScrollPhysics? physics;
/// Child widget to display.
final Widget? child; final Widget? child;
@override @override
@ -56,9 +47,7 @@ class _IntroductionState extends State<Introduction> {
// ignore: discarded_futures // ignore: discarded_futures
future: _service.shouldShow(), future: _service.shouldShow(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.data == null || if (snapshot.data == null || snapshot.data!) {
snapshot.data! ||
widget.options.mode == IntroductionScreenMode.showAlways) {
return IntroductionScreen( return IntroductionScreen(
options: widget.options, options: widget.options,
onComplete: () async { onComplete: () async {

View file

@ -1,28 +1,33 @@
name: flutter_introduction name: flutter_introduction
description: Combined Package of Flutter Introduction Widget and Flutter Introduction Service description: Combined Package of Flutter Introduction Widget and Flutter Introduction Service
version: 5.0.0 version: 2.0.0
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub publish_to: none
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ">=2.18.0 <3.0.0"
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_introduction_widget: flutter_introduction_widget:
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub git:
version: "^5.0.0" url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.0.0
path: packages/flutter_introduction_widget
flutter_introduction_service: flutter_introduction_service:
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub git:
version: "^5.0.0" url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.0.0
path: packages/flutter_introduction_service
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 6.0.0
flutter: flutter:

View file

@ -1 +0,0 @@
../../CHANGELOG.md

View file

@ -1 +0,0 @@
../../LICENSE

View file

@ -115,7 +115,7 @@ class _IntroductionState extends State<IntroductionFirebase> {
snapshot.data is List<IntroductionPageData>) { snapshot.data is List<IntroductionPageData>) {
return IntroductionScreen( return IntroductionScreen(
options: widget.options.copyWith( options: widget.options.copyWith(
pages: (context) => snapshot.data!.map( pages: snapshot.data?.map(
(e) { (e) {
var title = e.title.isEmpty var title = e.title.isEmpty
? '' ? ''

View file

@ -1,7 +1,7 @@
name: flutter_introduction_firebase name: flutter_introduction_firebase
description: Flutter Introduction Page that uses firebase for the pages and some settings description: Flutter Introduction Page that uses firebase for the pages and some settings
version: 5.0.0 version: 2.0.0
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub publish_to: none
environment: environment:
sdk: ">=3.1.5 <4.0.0" sdk: ">=3.1.5 <4.0.0"
@ -9,15 +9,19 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
cloud_firestore: "^4.12.2" cloud_firestore: ^4.12.2
cached_network_image: "^3.3.0" cached_network_image: ^3.3.0
flutter_introduction_widget: flutter_introduction_widget:
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub git:
version: "^5.0.0" url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.0.0
path: packages/flutter_introduction_widget
flutter_introduction_service: flutter_introduction_service:
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub git:
version: "^5.0.0" url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.0.0
path: packages/flutter_introduction_service
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -27,4 +31,4 @@ dev_dependencies:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 6.0.0
flutter: flutter:

View file

@ -1 +0,0 @@
../../CHANGELOG.md

View file

@ -1 +0,0 @@
../../LICENSE

View file

@ -1 +0,0 @@
../../README.md

View file

@ -6,35 +6,20 @@ import 'package:flutter_data_interface/flutter_data_interface.dart';
import 'package:flutter_introduction_interface/src/local_introduction.dart'; import 'package:flutter_introduction_interface/src/local_introduction.dart';
abstract class IntroductionInterface extends DataInterface { abstract class IntroductionInterface extends DataInterface {
/// Constructs an instance of [IntroductionInterface].
///
/// The [token] is used for verification purposes.
IntroductionInterface() : super(token: _token); IntroductionInterface() : super(token: _token);
static final Object _token = Object(); static final Object _token = Object();
static IntroductionInterface _instance = LocalIntroductionDataProvider(); static IntroductionInterface _instance = LocalIntroductionDataProvider();
/// Retrieves the current instance of [IntroductionInterface].
static IntroductionInterface get instance => _instance; static IntroductionInterface get instance => _instance;
/// Sets the current instance of [IntroductionInterface].
///
/// Throws an error if the provided instance does not match the token.
static set instance(IntroductionInterface instance) { static set instance(IntroductionInterface instance) {
DataInterface.verify(instance, _token); DataInterface.verify(instance, _token);
_instance = instance; _instance = instance;
} }
/// Sets whether the introduction is completed or not.
///
/// The [value] parameter specifies whether the introduction is completed.
/// By default, it is set to `true`.
Future<void> setCompleted({bool value = true}); Future<void> setCompleted({bool value = true});
/// Checks if the introduction should be shown.
///
/// Returns `true` if the introduction should be shown;
/// otherwise, returns `false`.
Future<bool> shouldShow(); Future<bool> shouldShow();
} }

View file

@ -4,17 +4,9 @@
import 'package:flutter_introduction_interface/src/introduction_interface.dart'; import 'package:flutter_introduction_interface/src/introduction_interface.dart';
/// Provides local data storage for managing introduction data.
///
/// This class extends [IntroductionInterface] and implements methods to manage
/// introduction data locally.
class LocalIntroductionDataProvider extends IntroductionInterface { class LocalIntroductionDataProvider extends IntroductionInterface {
/// Constructs an instance of [LocalIntroductionDataProvider].
///
/// Initializes the [hasViewed] flag to `false`.
LocalIntroductionDataProvider(); LocalIntroductionDataProvider();
/// Flag indicating whether the introduction has been viewed or not.
bool hasViewed = false; bool hasViewed = false;
@override @override

View file

@ -1,22 +1,24 @@
name: flutter_introduction_interface name: flutter_introduction_interface
description: A new Flutter package project. description: A new Flutter package project.
version: 5.0.0 version: 2.0.0
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub publish_to: none
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=2.18.0 <3.0.0'
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_data_interface: flutter_data_interface:
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub git:
version: "^1.0.0" url: https://github.com/Iconica-Development/flutter_data_interface.git
ref: 1.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis

View file

@ -1 +0,0 @@
../../CHANGELOG.md

View file

@ -1 +0,0 @@
../../LICENSE

View file

@ -1 +0,0 @@
../../README.md

View file

@ -4,36 +4,15 @@
import 'package:flutter_introduction_interface/flutter_introduction_interface.dart'; import 'package:flutter_introduction_interface/flutter_introduction_interface.dart';
/// A service for managing introduction-related operations.
///
/// This class provides methods for handling introduction-related actions
/// such as skipping, completing,
/// and determining whether to show the introduction.
class IntroductionService { class IntroductionService {
/// Constructs an instance of [IntroductionService].
///
/// Optionally takes a [dataProvider] parameter,
/// which is an implementation of [IntroductionInterface].
/// If no data provider is provided,
/// it defaults to [LocalIntroductionDataProvider].
IntroductionService([IntroductionInterface? dataProvider]) IntroductionService([IntroductionInterface? dataProvider])
: _dataProvider = dataProvider ?? LocalIntroductionDataProvider(); : _dataProvider = dataProvider ?? LocalIntroductionDataProvider();
late final IntroductionInterface _dataProvider; late final IntroductionInterface _dataProvider;
/// Marks the introduction as skipped.
///
/// Calls [_dataProvider.setCompleted] with the value `true`.
Future<void> onSkip() => _dataProvider.setCompleted(value: true); Future<void> onSkip() => _dataProvider.setCompleted(value: true);
/// Marks the introduction as completed.
///
/// Calls [_dataProvider.setCompleted] with the value `true`.
Future<void> onComplete() => _dataProvider.setCompleted(value: true); Future<void> onComplete() => _dataProvider.setCompleted(value: true);
/// Checks whether the introduction should be shown.
///
/// Returns a `Future<bool>` indicating whether the
/// introduction should be shown.
Future<bool> shouldShow() => _dataProvider.shouldShow(); Future<bool> shouldShow() => _dataProvider.shouldShow();
} }

View file

@ -1,22 +1,25 @@
name: flutter_introduction_service name: flutter_introduction_service
description: A new Flutter package project. description: A new Flutter package project.
version: 5.0.0 version: 2.0.0
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub publish_to: none
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=2.18.0 <3.0.0'
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_introduction_interface: flutter_introduction_interface:
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub git:
version: "^5.0.0" url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.0.0
path: packages/flutter_introduction_interface
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis

View file

@ -1 +0,0 @@
../../CHANGELOG.md

View file

@ -1 +0,0 @@
../../LICENSE

View file

@ -1 +0,0 @@
../../README.md

View file

@ -5,27 +5,16 @@
import 'package:flutter_introduction_interface/flutter_introduction_interface.dart'; import 'package:flutter_introduction_interface/flutter_introduction_interface.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
/// Provides data storage using SharedPreferences for
/// managing introduction data.
///
/// This class extends [IntroductionInterface] and implements methods to manage
/// introduction data using SharedPreferences.
class SharedPreferencesIntroductionDataProvider extends IntroductionInterface { class SharedPreferencesIntroductionDataProvider extends IntroductionInterface {
/// Constructs an instance of [SharedPreferencesIntroductionDataProvider].
SharedPreferencesIntroductionDataProvider(); SharedPreferencesIntroductionDataProvider();
SharedPreferences? _prefs; SharedPreferences? _prefs;
String key = '_completedIntroduction'; String key = '_completedIntroduction';
/// Writes a key-value pair to SharedPreferences.
///
/// The [key] is the key under which to store the [value].
/// The [value] is the boolean value to be stored.
Future<void> _writeKeyValue(String key, bool value) async { Future<void> _writeKeyValue(String key, bool value) async {
await _prefs!.setBool(key, value); await _prefs!.setBool(key, value);
} }
/// Initializes the SharedPreferences instance.
Future<void> _init() async { Future<void> _init() async {
_prefs ??= await SharedPreferences.getInstance(); _prefs ??= await SharedPreferences.getInstance();
} }

View file

@ -1,23 +1,26 @@
name: flutter_introduction_shared_preferences name: flutter_introduction_shared_preferences
description: A new Flutter package project. description: A new Flutter package project.
version: 5.0.0 version: 2.0.0
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub publish_to: none
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=2.18.0 <3.0.0'
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_introduction_interface: flutter_introduction_interface:
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub git:
version: "^5.0.0" url: https://github.com/Iconica-Development/flutter_introduction
shared_preferences: "^2.2.0" ref: 2.0.0
path: packages/flutter_introduction_interface
shared_preferences: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis

View file

@ -1 +0,0 @@
../../CHANGELOG.md

View file

@ -1 +0,0 @@
../../LICENSE

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View file

@ -31,30 +31,30 @@ class MyApp extends StatelessWidget {
), ),
home: IntroductionScreen( home: IntroductionScreen(
options: IntroductionOptions( options: IntroductionOptions(
pages: (context) => [ pages: [
const IntroductionPage( IntroductionPage(
title: Text('Basic Page'), title: const Text('Basic Page'),
text: Text( text: const Text(
'A page with some text and a widget in the middle.', 'A page with some text and a widget in the middle.',
), ),
graphic: FlutterLogo(size: 100), graphic: const FlutterLogo(size: 100),
), ),
const IntroductionPage( IntroductionPage(
title: Text('Layout Shift'), title: const Text('Layout Shift'),
text: Text( text: const Text(
'You can change the layout of a page to mix things up.', 'You can change the layout of a page to mix things up.',
), ),
graphic: FlutterLogo(size: 100), graphic: const FlutterLogo(size: 100),
layoutStyle: IntroductionLayoutStyle.imageTop, layoutStyle: IntroductionLayoutStyle.imageTop,
), ),
const IntroductionPage( IntroductionPage(
title: Text( title: const Text(
'Decoration', 'Decoration',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
), ),
), ),
decoration: BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topRight, begin: Alignment.topRight,
end: Alignment.bottomLeft, end: Alignment.bottomLeft,
@ -66,24 +66,24 @@ class MyApp extends StatelessWidget {
], ],
), ),
), ),
text: Text( text: const Text(
'Add a Decoration to make a custom background, like a LinearGradient', 'Add a Decoration to make a custom background, like a LinearGradient',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
), ),
), ),
graphic: FlutterLogo( graphic: const FlutterLogo(
size: 100, size: 100,
), ),
), ),
const IntroductionPage( IntroductionPage(
title: Text( title: const Text(
'Background Image', 'Background Image',
), ),
text: Text( text: const Text(
'Add a Decoration with a DecorationImage, to add an background image', 'Add a Decoration with a DecorationImage, to add an background image',
), ),
decoration: BoxDecoration( decoration: const BoxDecoration(
image: DecorationImage( image: DecorationImage(
fit: BoxFit.cover, fit: BoxFit.cover,
image: AssetImage( image: AssetImage(

View file

@ -18,7 +18,7 @@ dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^3.0.1 flutter_lints: ^2.0.0
flutter: flutter:

View file

@ -1,85 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_introduction_widget/flutter_introduction_widget.dart';
List<IntroductionPage> defaultIntroductionPages(BuildContext context) {
var theme = Theme.of(context);
return [
IntroductionPage(
title: Column(
children: [
const SizedBox(height: 50),
Text(
'welcome to iconinstagram',
style: theme.textTheme.headlineLarge,
),
const SizedBox(height: 6),
],
),
graphic: const Image(
image: AssetImage(
'assets/first.png',
package: 'flutter_introduction_widget',
),
),
text: Text(
'Welcome to the world of Instagram, where creativity'
' knows no bounds and connections are made'
' through captivating visuals.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyMedium,
),
),
IntroductionPage(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 50),
Text(
'discover iconinstagram',
style: theme.textTheme.headlineLarge,
),
const SizedBox(height: 6),
],
),
text: Text(
'Dive into the vibrant world of'
' Instagram and discover endless possibilities.'
' From stunning photography to engaging videos,'
' Instagram offers a diverse range of content to explore and enjoy.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyMedium,
),
graphic: const Image(
image: AssetImage(
'assets/second.png',
package: 'flutter_introduction_widget',
),
),
),
IntroductionPage(
title: Column(
children: [
const SizedBox(height: 50),
Text(
'elevate your experience',
style: theme.textTheme.headlineLarge,
),
const SizedBox(height: 6),
],
),
graphic: const Image(
image: AssetImage(
'assets/third.png',
package: 'flutter_introduction_widget',
),
),
text: Text(
'Whether promoting your business, or connecting'
' with friends and family, Instagram provides the'
' tools and platform to make your voice heard.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyMedium,
),
),
];
}

View file

@ -3,7 +3,6 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_introduction_widget/src/config/default_introduction_pages.dart';
enum IntroductionScreenMode { showNever, showAlways, showOnce } enum IntroductionScreenMode { showNever, showAlways, showOnce }
@ -43,7 +42,7 @@ class IntroductionPage {
/// ///
/// The [background] is fully optional and if not provided will show the /// The [background] is fully optional and if not provided will show the
/// [ThemeData.colorScheme.background] as default. /// [ThemeData.colorScheme.background] as default.
const IntroductionPage({ IntroductionPage({
this.title, this.title,
this.text, this.text,
this.graphic, this.graphic,
@ -60,12 +59,11 @@ class IntroductionPage {
class IntroductionOptions { class IntroductionOptions {
const IntroductionOptions({ const IntroductionOptions({
this.introductionTranslations = const IntroductionTranslations(), this.introductionTranslations = const IntroductionTranslations(),
this.introductionButtonTextstyles = const IntroductionButtonTextstyles(), this.indicatorMode = IndicatorMode.dash,
this.indicatorMode = IndicatorMode.dot,
this.indicatorBuilder, this.indicatorBuilder,
this.layoutStyle = IntroductionLayoutStyle.imageBottom, this.layoutStyle = IntroductionLayoutStyle.imageCenter,
this.pages = defaultIntroductionPages, this.pages = const [],
this.buttonMode = IntroductionScreenButtonMode.text, this.buttonMode = IntroductionScreenButtonMode.disabled,
this.tapEnabled = false, this.tapEnabled = false,
this.mode = IntroductionScreenMode.showNever, this.mode = IntroductionScreenMode.showNever,
this.textAlign = TextAlign.center, this.textAlign = TextAlign.center,
@ -73,9 +71,6 @@ class IntroductionOptions {
this.skippable = false, this.skippable = false,
this.buttonBuilder, this.buttonBuilder,
this.controlMode = IntroductionControlMode.previousNextButton, this.controlMode = IntroductionControlMode.previousNextButton,
this.dotSize = 12,
this.dotSpacing = 24,
this.dotColor,
}) : assert( }) : assert(
!(identical(indicatorMode, IndicatorMode.custom) && !(identical(indicatorMode, IndicatorMode.custom) &&
indicatorBuilder == null), indicatorBuilder == null),
@ -84,19 +79,19 @@ class IntroductionOptions {
); );
/// Determine when the introduction screens needs to be shown. /// Determine when the introduction screens needs to be shown.
/// ///
/// [IntroductionScreenMode.showNever] To disable introduction screens. /// [IntroductionScreenMode.showNever] To disable introduction screens.
/// ///
/// [IntroductionScreenMode.showAlways] To always show the introduction /// [IntroductionScreenMode.showAlways] To always show the introduction
/// screens on startup. /// screens on startup.
/// ///
/// [IntroductionScreenMode.showOnce] To only show the introduction screens /// [IntroductionScreenMode.showOnce] To only show the introduction screens
/// once on startup. /// once on startup.
final IntroductionScreenMode mode; final IntroductionScreenMode mode;
/// List of introduction pages to set the text, icons or images for the /// List of introduction pages to set the text, icons or images for the
/// introduction screens. /// introduction screens.
final List<IntroductionPage> Function(BuildContext context) pages; final List<IntroductionPage> pages;
/// Determines whether the user can tap the screen to go to the next /// Determines whether the user can tap the screen to go to the next
/// introduction screen. /// introduction screen.
@ -106,13 +101,13 @@ class IntroductionOptions {
/// introduction screen. /// introduction screen.
/// Introduction screens can always be navigated by swiping (or tapping if /// Introduction screens can always be navigated by swiping (or tapping if
/// [tapEnabled] is enabled). /// [tapEnabled] is enabled).
/// ///
/// [IntroductionScreenButtonMode.text] Use text buttons (text can be set by /// [IntroductionScreenButtonMode.text] Use text buttons (text can be set by
/// setting the translation key or using the default appshell translations). /// setting the translation key or using the default appshell translations).
/// ///
/// [IntroductionScreenButtonMode.icon] Use icon buttons (icons can be /// [IntroductionScreenButtonMode.icon] Use icon buttons (icons can be
/// changed by providing a icon library) /// changed by providing a icon library)
/// ///
/// [IntroductionScreenButtonMode.disabled] Disable buttons. /// [IntroductionScreenButtonMode.disabled] Disable buttons.
final IntroductionScreenButtonMode buttonMode; final IntroductionScreenButtonMode buttonMode;
@ -120,21 +115,21 @@ class IntroductionOptions {
/// using [pages]. /// using [pages].
/// Every introduction page provided with a image or icon will use the same /// Every introduction page provided with a image or icon will use the same
/// layout setting. /// layout setting.
/// ///
/// [IntroductionLayoutStyle.imageCenter] Image/icon will be at the center of the introduction page. /// [IntroductionLayoutStyle.imageCenter] Image/icon will be at the center of the introduction page.
/// ///
/// [IntroductionLayoutStyle.imageTop] Image/icon will be at the top of the introduction page. /// [IntroductionLayoutStyle.imageTop] Image/icon will be at the top of the introduction page.
/// ///
/// [IntroductionLayoutStyle.imageBottom] Image/icon will be at the bottom of the introduction page. /// [IntroductionLayoutStyle.imageBottom] Image/icon will be at the bottom of the introduction page.
final IntroductionLayoutStyle layoutStyle; final IntroductionLayoutStyle layoutStyle;
/// Determines the style of the page indicator shown at the bottom on the /// Determines the style of the page indicator shown at the bottom on the
/// introduction pages. /// introduction pages.
/// ///
/// [IndicatorMode.dot] Shows a dot for each page. /// [IndicatorMode.dot] Shows a dot for each page.
/// ///
/// [IndicatorMode.dash] Shows a dash for each page. /// [IndicatorMode.dash] Shows a dash for each page.
/// ///
/// [IndicatorMode.custom] calls indicatorBuilder for the indicator /// [IndicatorMode.custom] calls indicatorBuilder for the indicator
final IndicatorMode indicatorMode; final IndicatorMode indicatorMode;
@ -157,23 +152,23 @@ class IntroductionOptions {
/// [IntroductionDisplayMode.multiPageHorizontal] Configured introduction /// [IntroductionDisplayMode.multiPageHorizontal] Configured introduction
/// pages will be shown on seperate screens and can be navigated using using /// pages will be shown on seperate screens and can be navigated using using
/// buttons (if enabled) or swiping. /// buttons (if enabled) or swiping.
/// ///
/// !Unimplemented! [IntroductionDisplayMode.singleScrollablePageVertical] /// !Unimplemented! [IntroductionDisplayMode.singleScrollablePageVertical]
/// All configured introduction pages will be shown on a single scrollable /// All configured introduction pages will be shown on a single scrollable
/// page. /// page.
/// ///
final IntroductionDisplayMode displayMode; final IntroductionDisplayMode displayMode;
/// When [IntroductionDisplayMode.multiPageHorizontal] is selected multiple /// When [IntroductionDisplayMode.multiPageHorizontal] is selected multiple
/// controlMode can be selected. /// controlMode can be selected.
/// ///
/// [IntroductionControlMode.previousNextButton] shows two buttons at the /// [IntroductionControlMode.previousNextButton] shows two buttons at the
/// bottom of the screen to return or proceed. The skip button is placed at /// bottom of the screen to return or proceed. The skip button is placed at
/// the top left of the screen. /// the top left of the screen.
/// ///
/// [IntroductionControlMode.singleButton] contains one button at the bottom /// [IntroductionControlMode.singleButton] contains one button at the bottom
/// of the screen to proceed. Underneath is clickable text to skip if the /// 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 /// current page is the first page. If the current page is any different it
/// return to the previous screen. /// return to the previous screen.
/// ///
final IntroductionControlMode controlMode; final IntroductionControlMode controlMode;
@ -197,29 +192,9 @@ class IntroductionOptions {
/// - Finish /// - Finish
final IntroductionTranslations introductionTranslations; final IntroductionTranslations introductionTranslations;
/// The textstyles for all buttons on the introductionpages
///
/// See [IntroductionButtonTextstyles] for more information
/// The following buttons have a textstyle:
/// - Skip
/// - Next
/// - Previous
/// - Finish
final IntroductionButtonTextstyles introductionButtonTextstyles;
/// The size of the dots in the indicator. Default is 12
final double dotSize;
/// The distance between the center of each dot. Default is 24
final double dotSpacing;
/// The color of the dots in the indicator. Default is the primary color of
/// the theme
final Color? dotColor;
IntroductionOptions copyWith({ IntroductionOptions copyWith({
IntroductionScreenMode? mode, IntroductionScreenMode? mode,
List<IntroductionPage> Function(BuildContext context)? pages, List<IntroductionPage>? pages,
bool? tapEnabled, bool? tapEnabled,
IntroductionScreenButtonMode? buttonMode, IntroductionScreenButtonMode? buttonMode,
IntroductionLayoutStyle? layoutStyle, IntroductionLayoutStyle? layoutStyle,
@ -237,7 +212,6 @@ class IntroductionOptions {
Widget Function(BuildContext, VoidCallback, Widget, IntroductionButtonType)? Widget Function(BuildContext, VoidCallback, Widget, IntroductionButtonType)?
buttonBuilder, buttonBuilder,
IntroductionTranslations? introductionTranslations, IntroductionTranslations? introductionTranslations,
IntroductionButtonTextstyles? introductionButtonTextstyles,
}) => }) =>
IntroductionOptions( IntroductionOptions(
mode: mode ?? this.mode, mode: mode ?? this.mode,
@ -254,33 +228,19 @@ class IntroductionOptions {
buttonBuilder: buttonBuilder ?? this.buttonBuilder, buttonBuilder: buttonBuilder ?? this.buttonBuilder,
introductionTranslations: introductionTranslations:
introductionTranslations ?? this.introductionTranslations, introductionTranslations ?? this.introductionTranslations,
introductionButtonTextstyles:
introductionButtonTextstyles ?? this.introductionButtonTextstyles,
); );
} }
///
class IntroductionTranslations { class IntroductionTranslations {
const IntroductionTranslations({ const IntroductionTranslations({
this.skipButton = 'Skip', this.skipButton = 'skip',
this.nextButton = 'Next', this.nextButton = 'next',
this.previousButton = 'Previous', this.previousButton = 'previous',
this.finishButton = 'Get started', this.finishButton = 'finish',
}); });
final String skipButton; final String skipButton;
final String nextButton; final String nextButton;
final String previousButton; final String previousButton;
final String finishButton; final String finishButton;
} }
class IntroductionButtonTextstyles {
const IntroductionButtonTextstyles({
this.skipButtonStyle,
this.nextButtonStyle,
this.previousButtonStyle,
this.finishButtonStyle,
});
final TextStyle? skipButtonStyle;
final TextStyle? nextButtonStyle;
final TextStyle? previousButtonStyle;
final TextStyle? finishButtonStyle;
}

View file

@ -11,12 +11,7 @@ import 'package:flutter_introduction_widget/src/widgets/background.dart';
import 'package:flutter_introduction_widget/src/widgets/indicator.dart'; import 'package:flutter_introduction_widget/src/widgets/indicator.dart';
import 'package:flutter_introduction_widget/src/widgets/page_content.dart'; import 'package:flutter_introduction_widget/src/widgets/page_content.dart';
/// A screen widget for displaying a multi-page introduction.
///
/// This widget provides a multi-page introduction experience with options
/// for handling navigation and completion callbacks.
class MultiPageIntroductionScreen extends StatefulWidget { class MultiPageIntroductionScreen extends StatefulWidget {
/// Creates a new instance of [MultiPageIntroductionScreen].
const MultiPageIntroductionScreen({ const MultiPageIntroductionScreen({
required this.options, required this.options,
required this.onComplete, required this.onComplete,
@ -27,22 +22,12 @@ class MultiPageIntroductionScreen extends StatefulWidget {
super.key, super.key,
}); });
/// Callback function triggered when the introduction is completed.
final VoidCallback onComplete; final VoidCallback onComplete;
/// Callback function triggered when the "Next" button is pressed.
final void Function(IntroductionPage)? onNext;
/// Callback function triggered when the "Previous" button is pressed.
final void Function(IntroductionPage)? onPrevious;
/// Callback function triggered when the "Skip" button is pressed.
final VoidCallback? onSkip; final VoidCallback? onSkip;
final void Function(IntroductionPage)? onNext;
/// Physics for the scrolling behavior. final void Function(IntroductionPage)? onPrevious;
final ScrollPhysics? physics; final ScrollPhysics? physics;
/// Introduction options specifying the configuration of the introduction.
final IntroductionOptions options; final IntroductionOptions options;
@override @override
@ -50,9 +35,6 @@ class MultiPageIntroductionScreen extends StatefulWidget {
_MultiPageIntroductionScreenState(); _MultiPageIntroductionScreenState();
} }
/// State class for [MultiPageIntroductionScreen].
///
/// Manages the state and behavior of the [MultiPageIntroductionScreen] widget.
class _MultiPageIntroductionScreenState class _MultiPageIntroductionScreenState
extends State<MultiPageIntroductionScreen> { extends State<MultiPageIntroductionScreen> {
final PageController _controller = PageController(); final PageController _controller = PageController();
@ -77,7 +59,7 @@ class _MultiPageIntroductionScreenState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var pages = widget.options.pages.call(context); var pages = widget.options.pages;
var translations = widget.options.introductionTranslations; var translations = widget.options.introductionTranslations;
return Stack( return Stack(
children: [ children: [
@ -150,21 +132,15 @@ class _MultiPageIntroductionScreenState
AnimatedBuilder( AnimatedBuilder(
animation: _currentPage, animation: _currentPage,
builder: (context, _) => Indicator( builder: (context, _) => Indicator(
options: widget.options,
indicatorBuilder: widget.options.indicatorBuilder, indicatorBuilder: widget.options.indicatorBuilder,
mode: widget.options.indicatorMode, mode: widget.options.indicatorMode,
controller: _controller, controller: _controller,
count: pages.length, count: pages.length,
index: _currentPage.value, index: _currentPage.value,
dotSize: widget.options.dotSize,
dotSpacing: widget.options.dotSpacing,
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.all(32),
vertical: 40,
horizontal: 20,
),
child: AnimatedBuilder( child: AnimatedBuilder(
animation: _controller, animation: _controller,
builder: (context, _) { builder: (context, _) {
@ -276,14 +252,14 @@ class ExplainerPage extends StatelessWidget {
title: Padding( title: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32), padding: const EdgeInsets.symmetric(horizontal: 32),
child: DefaultTextStyle( child: DefaultTextStyle(
style: theme.textTheme.titleMedium!, style: theme.textTheme.displayMedium!,
child: page.title ?? Text('introduction.$index.title'), child: page.title ?? Text('introduction.$index.title'),
), ),
), ),
text: Padding( text: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32), padding: const EdgeInsets.symmetric(horizontal: 32),
child: DefaultTextStyle( child: DefaultTextStyle(
style: theme.textTheme.bodyMedium!, style: theme.textTheme.bodyLarge!,
child: page.text ?? Text('introduction.$index.description'), child: page.text ?? Text('introduction.$index.description'),
), ),
), ),
@ -342,159 +318,49 @@ class IntroductionTwoButtons extends StatelessWidget {
var translations = options.introductionTranslations; var translations = options.introductionTranslations;
var showFinishButton = var showFinishButton =
options.buttonMode == IntroductionScreenButtonMode.singleFinish; options.buttonMode == IntroductionScreenButtonMode.singleFinish;
var theme = Theme.of(context);
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
if (options.buttonMode == IntroductionScreenButtonMode.text) ...[ if (options.buttonMode == IntroductionScreenButtonMode.text) ...[
Flexible( if (previous) ...[
child: Padding( options.buttonBuilder?.call(
padding: const EdgeInsets.only(right: 6), context,
child: ConstrainedBox( _previous,
constraints: const BoxConstraints( Text(translations.previousButton),
maxWidth: 180, IntroductionButtonType.previous,
) ??
TextButton(
onPressed: _previous,
child: Text(translations.previousButton),
), ),
child: Opacity( ] else
opacity: previous ? 1 : 0, const SizedBox.shrink(),
child: IgnorePointer(
ignoring: !previous,
child: options.buttonBuilder?.call(
context,
_previous,
Text(
translations.previousButton,
style: options.introductionButtonTextstyles
.previousButtonStyle ??
theme.textTheme.bodyMedium,
),
IntroductionButtonType.previous,
) ??
InkWell(
onTap: _previous,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: const Color(
0xff979797,
),
),
),
child: Center(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 4),
child: Text(
translations.previousButton,
style: options.introductionButtonTextstyles
.previousButtonStyle ??
theme.textTheme.bodyMedium,
),
),
),
),
),
),
),
),
),
),
if (next) ...[ if (next) ...[
Flexible( options.buttonBuilder?.call(
child: Padding( context,
padding: const EdgeInsets.only(left: 6), _next,
child: ConstrainedBox( Text(translations.nextButton),
constraints: const BoxConstraints( IntroductionButtonType.next,
maxWidth: 180, ) ??
), TextButton(
child: options.buttonBuilder?.call( onPressed: _next,
context, child: Text(translations.nextButton),
_next,
Text(
translations.nextButton,
style: options.introductionButtonTextstyles
.nextButtonStyle ??
theme.textTheme.bodyMedium,
),
IntroductionButtonType.next,
) ??
InkWell(
onTap: _next,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: const Color(
0xff979797,
),
),
),
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Text(
translations.nextButton,
style: options.introductionButtonTextstyles
.nextButtonStyle ??
theme.textTheme.bodyMedium,
),
),
),
),
),
), ),
),
),
] else if (last) ...[ ] else if (last) ...[
Flexible( options.buttonBuilder?.call(
child: Padding( context,
padding: const EdgeInsets.only(left: 6), () {
child: ConstrainedBox( onFinish?.call();
constraints: const BoxConstraints( },
maxWidth: 180, Text(translations.finishButton),
), IntroductionButtonType.finish,
child: options.buttonBuilder?.call( ) ??
context, TextButton(
() { onPressed: () {
onFinish?.call(); onFinish?.call();
}, },
Text( child: Text(translations.finishButton),
translations.finishButton,
style: options.introductionButtonTextstyles
.finishButtonStyle ??
theme.textTheme.bodyMedium,
),
IntroductionButtonType.finish,
) ??
InkWell(
onTap: () {
onFinish?.call();
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: const Color(
0xff979797,
),
),
),
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Text(
translations.finishButton,
style: options.introductionButtonTextstyles
.finishButtonStyle ??
theme.textTheme.bodyMedium,
),
),
),
),
),
), ),
),
),
] else ...[ ] else ...[
const SizedBox.shrink(), const SizedBox.shrink(),
], ],
@ -508,53 +374,20 @@ class IntroductionTwoButtons extends StatelessWidget {
maintainState: true, maintainState: true,
maintainInteractivity: false, maintainInteractivity: false,
child: Align( child: Align(
child: Flexible( child: options.buttonBuilder?.call(
child: ConstrainedBox( context,
constraints: const BoxConstraints( () {
maxWidth: 180, onFinish?.call();
},
Text(translations.finishButton),
IntroductionButtonType.finish,
) ??
ElevatedButton(
onPressed: () {
onFinish?.call();
},
child: Text(translations.finishButton),
), ),
child: options.buttonBuilder?.call(
context,
() {
onFinish?.call();
},
Text(
translations.finishButton,
style: options.introductionButtonTextstyles
.finishButtonStyle ??
theme.textTheme.bodyMedium,
),
IntroductionButtonType.finish,
) ??
InkWell(
onTap: () {
onFinish?.call();
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: const Color(
0xff979797,
),
),
),
child: Center(
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 4),
child: Text(
translations.finishButton,
style: options.introductionButtonTextstyles
.finishButtonStyle ??
theme.textTheme.bodyMedium,
),
),
),
),
),
),
),
), ),
), ),
), ),
@ -586,31 +419,16 @@ class IntroductionOneButton extends StatelessWidget {
super.key, super.key,
}); });
/// Options specifying the configuration of the introduction.
final IntroductionOptions options; final IntroductionOptions options;
/// Controller for managing the pages of the introduction.
final PageController controller; final PageController controller;
/// Callback function triggered when the introduction is completed.
final VoidCallback? onFinish; final VoidCallback? onFinish;
/// Callback function triggered when the "Next" button is pressed.
final VoidCallback? onNext; final VoidCallback? onNext;
/// Callback function triggered when the "Previous" button is pressed.
final VoidCallback? onPrevious; final VoidCallback? onPrevious;
/// Indicates whether there are previous pages.
final bool previous; final bool previous;
/// Indicates whether there are next pages.
final bool next; final bool next;
/// Indicates whether this is the last page.
final bool last; final bool last;
/// Handles the navigation to the previous page.
Future<void> _previous() async { Future<void> _previous() async {
await controller.previousPage( await controller.previousPage(
duration: kAnimationDuration, duration: kAnimationDuration,
@ -632,59 +450,37 @@ class IntroductionOneButton extends StatelessWidget {
() { () {
onFinish?.call(); onFinish?.call();
}, },
Text( Text(translations.finishButton),
translations.finishButton,
style:
options.introductionButtonTextstyles.finishButtonStyle,
),
IntroductionButtonType.finish, IntroductionButtonType.finish,
) ?? ) ??
TextButton( TextButton(
onPressed: () { onPressed: () {
onFinish?.call(); onFinish?.call();
}, },
child: Text( child: Text(translations.finishButton),
translations.finishButton,
style:
options.introductionButtonTextstyles.finishButtonStyle,
),
), ),
] else ...[ ] else ...[
options.buttonBuilder?.call( options.buttonBuilder?.call(
context, context,
_next, _next,
Text( Text(translations.nextButton),
translations.nextButton,
style: options.introductionButtonTextstyles.nextButtonStyle,
),
IntroductionButtonType.next, IntroductionButtonType.next,
) ?? ) ??
TextButton( TextButton(
onPressed: _next, onPressed: _next,
child: Text( child: Text(translations.nextButton),
translations.nextButton,
style: options.introductionButtonTextstyles.nextButtonStyle,
),
), ),
], ],
if (previous) ...[ if (previous) ...[
options.buttonBuilder?.call( options.buttonBuilder?.call(
context, context,
_previous, _previous,
Text( Text(translations.previousButton),
translations.previousButton,
style: options
.introductionButtonTextstyles.previousButtonStyle,
),
IntroductionButtonType.previous, IntroductionButtonType.previous,
) ?? ) ??
TextButton( TextButton(
onPressed: _previous, onPressed: _previous,
child: Text( child: Text(translations.previousButton),
translations.previousButton,
style: options
.introductionButtonTextstyles.previousButtonStyle,
),
), ),
] else ...[ ] else ...[
options.buttonBuilder?.call( options.buttonBuilder?.call(
@ -692,22 +488,14 @@ class IntroductionOneButton extends StatelessWidget {
() { () {
onFinish?.call(); onFinish?.call();
}, },
Text( Text(translations.finishButton),
translations.finishButton,
style:
options.introductionButtonTextstyles.finishButtonStyle,
),
IntroductionButtonType.skip, IntroductionButtonType.skip,
) ?? ) ??
TextButton( TextButton(
onPressed: () { onPressed: () {
onFinish?.call(); onFinish?.call();
}, },
child: Text( child: Text(translations.finishButton),
translations.finishButton,
style:
options.introductionButtonTextstyles.finishButtonStyle,
),
), ),
], ],
], ],
@ -737,31 +525,16 @@ class IntroductionIconButtons extends StatelessWidget {
super.key, super.key,
}); });
/// Options specifying the configuration of the introduction.
final IntroductionOptions options; final IntroductionOptions options;
/// Controller for managing the pages of the introduction.
final PageController controller; final PageController controller;
/// Callback function triggered when the introduction is completed.
final VoidCallback? onFinish; final VoidCallback? onFinish;
/// Callback function triggered when the "Next" button is pressed.
final VoidCallback? onNext; final VoidCallback? onNext;
/// Callback function triggered when the "Previous" button is pressed.
final VoidCallback? onPrevious; final VoidCallback? onPrevious;
/// Indicates whether there are previous pages.
final bool previous; final bool previous;
/// Indicates whether there are next pages.
final bool next; final bool next;
/// Indicates whether this is the last page.
final bool last; final bool last;
/// Handles the navigation to the previous page.
Future<void> _previous() async { Future<void> _previous() async {
await controller.previousPage( await controller.previousPage(
duration: kAnimationDuration, duration: kAnimationDuration,

View file

@ -6,15 +6,12 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter_introduction_widget/src/config/introduction.dart'; import 'package:flutter_introduction_widget/src/config/introduction.dart';
/// Widget representing a single introduction page.
class SingleIntroductionPage extends StatelessWidget { class SingleIntroductionPage extends StatelessWidget {
/// Constructs a [SingleIntroductionPage] widget.
const SingleIntroductionPage({ const SingleIntroductionPage({
required this.options, required this.options,
super.key, super.key,
}); });
/// Options specifying the configuration of the introduction.
final IntroductionOptions options; final IntroductionOptions options;
@override @override

View file

@ -4,19 +4,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// Widget representing a background with optional decoration.
class Background extends StatelessWidget { class Background extends StatelessWidget {
/// Constructs a Background widget.
const Background({ const Background({
required this.child, required this.child,
this.background, this.background,
super.key, super.key,
}); });
/// Optional decoration for the background.
final BoxDecoration? background; final BoxDecoration? background;
/// The widget to be placed on the background.
final Widget child; final Widget child;
@override @override
@ -24,7 +19,7 @@ class Background extends StatelessWidget {
var theme = Theme.of(context); var theme = Theme.of(context);
var background = this.background ?? var background = this.background ??
BoxDecoration( BoxDecoration(
color: theme.colorScheme.surface, color: theme.colorScheme.background,
); );
var size = MediaQuery.of(context).size; var size = MediaQuery.of(context).size;
return Container( return Container(

View file

@ -3,6 +3,7 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_introduction_widget/src/config/introduction.dart'; import 'package:flutter_introduction_widget/src/config/introduction.dart';
@ -15,9 +16,6 @@ class Indicator extends StatelessWidget {
required this.count, required this.count,
required this.index, required this.index,
required this.indicatorBuilder, required this.indicatorBuilder,
required this.dotSize,
required this.dotSpacing,
required this.options,
super.key, super.key,
}) : assert( }) : assert(
!(mode == IndicatorMode.custom && indicatorBuilder == null), !(mode == IndicatorMode.custom && indicatorBuilder == null),
@ -25,29 +23,16 @@ class Indicator extends StatelessWidget {
'must be provided', 'must be provided',
); );
/// The mode of the indicator.
final IndicatorMode mode; final IndicatorMode mode;
/// The PageController for which the indicator is displayed.
final PageController controller; final PageController controller;
final Widget Function(
/// The total number of items managed by the PageController. BuildContext,
final int count; PageController,
int,
/// The index of the current item in the PageController. int,
)? indicatorBuilder;
final int index; final int index;
final int count;
/// Builder function for a custom indicator.
final Widget Function(BuildContext, PageController, int, int)?
indicatorBuilder;
/// The size of the dots.
final double dotSize;
/// The distance between the center of each dot.
final double dotSpacing;
final IntroductionOptions options;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -57,10 +42,9 @@ class Indicator extends StatelessWidget {
return indicatorBuilder!.call(context, controller, index, count); return indicatorBuilder!.call(context, controller, index, count);
case IndicatorMode.dot: case IndicatorMode.dot:
return DotsIndicator( return DotsIndicator(
dotSize: dotSize,
dotSpacing: dotSpacing,
controller: controller, controller: controller,
color: options.dotColor ?? theme.colorScheme.primary, color: theme.colorScheme.primary,
dotcolor: theme.colorScheme.secondary,
itemCount: count, itemCount: count,
onPageSelected: (int page) { onPageSelected: (int page) {
unawaited( unawaited(
@ -74,9 +58,8 @@ class Indicator extends StatelessWidget {
); );
case IndicatorMode.dash: case IndicatorMode.dash:
return DashIndicator( return DashIndicator(
color: theme.colorScheme.primary,
controller: controller, controller: controller,
selectedColor: options.dotColor ?? theme.colorScheme.primary, selectedColor: theme.colorScheme.primary,
itemCount: count, itemCount: count,
onPageSelected: (int page) { onPageSelected: (int page) {
unawaited( unawaited(
@ -101,20 +84,10 @@ class DashIndicator extends AnimatedWidget {
this.color = Colors.white, this.color = Colors.white,
super.key, super.key,
}) : super(listenable: controller); }) : super(listenable: controller);
/// The PageController for which the indicator is displayed.
final PageController controller; final PageController controller;
/// The color of the dashes.
final Color color; final Color color;
/// The color of the selected dash.
final Color selectedColor; final Color selectedColor;
/// The total number of items managed by the PageController.
final int itemCount; final int itemCount;
/// Callback function called when a dash is selected.
final Function(int) onPageSelected; final Function(int) onPageSelected;
int _getPage() { int _getPage() {
@ -164,15 +137,16 @@ class DotsIndicator extends AnimatedWidget {
const DotsIndicator({ const DotsIndicator({
required this.controller, required this.controller,
this.color = Colors.white, this.color = Colors.white,
this.dotcolor = Colors.green,
this.itemCount, this.itemCount,
this.onPageSelected, this.onPageSelected,
this.dotSize = 8.0,
this.dotSpacing = 24.0,
super.key, super.key,
}) : super( }) : super(
listenable: controller, listenable: controller,
); );
/// The PageController that this DotsIndicator is representing.
final Color? dotcolor;
final PageController controller; final PageController controller;
/// The number of items managed by the PageController /// The number of items managed by the PageController
@ -187,31 +161,47 @@ class DotsIndicator extends AnimatedWidget {
final Color color; final Color color;
// The base size of the dots // The base size of the dots
final double dotSize; static const double _kDotSize = 4.0;
final double dotSpacing;
Widget _buildDot(int index) => SizedBox( // The increase in the size of the selected dot
width: dotSpacing, static const double _kMaxZoom = 2.0;
child: Center(
child: Material( // The distance between the center of each dot
color: static const double _kDotSpacing = 12.0;
(((controller.page ?? controller.initialPage).round()) == index
? color Widget _buildDot(int index) {
: color.withAlpha(62)), var selectedness = Curves.easeOut.transform(
type: MaterialType.circle, max(
child: Container( 0.0,
decoration: const BoxDecoration( 1.0 -
shape: BoxShape.circle, ((controller.page ?? controller.initialPage).round() - index).abs(),
), ),
width: dotSize, );
height: dotSize, var zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
child: InkWell(
onTap: () => onPageSelected!.call(index), return SizedBox(
), width: _kDotSpacing,
child: Center(
child: Material(
color: (((controller.page ?? controller.initialPage).round()) == index
? color
: color.withAlpha(125)),
type: MaterialType.circle,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(width: 2, color: dotcolor!),
),
width: _kDotSize * 2 * zoom,
height: _kDotSize * 2 * zoom,
child: InkWell(
onTap: () => onPageSelected!.call(index),
), ),
), ),
), ),
); ),
);
}
@override @override
Widget build(BuildContext context) => Row( Widget build(BuildContext context) => Row(

View file

@ -6,9 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_introduction_widget/src/config/introduction.dart'; import 'package:flutter_introduction_widget/src/config/introduction.dart';
/// Widget representing the content of an introduction page.
class IntroductionPageContent extends StatelessWidget { class IntroductionPageContent extends StatelessWidget {
/// Constructs an IntroductionPageContent widget.
const IntroductionPageContent({ const IntroductionPageContent({
required this.title, required this.title,
required this.text, required this.text,
@ -18,19 +16,10 @@ class IntroductionPageContent extends StatelessWidget {
super.key, super.key,
}); });
/// The title widget.
final Widget? title; final Widget? title;
/// The text widget.
final Widget? text; final Widget? text;
/// The graphic widget.
final Widget? graphic; final Widget? graphic;
/// The layout style of the content.
final IntroductionLayoutStyle layoutStyle; final IntroductionLayoutStyle layoutStyle;
/// Callback function called when the content is tapped.
final VoidCallback onTap; final VoidCallback onTap;
@override @override

View file

@ -1,12 +1,10 @@
name: flutter_introduction_widget name: flutter_introduction_widget
description: Flutter Introduction Widget for showing a list of introduction pages on a single scrollable page or horizontal pageview description: Flutter Introduction Widget for showing a list of introduction pages on a single scrollable page or horizontal pageview
version: 5.0.0 version: 2.0.0
homepage: https://github.com/Iconica-Development/flutter_introduction_widget homepage: https://github.com/Iconica-Development/flutter_introduction_widget
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ">=2.18.0 <3.0.0"
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
@ -16,11 +14,10 @@ dependencies:
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
flutter_iconica_analysis: flutter_iconica_analysis:
git: git:
url: https://github.com/Iconica-Development/flutter_iconica_analysis url: https://github.com/Iconica-Development/flutter_iconica_analysis
ref: 6.0.0 ref: 6.0.0
flutter: flutter:
assets:
- assets/

View file

@ -1,10 +1,7 @@
name: flutter_introduction_workspace name: flutter_introduction_workspace
description: The use case level package using both the flutter_introduction_widget and the flutter_introduction_service combined version: 2.0.0
version: 5.0.0
publish_to: None
environment: environment:
sdk: '>=3.1.0 <4.0.0' sdk: '>=3.1.0 <4.0.0'
dev_dependencies: dev_dependencies:
melos: ">=3.0.1 <7.0.0" melos: ^3.0.1