mirror of
https://github.com/Iconica-Development/flutter_introduction.git
synced 2025-05-18 19:43:44 +02:00
feat: Added linter
This commit is contained in:
parent
36125f4174
commit
59e96159f0
27 changed files with 487 additions and 477 deletions
|
@ -1,4 +0,0 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
|
@ -1,29 +1,9 @@
|
||||||
# This file configures the analyzer, which statically analyzes Dart code to
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
# check for errors, warnings, and lints.
|
|
||||||
#
|
|
||||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
|
||||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
|
||||||
# invoked from the command line by running `flutter analyze`.
|
|
||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
# Possible to overwrite the rules from the package
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# The lint rules applied to this project can be customized in the
|
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
|
||||||
# included above or to enable additional rules. A list of all available lints
|
|
||||||
# and their documentation is published at
|
|
||||||
# https://dart-lang.github.io/linter/lints/index.html.
|
|
||||||
#
|
|
||||||
# Instead of disabling a lint rule for the entire project in the
|
|
||||||
# section below, it can also be suppressed for a single line of code
|
|
||||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
|
||||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
|
||||||
# producing the lint.
|
|
||||||
rules:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
|
|
@ -14,15 +14,13 @@ class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => MaterialApp(
|
||||||
return MaterialApp(
|
title: 'Flutter Demo',
|
||||||
title: 'Flutter Demo',
|
theme: ThemeData(
|
||||||
theme: ThemeData(
|
primarySwatch: Colors.blue,
|
||||||
primarySwatch: Colors.blue,
|
),
|
||||||
),
|
home: const MyHomePage(),
|
||||||
home: const MyHomePage(),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
|
@ -37,55 +35,51 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
IntroductionService(SharedPreferencesIntroductionDataProvider());
|
IntroductionService(SharedPreferencesIntroductionDataProvider());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Scaffold(
|
||||||
return Scaffold(
|
body: Introduction(
|
||||||
body: Introduction(
|
options: IntroductionOptions(
|
||||||
options: IntroductionOptions(
|
pages: [
|
||||||
pages: [
|
IntroductionPage(
|
||||||
IntroductionPage(
|
title: const Text('First page'),
|
||||||
title: const Text('First page'),
|
text: const Text('Wow a page'),
|
||||||
text: const Text('Wow a page'),
|
graphic: const FlutterLogo(),
|
||||||
graphic: const FlutterLogo(),
|
),
|
||||||
|
IntroductionPage(
|
||||||
|
title: const Text('Second page'),
|
||||||
|
text: const Text('Another page'),
|
||||||
|
graphic: const FlutterLogo(),
|
||||||
|
),
|
||||||
|
IntroductionPage(
|
||||||
|
title: const Text('Third page'),
|
||||||
|
text: const Text('The final page of this app'),
|
||||||
|
graphic: const FlutterLogo(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
introductionTranslations: const IntroductionTranslations(
|
||||||
|
skipButton: 'Skip it!',
|
||||||
|
nextButton: 'Previous',
|
||||||
|
previousButton: 'Next',
|
||||||
|
finishButton: 'To the app!',
|
||||||
),
|
),
|
||||||
IntroductionPage(
|
tapEnabled: true,
|
||||||
title: const Text('Second page'),
|
displayMode: IntroductionDisplayMode.multiPageHorizontal,
|
||||||
text: const Text('Another page'),
|
buttonMode: IntroductionScreenButtonMode.text,
|
||||||
graphic: const FlutterLogo(),
|
indicatorMode: IndicatorMode.dash,
|
||||||
),
|
skippable: true,
|
||||||
IntroductionPage(
|
buttonBuilder: (context, onPressed, child, type) =>
|
||||||
title: const Text('Third page'),
|
ElevatedButton(onPressed: onPressed, child: child),
|
||||||
text: const Text('The final page of this app'),
|
|
||||||
graphic: const FlutterLogo(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
introductionTranslations: const IntroductionTranslations(
|
|
||||||
skipButton: 'Skip it!',
|
|
||||||
nextButton: 'Previous',
|
|
||||||
previousButton: 'Next',
|
|
||||||
finishButton: 'To the app!',
|
|
||||||
),
|
),
|
||||||
tapEnabled: true,
|
service: service,
|
||||||
displayMode: IntroductionDisplayMode.multiPageHorizontal,
|
navigateTo: () async {
|
||||||
buttonMode: IntroductionScreenButtonMode.text,
|
await Navigator.of(context).push(
|
||||||
indicatorMode: IndicatorMode.dash,
|
MaterialPageRoute(
|
||||||
skippable: true,
|
builder: (context) => const Home(),
|
||||||
buttonBuilder: (context, onPressed, child, type) =>
|
),
|
||||||
ElevatedButton(onPressed: onPressed, child: child),
|
);
|
||||||
|
},
|
||||||
|
child: const Home(),
|
||||||
),
|
),
|
||||||
service: service,
|
);
|
||||||
navigateTo: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) {
|
|
||||||
return const Home();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Home(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Home extends StatelessWidget {
|
class Home extends StatelessWidget {
|
||||||
|
@ -94,7 +88,5 @@ class Home extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Container();
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,10 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:example/main.dart';
|
||||||
// This is a basic Flutter widget test.
|
// This is a basic Flutter widget test.
|
||||||
//
|
//
|
||||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||||
|
@ -12,8 +13,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'package:example/main.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
// Build our app and trigger a frame.
|
// Build our app and trigger a frame.
|
||||||
|
|
|
@ -2,14 +2,12 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
library flutter_introduction;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_introduction_service/flutter_introduction_service.dart';
|
import 'package:flutter_introduction_service/flutter_introduction_service.dart';
|
||||||
import 'package:flutter_introduction_widget/flutter_introduction_widget.dart';
|
import 'package:flutter_introduction_widget/flutter_introduction_widget.dart';
|
||||||
|
|
||||||
export 'package:flutter_introduction_widget/flutter_introduction_widget.dart';
|
|
||||||
export 'package:flutter_introduction_service/flutter_introduction_service.dart';
|
export 'package:flutter_introduction_service/flutter_introduction_service.dart';
|
||||||
|
export 'package:flutter_introduction_widget/flutter_introduction_widget.dart';
|
||||||
|
|
||||||
class Introduction extends StatefulWidget {
|
class Introduction extends StatefulWidget {
|
||||||
const Introduction({
|
const Introduction({
|
||||||
|
@ -21,7 +19,7 @@ class Introduction extends StatefulWidget {
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Function navigateTo;
|
final VoidCallback navigateTo;
|
||||||
final IntroductionService? service;
|
final IntroductionService? service;
|
||||||
final IntroductionOptions options;
|
final IntroductionOptions options;
|
||||||
final ScrollPhysics? physics;
|
final ScrollPhysics? physics;
|
||||||
|
@ -45,30 +43,29 @@ class _IntroductionState extends State<Introduction> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => FutureBuilder(
|
||||||
return FutureBuilder(
|
// ignore: discarded_futures
|
||||||
future: _service.shouldShow(),
|
future: _service.shouldShow(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.data == null || snapshot.data!) {
|
if (snapshot.data == null || snapshot.data!) {
|
||||||
return IntroductionScreen(
|
return IntroductionScreen(
|
||||||
options: widget.options,
|
options: widget.options,
|
||||||
onComplete: () async {
|
onComplete: () async {
|
||||||
_service.onComplete();
|
await _service.onComplete();
|
||||||
|
widget.navigateTo();
|
||||||
|
},
|
||||||
|
physics: widget.physics,
|
||||||
|
onSkip: () async {
|
||||||
|
await _service.onComplete();
|
||||||
|
widget.navigateTo();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
widget.navigateTo();
|
widget.navigateTo();
|
||||||
},
|
});
|
||||||
physics: widget.physics,
|
return widget.child ?? const CircularProgressIndicator();
|
||||||
onSkip: () async {
|
}
|
||||||
_service.onComplete();
|
},
|
||||||
widget.navigateTo();
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
widget.navigateTo();
|
|
||||||
});
|
|
||||||
return widget.child ?? const CircularProgressIndicator();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,5 +25,9 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -19,7 +19,7 @@ abstract class IntroductionInterface extends DataInterface {
|
||||||
_instance = instance;
|
_instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setCompleted([bool value = true]);
|
Future<void> setCompleted({bool value = true});
|
||||||
|
|
||||||
Future<bool> shouldShow();
|
Future<bool> shouldShow();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,10 @@ class LocalIntroductionDataProvider extends IntroductionInterface {
|
||||||
bool hasViewed = false;
|
bool hasViewed = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setCompleted([bool value = true]) async {
|
Future<void> setCompleted({bool value = true}) async {
|
||||||
hasViewed = value;
|
hasViewed = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> shouldShow() async {
|
Future<bool> shouldShow() async => hasViewed;
|
||||||
return hasViewed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,9 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
library flutter_introduction_service;
|
export 'package:flutter_introduction_interface/flutter_introduction_interface.dart';
|
||||||
|
|
||||||
export './src/introduction_service.dart';
|
export './src/introduction_service.dart';
|
||||||
export 'package:flutter_introduction_interface/flutter_introduction_interface.dart';
|
|
||||||
|
|
|
@ -10,15 +10,9 @@ class IntroductionService {
|
||||||
|
|
||||||
late final IntroductionInterface _dataProvider;
|
late final IntroductionInterface _dataProvider;
|
||||||
|
|
||||||
Future<void> onSkip() {
|
Future<void> onSkip() => _dataProvider.setCompleted(value: true);
|
||||||
return _dataProvider.setCompleted(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> onComplete() {
|
Future<void> onComplete() => _dataProvider.setCompleted(value: true);
|
||||||
return _dataProvider.setCompleted(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> shouldShow() {
|
Future<bool> shouldShow() => _dataProvider.shouldShow();
|
||||||
return _dataProvider.shouldShow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,9 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
library flutter_introduction_shared_preferences;
|
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
|
@ -13,18 +11,18 @@ class SharedPreferencesIntroductionDataProvider extends IntroductionInterface {
|
||||||
SharedPreferences? _prefs;
|
SharedPreferences? _prefs;
|
||||||
String key = '_completedIntroduction';
|
String key = '_completedIntroduction';
|
||||||
|
|
||||||
_writeKeyValue(String key, bool value) async {
|
Future<void> _writeKeyValue(String key, bool value) async {
|
||||||
_prefs!.setBool(key, value);
|
await _prefs!.setBool(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
_init() async {
|
Future<void> _init() async {
|
||||||
_prefs ??= await SharedPreferences.getInstance();
|
_prefs ??= await SharedPreferences.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setCompleted([bool value = true]) async {
|
Future<void> setCompleted({bool value = true}) async {
|
||||||
await _init();
|
await _init();
|
||||||
_writeKeyValue(key, value);
|
await _writeKeyValue(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -21,5 +21,9 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
analyzer:
|
|
||||||
errors:
|
|
||||||
todo: ignore
|
|
||||||
exclude: [lib/generated_plugin_registrant.dart]
|
|
||||||
linter:
|
|
||||||
# https://dart.dev/tools/linter-rules#lints
|
|
||||||
rules:
|
|
||||||
# pub rules
|
|
||||||
depend_on_referenced_packages: true
|
|
||||||
secure_pubspec_urls: false
|
|
||||||
sort_pub_dependencies: false
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -34,12 +34,6 @@ enum IntroductionButtonType {
|
||||||
}
|
}
|
||||||
|
|
||||||
class IntroductionPage {
|
class IntroductionPage {
|
||||||
final Widget? title;
|
|
||||||
final Widget? text;
|
|
||||||
final Widget? graphic;
|
|
||||||
final BoxDecoration? decoration;
|
|
||||||
final IntroductionLayoutStyle? layoutStyle;
|
|
||||||
|
|
||||||
/// Creates an introduction page with data used in the introduction screen for
|
/// Creates an introduction page with data used in the introduction screen for
|
||||||
/// each page.
|
/// each page.
|
||||||
///
|
///
|
||||||
|
@ -55,83 +49,14 @@ class IntroductionPage {
|
||||||
this.decoration,
|
this.decoration,
|
||||||
this.layoutStyle,
|
this.layoutStyle,
|
||||||
});
|
});
|
||||||
|
final Widget? title;
|
||||||
|
final Widget? text;
|
||||||
|
final Widget? graphic;
|
||||||
|
final BoxDecoration? decoration;
|
||||||
|
final IntroductionLayoutStyle? layoutStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
class IntroductionOptions {
|
class IntroductionOptions {
|
||||||
/// Determine when the introduction screens needs to be shown.
|
|
||||||
/// [IntroductionScreenMode.showNever] To disable introduction screens.
|
|
||||||
/// [IntroductionScreenMode.showAlways] To always show the introduction screens on startup.
|
|
||||||
/// [IntroductionScreenMode.showOnce] To only show the introduction screens once on startup.
|
|
||||||
final IntroductionScreenMode mode;
|
|
||||||
|
|
||||||
/// List of introduction pages to set the text, icons or images for the introduction screens.
|
|
||||||
final List<IntroductionPage> pages;
|
|
||||||
|
|
||||||
/// Determines whether the user can tap the screen to go to the next introduction screen.
|
|
||||||
final bool tapEnabled;
|
|
||||||
|
|
||||||
/// Determines what kind of buttons are used to navigate to the next introduction screen.
|
|
||||||
/// Introduction screens can always be navigated by swiping (or tapping if [tapEnabled] is enabled).
|
|
||||||
/// [IntroductionScreenButtonMode.text] Use text buttons (text can be set by setting the translation key or using the default appshell translations).
|
|
||||||
/// [IntroductionScreenButtonMode.icon] Use icon buttons (icons can be changed by providing a icon library)
|
|
||||||
/// [IntroductionScreenButtonMode.disabled] Disable buttons.
|
|
||||||
final IntroductionScreenButtonMode buttonMode;
|
|
||||||
|
|
||||||
/// Determines the position of the provided images or icons that are set using [pages].
|
|
||||||
/// Every introduction page provided with a image or icon will use the same layout setting.
|
|
||||||
/// [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.imageBottom] Image/icon will be at the bottom of the introduction page.
|
|
||||||
final IntroductionLayoutStyle layoutStyle;
|
|
||||||
|
|
||||||
/// Determines the style of the page indicator shown at the bottom on the introduction pages.
|
|
||||||
/// [IndicatorMode.dot] Shows a dot for each page.
|
|
||||||
/// [IndicatorMode.dash] Shows a dash for each page.
|
|
||||||
/// [IndicatorMode.custom] calls indicatorBuilder for the indicator
|
|
||||||
final IndicatorMode indicatorMode;
|
|
||||||
|
|
||||||
/// Builder that is called when [indicatorMode] is set to [IndicatorMode.custom]
|
|
||||||
final Widget Function(
|
|
||||||
BuildContext,
|
|
||||||
PageController,
|
|
||||||
int,
|
|
||||||
int,
|
|
||||||
)? indicatorBuilder;
|
|
||||||
|
|
||||||
/// Determines whether the user can skip the introduction pages using a button
|
|
||||||
/// in the header
|
|
||||||
final bool skippable;
|
|
||||||
|
|
||||||
/// Determines whether the introduction screens should be shown in a single
|
|
||||||
final TextAlign textAlign;
|
|
||||||
|
|
||||||
/// [IntroductionDisplayMode.multiPageHorizontal] Configured introduction pages will be shown on seperate screens and can be navigated using using buttons (if enabled) or swiping.
|
|
||||||
/// !Unimplemented! [IntroductionDisplayMode.singleScrollablePageVertical] All configured introduction pages will be shown on a single scrollable page.
|
|
||||||
///
|
|
||||||
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, IntroductionButtonType)?
|
|
||||||
buttonBuilder;
|
|
||||||
|
|
||||||
/// The translations for all buttons on the introductionpages
|
|
||||||
///
|
|
||||||
/// See [IntroductionTranslations] for more information
|
|
||||||
/// The following buttons have a translation:
|
|
||||||
/// - Skip
|
|
||||||
/// - Next
|
|
||||||
/// - Previous
|
|
||||||
/// - Finish
|
|
||||||
final IntroductionTranslations introductionTranslations;
|
|
||||||
|
|
||||||
const IntroductionOptions({
|
const IntroductionOptions({
|
||||||
this.introductionTranslations = const IntroductionTranslations(),
|
this.introductionTranslations = const IntroductionTranslations(),
|
||||||
this.indicatorMode = IndicatorMode.dash,
|
this.indicatorMode = IndicatorMode.dash,
|
||||||
|
@ -153,6 +78,120 @@ class IntroductionOptions {
|
||||||
'make sure to define indicatorBuilder',
|
'make sure to define indicatorBuilder',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Determine when the introduction screens needs to be shown.
|
||||||
|
///
|
||||||
|
/// [IntroductionScreenMode.showNever] To disable introduction screens.
|
||||||
|
///
|
||||||
|
/// [IntroductionScreenMode.showAlways] To always show the introduction
|
||||||
|
/// screens on startup.
|
||||||
|
///
|
||||||
|
/// [IntroductionScreenMode.showOnce] To only show the introduction screens
|
||||||
|
/// once on startup.
|
||||||
|
final IntroductionScreenMode mode;
|
||||||
|
|
||||||
|
/// List of introduction pages to set the text, icons or images for the
|
||||||
|
/// introduction screens.
|
||||||
|
final List<IntroductionPage> pages;
|
||||||
|
|
||||||
|
/// Determines whether the user can tap the screen to go to the next
|
||||||
|
/// introduction screen.
|
||||||
|
final bool tapEnabled;
|
||||||
|
|
||||||
|
/// Determines what kind of buttons are used to navigate to the next
|
||||||
|
/// introduction screen.
|
||||||
|
/// Introduction screens can always be navigated by swiping (or tapping if
|
||||||
|
/// [tapEnabled] is enabled).
|
||||||
|
///
|
||||||
|
/// [IntroductionScreenButtonMode.text] Use text buttons (text can be set by
|
||||||
|
/// setting the translation key or using the default appshell translations).
|
||||||
|
///
|
||||||
|
/// [IntroductionScreenButtonMode.icon] Use icon buttons (icons can be
|
||||||
|
/// changed by providing a icon library)
|
||||||
|
///
|
||||||
|
/// [IntroductionScreenButtonMode.disabled] Disable buttons.
|
||||||
|
final IntroductionScreenButtonMode buttonMode;
|
||||||
|
|
||||||
|
/// Determines the position of the provided images or icons that are set
|
||||||
|
/// using [pages].
|
||||||
|
/// Every introduction page provided with a image or icon will use the same
|
||||||
|
/// layout setting.
|
||||||
|
///
|
||||||
|
/// [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.imageBottom] Image/icon will be at the bottom of the introduction page.
|
||||||
|
final IntroductionLayoutStyle layoutStyle;
|
||||||
|
|
||||||
|
/// Determines the style of the page indicator shown at the bottom on the
|
||||||
|
/// introduction pages.
|
||||||
|
///
|
||||||
|
/// [IndicatorMode.dot] Shows a dot for each page.
|
||||||
|
///
|
||||||
|
/// [IndicatorMode.dash] Shows a dash for each page.
|
||||||
|
///
|
||||||
|
/// [IndicatorMode.custom] calls indicatorBuilder for the indicator
|
||||||
|
final IndicatorMode indicatorMode;
|
||||||
|
|
||||||
|
/// Builder that is called when [indicatorMode] is set
|
||||||
|
/// to [IndicatorMode.custom]
|
||||||
|
final Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
PageController,
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
)? indicatorBuilder;
|
||||||
|
|
||||||
|
/// Determines whether the user can skip the introduction pages using a button
|
||||||
|
/// in the header
|
||||||
|
final bool skippable;
|
||||||
|
|
||||||
|
/// Determines whether the introduction screens should be shown in a single
|
||||||
|
final TextAlign textAlign;
|
||||||
|
|
||||||
|
/// [IntroductionDisplayMode.multiPageHorizontal] Configured introduction
|
||||||
|
/// pages will be shown on seperate screens and can be navigated using using
|
||||||
|
/// buttons (if enabled) or swiping.
|
||||||
|
///
|
||||||
|
/// !Unimplemented! [IntroductionDisplayMode.singleScrollablePageVertical]
|
||||||
|
/// All configured introduction pages will be shown on a single scrollable
|
||||||
|
/// page.
|
||||||
|
///
|
||||||
|
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,
|
||||||
|
IntroductionButtonType,
|
||||||
|
)? buttonBuilder;
|
||||||
|
|
||||||
|
/// The translations for all buttons on the introductionpages
|
||||||
|
///
|
||||||
|
/// See [IntroductionTranslations] for more information
|
||||||
|
/// The following buttons have a translation:
|
||||||
|
/// - Skip
|
||||||
|
/// - Next
|
||||||
|
/// - Previous
|
||||||
|
/// - Finish
|
||||||
|
final IntroductionTranslations introductionTranslations;
|
||||||
|
|
||||||
IntroductionOptions copyWith({
|
IntroductionOptions copyWith({
|
||||||
IntroductionScreenMode? mode,
|
IntroductionScreenMode? mode,
|
||||||
List<IntroductionPage>? pages,
|
List<IntroductionPage>? pages,
|
||||||
|
@ -173,37 +212,35 @@ class IntroductionOptions {
|
||||||
Widget Function(BuildContext, VoidCallback, Widget, IntroductionButtonType)?
|
Widget Function(BuildContext, VoidCallback, Widget, IntroductionButtonType)?
|
||||||
buttonBuilder,
|
buttonBuilder,
|
||||||
IntroductionTranslations? introductionTranslations,
|
IntroductionTranslations? introductionTranslations,
|
||||||
}) {
|
}) =>
|
||||||
return IntroductionOptions(
|
IntroductionOptions(
|
||||||
mode: mode ?? this.mode,
|
mode: mode ?? this.mode,
|
||||||
pages: pages ?? this.pages,
|
pages: pages ?? this.pages,
|
||||||
tapEnabled: tapEnabled ?? this.tapEnabled,
|
tapEnabled: tapEnabled ?? this.tapEnabled,
|
||||||
buttonMode: buttonMode ?? this.buttonMode,
|
buttonMode: buttonMode ?? this.buttonMode,
|
||||||
layoutStyle: layoutStyle ?? this.layoutStyle,
|
layoutStyle: layoutStyle ?? this.layoutStyle,
|
||||||
indicatorMode: indicatorMode ?? this.indicatorMode,
|
indicatorMode: indicatorMode ?? this.indicatorMode,
|
||||||
indicatorBuilder: indicatorBuilder ?? this.indicatorBuilder,
|
indicatorBuilder: indicatorBuilder ?? this.indicatorBuilder,
|
||||||
skippable: skippable ?? this.skippable,
|
skippable: skippable ?? this.skippable,
|
||||||
textAlign: textAlign ?? this.textAlign,
|
textAlign: textAlign ?? this.textAlign,
|
||||||
displayMode: displayMode ?? this.displayMode,
|
displayMode: displayMode ?? this.displayMode,
|
||||||
controlMode: controlMode ?? this.controlMode,
|
controlMode: controlMode ?? this.controlMode,
|
||||||
buttonBuilder: buttonBuilder ?? this.buttonBuilder,
|
buttonBuilder: buttonBuilder ?? this.buttonBuilder,
|
||||||
introductionTranslations:
|
introductionTranslations:
|
||||||
introductionTranslations ?? this.introductionTranslations,
|
introductionTranslations ?? this.introductionTranslations,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
class IntroductionTranslations {
|
class IntroductionTranslations {
|
||||||
final String skipButton;
|
|
||||||
final String nextButton;
|
|
||||||
final String previousButton;
|
|
||||||
final String finishButton;
|
|
||||||
|
|
||||||
const IntroductionTranslations({
|
const IntroductionTranslations({
|
||||||
this.skipButton = 'skip',
|
this.skipButton = 'skip',
|
||||||
this.nextButton = 'next',
|
this.nextButton = 'next',
|
||||||
this.previousButton = 'previous',
|
this.previousButton = 'previous',
|
||||||
this.finishButton = 'finish',
|
this.finishButton = 'finish',
|
||||||
});
|
});
|
||||||
|
final String skipButton;
|
||||||
|
final String nextButton;
|
||||||
|
final String previousButton;
|
||||||
|
final String finishButton;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,22 @@
|
||||||
// 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/introduction.dart';
|
||||||
import 'package:flutter_introduction_widget/src/types/page_introduction.dart';
|
import 'package:flutter_introduction_widget/src/types/page_introduction.dart';
|
||||||
import 'package:flutter_introduction_widget/src/types/single_introduction.dart';
|
import 'package:flutter_introduction_widget/src/types/single_introduction.dart';
|
||||||
|
|
||||||
import 'config/introduction.dart';
|
|
||||||
|
|
||||||
const kAnimationDuration = Duration(milliseconds: 300);
|
const kAnimationDuration = Duration(milliseconds: 300);
|
||||||
|
|
||||||
class IntroductionScreen extends StatelessWidget {
|
class IntroductionScreen extends StatelessWidget {
|
||||||
const IntroductionScreen({
|
const IntroductionScreen({
|
||||||
Key? key,
|
|
||||||
required this.options,
|
required this.options,
|
||||||
required this.onComplete,
|
required this.onComplete,
|
||||||
|
super.key,
|
||||||
this.physics,
|
this.physics,
|
||||||
this.onNext,
|
this.onNext,
|
||||||
this.onPrevious,
|
this.onPrevious,
|
||||||
this.onSkip,
|
this.onSkip,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
/// The options used to build the introduction screen
|
/// The options used to build the introduction screen
|
||||||
final IntroductionOptions options;
|
final IntroductionOptions options;
|
||||||
|
@ -40,28 +39,26 @@ class IntroductionScreen extends StatelessWidget {
|
||||||
final void Function(IntroductionPage)? onPrevious;
|
final void Function(IntroductionPage)? onPrevious;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Scaffold(
|
||||||
return Scaffold(
|
backgroundColor: Colors.transparent,
|
||||||
backgroundColor: Colors.transparent,
|
body: Builder(
|
||||||
body: Builder(
|
builder: (context) {
|
||||||
builder: (context) {
|
switch (options.displayMode) {
|
||||||
switch (options.displayMode) {
|
case IntroductionDisplayMode.multiPageHorizontal:
|
||||||
case IntroductionDisplayMode.multiPageHorizontal:
|
return MultiPageIntroductionScreen(
|
||||||
return MultiPageIntroductionScreen(
|
onComplete: onComplete,
|
||||||
onComplete: onComplete,
|
physics: physics,
|
||||||
physics: physics,
|
onSkip: onSkip,
|
||||||
onSkip: onSkip,
|
onPrevious: onPrevious,
|
||||||
onPrevious: onPrevious,
|
onNext: onNext,
|
||||||
onNext: onNext,
|
options: options,
|
||||||
options: options,
|
);
|
||||||
);
|
case IntroductionDisplayMode.singleScrollablePageVertical:
|
||||||
case IntroductionDisplayMode.singleScrollablePageVertical:
|
return SingleIntroductionPage(
|
||||||
return SingleIntroductionPage(
|
options: options,
|
||||||
options: options,
|
);
|
||||||
);
|
}
|
||||||
}
|
},
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'dart:async';
|
||||||
import 'package:flutter_introduction_widget/src/introduction.dart';
|
|
||||||
|
|
||||||
import '../config/introduction.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../widgets/background.dart';
|
import 'package:flutter_introduction_widget/src/config/introduction.dart';
|
||||||
import '../widgets/indicator.dart';
|
import 'package:flutter_introduction_widget/src/introduction.dart';
|
||||||
import '../widgets/page_content.dart';
|
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/page_content.dart';
|
||||||
|
|
||||||
class MultiPageIntroductionScreen extends StatefulWidget {
|
class MultiPageIntroductionScreen extends StatefulWidget {
|
||||||
const MultiPageIntroductionScreen({
|
const MultiPageIntroductionScreen({
|
||||||
|
@ -18,8 +19,8 @@ class MultiPageIntroductionScreen extends StatefulWidget {
|
||||||
this.onNext,
|
this.onNext,
|
||||||
this.onPrevious,
|
this.onPrevious,
|
||||||
this.onSkip,
|
this.onSkip,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final VoidCallback onComplete;
|
final VoidCallback onComplete;
|
||||||
final VoidCallback? onSkip;
|
final VoidCallback? onSkip;
|
||||||
|
@ -71,7 +72,7 @@ class _MultiPageIntroductionScreenState
|
||||||
}
|
}
|
||||||
// add bouncing scroll physics support
|
// add bouncing scroll physics support
|
||||||
if (notification is ScrollUpdateNotification) {
|
if (notification is ScrollUpdateNotification) {
|
||||||
final offset = notification.metrics.pixels;
|
var offset = notification.metrics.pixels;
|
||||||
if (offset > notification.metrics.maxScrollExtent + 8) {
|
if (offset > notification.metrics.maxScrollExtent + 8) {
|
||||||
widget.onComplete.call();
|
widget.onComplete.call();
|
||||||
}
|
}
|
||||||
|
@ -83,17 +84,15 @@ class _MultiPageIntroductionScreenState
|
||||||
physics: widget.physics,
|
physics: widget.physics,
|
||||||
children: List.generate(
|
children: List.generate(
|
||||||
pages.length,
|
pages.length,
|
||||||
(index) {
|
(index) => ExplainerPage(
|
||||||
return ExplainerPage(
|
onTap: () {
|
||||||
onTap: () {
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
widget.onNext?.call(pages[_currentPage.value]);
|
},
|
||||||
},
|
controller: _controller,
|
||||||
controller: _controller,
|
page: pages[index],
|
||||||
page: pages[index],
|
options: widget.options,
|
||||||
options: widget.options,
|
index: index,
|
||||||
index: index,
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -109,25 +108,22 @@ class _MultiPageIntroductionScreenState
|
||||||
height: 64,
|
height: 64,
|
||||||
child: AnimatedBuilder(
|
child: AnimatedBuilder(
|
||||||
animation: _controller,
|
animation: _controller,
|
||||||
builder: (context, _) {
|
builder: (context, _) => Row(
|
||||||
return Row(
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
children: [
|
||||||
children: [
|
if (widget.options.skippable && !_isLast(pages)) ...[
|
||||||
if (widget.options.skippable &&
|
TextButton(
|
||||||
!_isLast(pages)) ...[
|
onPressed: widget.onComplete,
|
||||||
TextButton(
|
child: Text(translations.skipButton),
|
||||||
onPressed: widget.onComplete,
|
),
|
||||||
child: Text(translations.skipButton),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
);
|
],
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
] else ...[
|
] else ...[
|
||||||
const SizedBox.shrink()
|
const SizedBox.shrink(),
|
||||||
],
|
],
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
|
@ -190,23 +186,22 @@ class _MultiPageIntroductionScreenState
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
AnimatedBuilder(
|
AnimatedBuilder(
|
||||||
animation: _controller,
|
animation: _controller,
|
||||||
builder: (context, _) {
|
builder: (context, _) => IntroductionIconButtons(
|
||||||
return IntroductionIconButtons(
|
controller: _controller,
|
||||||
controller: _controller,
|
next: _isNext(pages),
|
||||||
next: _isNext(pages),
|
previous: _isPrevious,
|
||||||
previous: _isPrevious,
|
last: _isLast(pages),
|
||||||
last: _isLast(pages),
|
options: widget.options,
|
||||||
options: widget.options,
|
onFinish: widget.onComplete,
|
||||||
onFinish: widget.onComplete,
|
onNext: () {
|
||||||
onNext: () {
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
widget.onNext?.call(pages[_currentPage.value]);
|
},
|
||||||
},
|
onPrevious: () {
|
||||||
onPrevious: () {
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
widget.onNext?.call(pages[_currentPage.value]);
|
},
|
||||||
},
|
),
|
||||||
);
|
),
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -227,8 +222,8 @@ class ExplainerPage extends StatelessWidget {
|
||||||
required this.index,
|
required this.index,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final IntroductionPage page;
|
final IntroductionPage page;
|
||||||
final IntroductionOptions options;
|
final IntroductionOptions options;
|
||||||
|
@ -297,8 +292,8 @@ class IntroductionTwoButtons extends StatelessWidget {
|
||||||
required this.onFinish,
|
required this.onFinish,
|
||||||
required this.onNext,
|
required this.onNext,
|
||||||
required this.onPrevious,
|
required this.onPrevious,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final IntroductionOptions options;
|
final IntroductionOptions options;
|
||||||
final PageController controller;
|
final PageController controller;
|
||||||
|
@ -310,8 +305,8 @@ class IntroductionTwoButtons extends StatelessWidget {
|
||||||
final bool next;
|
final bool next;
|
||||||
final bool last;
|
final bool last;
|
||||||
|
|
||||||
void _previous() {
|
Future<void> _previous() async {
|
||||||
controller.previousPage(
|
await controller.previousPage(
|
||||||
duration: kAnimationDuration,
|
duration: kAnimationDuration,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
|
@ -402,8 +397,8 @@ class IntroductionTwoButtons extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_next() {
|
Future<void> _next() async {
|
||||||
controller.nextPage(
|
await controller.nextPage(
|
||||||
duration: kAnimationDuration,
|
duration: kAnimationDuration,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
|
@ -421,8 +416,8 @@ class IntroductionOneButton extends StatelessWidget {
|
||||||
required this.onFinish,
|
required this.onFinish,
|
||||||
required this.onNext,
|
required this.onNext,
|
||||||
required this.onPrevious,
|
required this.onPrevious,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final IntroductionOptions options;
|
final IntroductionOptions options;
|
||||||
final PageController controller;
|
final PageController controller;
|
||||||
|
@ -434,8 +429,8 @@ class IntroductionOneButton extends StatelessWidget {
|
||||||
final bool next;
|
final bool next;
|
||||||
final bool last;
|
final bool last;
|
||||||
|
|
||||||
void _previous() {
|
Future<void> _previous() async {
|
||||||
controller.previousPage(
|
await controller.previousPage(
|
||||||
duration: kAnimationDuration,
|
duration: kAnimationDuration,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
|
@ -508,8 +503,8 @@ class IntroductionOneButton extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_next() {
|
Future<void> _next() async {
|
||||||
controller.nextPage(
|
await controller.nextPage(
|
||||||
duration: kAnimationDuration,
|
duration: kAnimationDuration,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
|
@ -527,8 +522,8 @@ class IntroductionIconButtons extends StatelessWidget {
|
||||||
required this.onFinish,
|
required this.onFinish,
|
||||||
required this.onNext,
|
required this.onNext,
|
||||||
required this.onPrevious,
|
required this.onPrevious,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final IntroductionOptions options;
|
final IntroductionOptions options;
|
||||||
final PageController controller;
|
final PageController controller;
|
||||||
|
@ -540,8 +535,8 @@ class IntroductionIconButtons extends StatelessWidget {
|
||||||
final bool next;
|
final bool next;
|
||||||
final bool last;
|
final bool last;
|
||||||
|
|
||||||
void _previous() {
|
Future<void> _previous() async {
|
||||||
controller.previousPage(
|
await controller.previousPage(
|
||||||
duration: kAnimationDuration,
|
duration: kAnimationDuration,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
|
@ -549,39 +544,37 @@ class IntroductionIconButtons extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Center(
|
||||||
return Center(
|
child: Row(
|
||||||
child: Row(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
children: [
|
if (options.buttonMode == IntroductionScreenButtonMode.icon) ...[
|
||||||
if (options.buttonMode == IntroductionScreenButtonMode.icon) ...[
|
if (previous) ...[
|
||||||
if (previous) ...[
|
IconButton(
|
||||||
|
iconSize: 70,
|
||||||
|
onPressed: _previous,
|
||||||
|
icon: const Icon(Icons.chevron_left),
|
||||||
|
),
|
||||||
|
] else
|
||||||
|
const SizedBox.shrink(),
|
||||||
IconButton(
|
IconButton(
|
||||||
iconSize: 70,
|
iconSize: 70,
|
||||||
onPressed: _previous,
|
onPressed: () {
|
||||||
icon: const Icon(Icons.chevron_left),
|
if (next) {
|
||||||
|
unawaited(_next());
|
||||||
|
} else {
|
||||||
|
onFinish?.call();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.chevron_right),
|
||||||
),
|
),
|
||||||
] else
|
],
|
||||||
const SizedBox.shrink(),
|
|
||||||
IconButton(
|
|
||||||
iconSize: 70,
|
|
||||||
onPressed: () {
|
|
||||||
if (next) {
|
|
||||||
_next();
|
|
||||||
} else {
|
|
||||||
onFinish?.call();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.chevron_right),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_next() {
|
Future<void> _next() async {
|
||||||
controller.nextPage(
|
await controller.nextPage(
|
||||||
duration: kAnimationDuration,
|
duration: kAnimationDuration,
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
import '../config/introduction.dart';
|
import 'package:flutter_introduction_widget/src/config/introduction.dart';
|
||||||
|
|
||||||
class SingleIntroductionPage extends StatelessWidget {
|
class SingleIntroductionPage extends StatelessWidget {
|
||||||
const SingleIntroductionPage({
|
const SingleIntroductionPage({
|
||||||
Key? key,
|
|
||||||
required this.options,
|
required this.options,
|
||||||
}) : super(key: key);
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final IntroductionOptions options;
|
final IntroductionOptions options;
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Background extends StatelessWidget {
|
class Background extends StatelessWidget {
|
||||||
const Background({
|
const Background({
|
||||||
this.background,
|
|
||||||
required this.child,
|
required this.child,
|
||||||
Key? key,
|
this.background,
|
||||||
}) : super(key: key);
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final BoxDecoration? background;
|
final BoxDecoration? background;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:math';
|
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/introduction.dart';
|
import 'package:flutter_introduction_widget/src/introduction.dart';
|
||||||
|
|
||||||
import '../config/introduction.dart';
|
|
||||||
|
|
||||||
class Indicator extends StatelessWidget {
|
class Indicator extends StatelessWidget {
|
||||||
const Indicator({
|
const Indicator({
|
||||||
required this.mode,
|
required this.mode,
|
||||||
|
@ -16,12 +16,12 @@ class Indicator extends StatelessWidget {
|
||||||
required this.count,
|
required this.count,
|
||||||
required this.index,
|
required this.index,
|
||||||
required this.indicatorBuilder,
|
required this.indicatorBuilder,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : assert(
|
}) : assert(
|
||||||
!(mode == IndicatorMode.custom && indicatorBuilder == null),
|
!(mode == IndicatorMode.custom && indicatorBuilder == null),
|
||||||
"When a custom indicator is used the indicatorBuilder must be provided",
|
'When a custom indicator is used the indicatorBuilder '
|
||||||
),
|
'must be provided',
|
||||||
super(key: key);
|
);
|
||||||
|
|
||||||
final IndicatorMode mode;
|
final IndicatorMode mode;
|
||||||
final PageController controller;
|
final PageController controller;
|
||||||
|
@ -36,7 +36,7 @@ class Indicator extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case IndicatorMode.custom:
|
case IndicatorMode.custom:
|
||||||
return indicatorBuilder!.call(context, controller, index, count);
|
return indicatorBuilder!.call(context, controller, index, count);
|
||||||
|
@ -47,10 +47,12 @@ class Indicator extends StatelessWidget {
|
||||||
dotcolor: theme.colorScheme.secondary,
|
dotcolor: theme.colorScheme.secondary,
|
||||||
itemCount: count,
|
itemCount: count,
|
||||||
onPageSelected: (int page) {
|
onPageSelected: (int page) {
|
||||||
controller.animateToPage(
|
unawaited(
|
||||||
page,
|
controller.animateToPage(
|
||||||
duration: kAnimationDuration,
|
page,
|
||||||
curve: Curves.easeInOut,
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -60,10 +62,12 @@ class Indicator extends StatelessWidget {
|
||||||
selectedColor: theme.colorScheme.primary,
|
selectedColor: theme.colorScheme.primary,
|
||||||
itemCount: count,
|
itemCount: count,
|
||||||
onPageSelected: (int page) {
|
onPageSelected: (int page) {
|
||||||
controller.animateToPage(
|
unawaited(
|
||||||
page,
|
controller.animateToPage(
|
||||||
duration: kAnimationDuration,
|
page,
|
||||||
curve: Curves.easeInOut,
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -72,76 +76,73 @@ class Indicator extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DashIndicator extends AnimatedWidget {
|
class DashIndicator extends AnimatedWidget {
|
||||||
|
const DashIndicator({
|
||||||
|
required this.controller,
|
||||||
|
required this.selectedColor,
|
||||||
|
required this.itemCount,
|
||||||
|
required this.onPageSelected,
|
||||||
|
this.color = Colors.white,
|
||||||
|
super.key,
|
||||||
|
}) : super(listenable: controller);
|
||||||
final PageController controller;
|
final PageController controller;
|
||||||
final Color color;
|
final Color color;
|
||||||
final Color selectedColor;
|
final Color selectedColor;
|
||||||
final int itemCount;
|
final int itemCount;
|
||||||
final Function(int) onPageSelected;
|
final Function(int) onPageSelected;
|
||||||
const DashIndicator(
|
|
||||||
{required this.controller,
|
|
||||||
this.color = Colors.white,
|
|
||||||
required this.selectedColor,
|
|
||||||
required this.itemCount,
|
|
||||||
required this.onPageSelected,
|
|
||||||
Key? key})
|
|
||||||
: super(listenable: controller, key: key);
|
|
||||||
|
|
||||||
int _getPage() {
|
int _getPage() {
|
||||||
try {
|
try {
|
||||||
return controller.page?.round() ?? 0;
|
return controller.page?.round() ?? 0;
|
||||||
} catch (_) {
|
} on Exception catch (_) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
int index = _getPage();
|
var index = _getPage();
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
for (int i = 0; i < itemCount; i++) ...[
|
for (int i = 0; i < itemCount; i++) ...[
|
||||||
buildDash(i, index == i),
|
buildDash(i, selected: index == i),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildDash(int index, bool selected) {
|
Widget buildDash(int index, {required bool selected}) => SizedBox(
|
||||||
return SizedBox(
|
width: 20,
|
||||||
width: 20,
|
child: Center(
|
||||||
child: Center(
|
child: Material(
|
||||||
child: Material(
|
color: selected ? color : color.withAlpha(125),
|
||||||
color: selected ? color : color.withAlpha(125),
|
type: MaterialType.card,
|
||||||
type: MaterialType.card,
|
child: Container(
|
||||||
child: Container(
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
borderRadius: BorderRadius.circular(2.5),
|
||||||
borderRadius: BorderRadius.circular(2.5),
|
),
|
||||||
),
|
width: 16,
|
||||||
width: 16,
|
height: 5,
|
||||||
height: 5,
|
child: InkWell(
|
||||||
child: InkWell(
|
onTap: () => onPageSelected.call(index),
|
||||||
onTap: () => onPageSelected.call(index),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An indicator showing the currently selected page of a PageController
|
/// An indicator showing the currently selected page of a PageController
|
||||||
class DotsIndicator extends AnimatedWidget {
|
class DotsIndicator extends AnimatedWidget {
|
||||||
const DotsIndicator({
|
const DotsIndicator({
|
||||||
this.color = Colors.white,
|
|
||||||
required this.controller,
|
required this.controller,
|
||||||
|
this.color = Colors.white,
|
||||||
this.dotcolor = Colors.green,
|
this.dotcolor = Colors.green,
|
||||||
this.itemCount,
|
this.itemCount,
|
||||||
this.onPageSelected,
|
this.onPageSelected,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(
|
}) : super(
|
||||||
listenable: controller,
|
listenable: controller,
|
||||||
key: key,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The PageController that this DotsIndicator is representing.
|
/// The PageController that this DotsIndicator is representing.
|
||||||
|
@ -169,14 +170,14 @@ class DotsIndicator extends AnimatedWidget {
|
||||||
static const double _kDotSpacing = 12.0;
|
static const double _kDotSpacing = 12.0;
|
||||||
|
|
||||||
Widget _buildDot(int index) {
|
Widget _buildDot(int index) {
|
||||||
double selectedness = Curves.easeOut.transform(
|
var selectedness = Curves.easeOut.transform(
|
||||||
max(
|
max(
|
||||||
0.0,
|
0.0,
|
||||||
1.0 -
|
1.0 -
|
||||||
((controller.page ?? controller.initialPage).round() - index).abs(),
|
((controller.page ?? controller.initialPage).round() - index).abs(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
|
var zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: _kDotSpacing,
|
width: _kDotSpacing,
|
||||||
|
@ -203,10 +204,8 @@ class DotsIndicator extends AnimatedWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Row(
|
||||||
return Row(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: List<Widget>.generate(itemCount!, _buildDot),
|
||||||
children: List<Widget>.generate(itemCount!, _buildDot),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../config/introduction.dart';
|
import 'package:flutter_introduction_widget/src/config/introduction.dart';
|
||||||
|
|
||||||
class IntroductionPageContent extends StatelessWidget {
|
class IntroductionPageContent extends StatelessWidget {
|
||||||
const IntroductionPageContent({
|
const IntroductionPageContent({
|
||||||
|
@ -13,8 +13,8 @@ class IntroductionPageContent extends StatelessWidget {
|
||||||
required this.graphic,
|
required this.graphic,
|
||||||
required this.layoutStyle,
|
required this.layoutStyle,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final Widget? title;
|
final Widget? title;
|
||||||
final Widget? text;
|
final Widget? text;
|
||||||
|
@ -23,24 +23,22 @@ class IntroductionPageContent extends StatelessWidget {
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => GestureDetector(
|
||||||
return GestureDetector(
|
onTap: onTap,
|
||||||
onTap: onTap,
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
if (graphic != null &&
|
||||||
if (graphic != null &&
|
layoutStyle == IntroductionLayoutStyle.imageTop)
|
||||||
layoutStyle == IntroductionLayoutStyle.imageTop)
|
graphic!,
|
||||||
graphic!,
|
if (title != null) title!,
|
||||||
if (title != null) title!,
|
if (graphic != null &&
|
||||||
if (graphic != null &&
|
layoutStyle == IntroductionLayoutStyle.imageCenter)
|
||||||
layoutStyle == IntroductionLayoutStyle.imageCenter)
|
graphic!,
|
||||||
graphic!,
|
if (text != null) text!,
|
||||||
if (text != null) text!,
|
if (graphic != null &&
|
||||||
if (graphic != null &&
|
layoutStyle == IntroductionLayoutStyle.imageBottom)
|
||||||
layoutStyle == IntroductionLayoutStyle.imageBottom)
|
graphic!,
|
||||||
graphic!,
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,5 +15,9 @@ dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
Loading…
Reference in a new issue