mirror of
https://github.com/Iconica-Development/flutter_introduction.git
synced 2025-05-18 19:43:44 +02:00
feat: create melos variant of all combined introduction components
This commit is contained in:
parent
571dd66aa9
commit
2910202f6b
64 changed files with 2626 additions and 600 deletions
10
.github/dependabot.yaml
vendored
Normal file
10
.github/dependabot.yaml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "pub"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
12
.github/workflows/melos-component-ci.yml
vendored
Normal file
12
.github/workflows/melos-component-ci.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name: Iconica Standard Melos CI Workflow
|
||||||
|
# Workflow Caller version: 1.0.0
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-global-iconica-workflow:
|
||||||
|
uses: Iconica-Development/.github/.github/workflows/melos-ci.yml@master
|
||||||
|
secrets: inherit
|
||||||
|
permissions: write-all
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -31,4 +31,13 @@ build/
|
||||||
|
|
||||||
.metadata
|
.metadata
|
||||||
|
|
||||||
|
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
pubspec_overrides.yaml
|
||||||
|
|
||||||
|
example/ios
|
||||||
example/web
|
example/web
|
||||||
|
example/android
|
||||||
|
example/linux
|
||||||
|
example/macos
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
## 1.0.0
|
|
||||||
|
|
||||||
* Update introduction_widget and introduction_service
|
|
||||||
|
|
||||||
## 0.0.1
|
## 0.0.1
|
||||||
|
|
||||||
* Initial release.
|
* Initial release of combined flutter_introduction melos project
|
||||||
|
|
194
CONTRIBUTING.md
Normal file
194
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
# Contributing
|
||||||
|
First off, thanks for taking the time to contribute! ❤️
|
||||||
|
|
||||||
|
All types of contributions are encouraged and valued.
|
||||||
|
See the [Table of Contents](#table-of-contents) for different ways to help and details about how we handle them.
|
||||||
|
Please make sure to read the relevant section before making your contribution.
|
||||||
|
It will make it a lot easier for us maintainers and smooth out the experience for all involved.
|
||||||
|
Iconica looks forward to your contributions. 🎉
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
- [Code of conduct](#code-of-conduct)
|
||||||
|
- [I Have a Question](#i-have-a-question)
|
||||||
|
- [I Want To Contribute](#i-want-to-contribute)
|
||||||
|
- [Reporting Bugs](#reporting-bugs)
|
||||||
|
- [Contributing code](#contributing-code)
|
||||||
|
|
||||||
|
## Code of conduct
|
||||||
|
|
||||||
|
### Legal notice
|
||||||
|
When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
|
||||||
|
All accepted pull requests and other additions to this project will be considered intellectual property of Iconica.
|
||||||
|
|
||||||
|
All repositories should be kept clean of jokes, easter eggs and other unnecessary additions.
|
||||||
|
|
||||||
|
## I have a question
|
||||||
|
|
||||||
|
If you want to ask a question, we assume that you have read the available documentation found within the code.
|
||||||
|
Before you ask a question, it is best to search for existing issues that might help you.
|
||||||
|
In case you have found a suitable issue but still need clarification, you can ask your question
|
||||||
|
It is also advisable to search the internet for answers first.
|
||||||
|
|
||||||
|
If you then still feel the need to ask a question and need clarification, we recommend the following:
|
||||||
|
|
||||||
|
- Open an issue.
|
||||||
|
- Provide as much context as you can about what you're running into.
|
||||||
|
|
||||||
|
We will then take care of the issue as soon as possible.
|
||||||
|
|
||||||
|
## I want to contribute
|
||||||
|
|
||||||
|
### Reporting bugs
|
||||||
|
|
||||||
|
<!-- omit in toc -->
|
||||||
|
**Before submitting a bug report**
|
||||||
|
|
||||||
|
A good bug report shouldn't leave others needing to chase you up for more information.
|
||||||
|
Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report.
|
||||||
|
Please complete the following steps in advance to help us fix any potential bug as fast as possible.
|
||||||
|
|
||||||
|
- Make sure that you are using the latest version.
|
||||||
|
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (If you are looking for support, you might want to check [this section](#i-have-a-question)).
|
||||||
|
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error.
|
||||||
|
- Also make sure to search the internet (including Stack Overflow) to see if users outside of Iconica have discussed the issue.
|
||||||
|
- Collect information about the bug:
|
||||||
|
- Stack trace (Traceback)
|
||||||
|
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
|
||||||
|
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
|
||||||
|
- Time and date of occurance
|
||||||
|
- Describe the expected result and actual result
|
||||||
|
- Can you reliably reproduce the issue? And can you also reproduce it with older versions? Describe all steps that lead to the bug.
|
||||||
|
|
||||||
|
Once it's filed:
|
||||||
|
|
||||||
|
- The project team will label the issue accordingly.
|
||||||
|
- A team member will try to reproduce the issue with your provided steps.
|
||||||
|
If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for additional information.
|
||||||
|
- If the team is able to reproduce the issue, it will be moved into the backlog, as well as marked with a priority, and the issue will be left to be [implemented by someone](#contributing-code).
|
||||||
|
|
||||||
|
### Contributing code
|
||||||
|
|
||||||
|
When you start working on your contribution, make sure you are aware of the relevant documentation and the functionality of the component you are working on.
|
||||||
|
|
||||||
|
When writing code, follow the style guidelines set by Dart: [Effective Dart](https://Dart.dev/guides/language/effective-Dart). This contains most information you will need to write clean Dart code that is well documented.
|
||||||
|
|
||||||
|
**Documentation**
|
||||||
|
|
||||||
|
As Effective Dart indicates, documenting your public methods with Dart doc comments is recommended.
|
||||||
|
Aside from Effective Dart, we require specific information in the documentation of a method:
|
||||||
|
|
||||||
|
At the very least, your documentation should first name what the code does, then followed below by requirements for calling the method, the result of the method.
|
||||||
|
Any references to internal variables or other methods should be done through [var] to indicate a reference.
|
||||||
|
|
||||||
|
If the method or class is complex enough (determined by the reviewers) an example is required.
|
||||||
|
If unsure, add an example in the docs using code blocks.
|
||||||
|
|
||||||
|
For classes and methods, document the individual parameters with their implications.
|
||||||
|
|
||||||
|
> Tip: Remember that the shortest documentation can be written by having good descriptive names in the first place.
|
||||||
|
|
||||||
|
An example:
|
||||||
|
```Dart
|
||||||
|
library iconica_utilities.bidirectional_sorter;
|
||||||
|
|
||||||
|
part 'sorter.Dart';
|
||||||
|
part 'enum.Dart';
|
||||||
|
|
||||||
|
/// Generic sort method, allow sorting of list with primitives or complex types.
|
||||||
|
/// Uses [SortDirection] to determine the direction, either Ascending or Descending,
|
||||||
|
/// Gets called on [List] toSort of type [T] which cannot be shorter than 2.
|
||||||
|
/// Optionally for complex types a [Comparable] [Function] can be given to compare complex types.
|
||||||
|
/// ```
|
||||||
|
/// List<TestObject> objects = [];
|
||||||
|
/// for (int i = 0; i < 10; i++) {
|
||||||
|
/// objects.add(TestObject(name: "name", id: i));
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// sort<TestObject>(
|
||||||
|
/// SortDirection.descending, objects, (object) => object.id);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// In the above example a list of TestObjects is created, and then sorted in descending order.
|
||||||
|
/// If the implementation of TestObject is as following:
|
||||||
|
/// ```
|
||||||
|
/// class TestObject {
|
||||||
|
/// final String name;
|
||||||
|
/// final int id;
|
||||||
|
///
|
||||||
|
/// TestObject({required this.name, required this.id});
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// And the list is logged to the console, the following will appear:
|
||||||
|
/// ```
|
||||||
|
/// [name9, name8, name7, name6, name5, name4, name3, name2, name1, name0]
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
void sort<T>(
|
||||||
|
/// Determines the sorting direction, can be either Ascending or Descending
|
||||||
|
SortDirection sortDirection,
|
||||||
|
|
||||||
|
/// Incoming list, which gets sorted
|
||||||
|
List<T> toSort, [
|
||||||
|
|
||||||
|
/// Optional comparable, which is only necessary for complex types
|
||||||
|
SortFieldGetter<T>? sortValueCallback,
|
||||||
|
]) {
|
||||||
|
if (toSort.length < 2) return;
|
||||||
|
assert(
|
||||||
|
toSort.whereType<Comparable>().isNotEmpty || sortValueCallback != null);
|
||||||
|
BidirectionalSorter<T>(
|
||||||
|
sortInstructions: <SortInstruction<T>>[
|
||||||
|
SortInstruction(
|
||||||
|
sortValueCallback ?? (t) => t as Comparable, sortDirection),
|
||||||
|
],
|
||||||
|
).sort(toSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// same functionality as [sort] but with the added functionality
|
||||||
|
/// of sorting multiple values
|
||||||
|
void sortMulti<T>(
|
||||||
|
/// Incoming list, which gets sorted
|
||||||
|
List<T> toSort,
|
||||||
|
|
||||||
|
/// list of comparables to sort multiple values at once,
|
||||||
|
/// priority based on index
|
||||||
|
List<SortInstruction<T>> sortValueCallbacks,
|
||||||
|
) {
|
||||||
|
if (toSort.length < 2) return;
|
||||||
|
assert(sortValueCallbacks.isNotEmpty);
|
||||||
|
BidirectionalSorter<T>(
|
||||||
|
sortInstructions: sortValueCallbacks,
|
||||||
|
).sort(toSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Tests**
|
||||||
|
|
||||||
|
For each public method that was created, excluding widgets, which contains any form of logic (e.g. Calculations, predicates or major side-effects) tests are required.
|
||||||
|
|
||||||
|
A set of tests is written for each method, covering at least each path within the method. For example:
|
||||||
|
|
||||||
|
```Dart
|
||||||
|
void foo() {
|
||||||
|
try {
|
||||||
|
var bar = doSomething();
|
||||||
|
if (bar) {
|
||||||
|
doSomethingElse();
|
||||||
|
} else {
|
||||||
|
doSomethingCool();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
displayError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The method above should result in 3 tests:
|
||||||
|
|
||||||
|
1. A test for the path leading to displayError by the cause of an exception
|
||||||
|
2. A test for if bar is true, resulting in doSomethingElse()
|
||||||
|
3. A test for if bar is false, resulting in the doSomethingCool() method being called.
|
||||||
|
|
||||||
|
This means that we require 100% coverage of each method you test.
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2022 Iconica, All rights reserved.
|
Copyright (c) 2023 Iconica, All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
|
38
README.md
38
README.md
|
@ -1,39 +1,21 @@
|
||||||
<!--
|
# Flutter Introduction
|
||||||
This README describes the package. If you publish this package to pub.dev,
|
|
||||||
this README's contents appear on the landing page for your package.
|
|
||||||
|
|
||||||
For information about how to write a good package README, see the guide for
|

|
||||||
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
|
|
||||||
|
|
||||||
For general information about developing packages, see the Dart guide for
|
## Setup
|
||||||
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
|
|
||||||
and the Flutter guide for
|
|
||||||
[developing packages and plugins](https://flutter.dev/developing-packages).
|
|
||||||
-->
|
|
||||||
|
|
||||||
TODO: Put a short description of the package here that helps potential users
|
|
||||||
know whether this package might be useful for them.
|
|
||||||
|
|
||||||
## Features
|
## How to use
|
||||||
|
|
||||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
|
||||||
|
|
||||||
## Getting started
|
## Issues
|
||||||
|
|
||||||
TODO: List prerequisites and provide or point to information on how to
|
Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/flutter_introduction/pulls) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl).
|
||||||
start using the package.
|
|
||||||
|
|
||||||
## Usage
|
## Want to contribute
|
||||||
|
|
||||||
TODO: Include short and useful examples for package users. Add longer examples
|
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).
|
||||||
to `/example` folder.
|
|
||||||
|
|
||||||
```dart
|
## Author
|
||||||
const like = 'sample';
|
|
||||||
```
|
|
||||||
|
|
||||||
## Additional information
|
This `flutter_introduction` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
|
||||||
|
|
||||||
TODO: Tell users more about the package: where to find more information, how to
|
|
||||||
contribute to the package, how to file issues, what response they can expect
|
|
||||||
from the package authors, and more.
|
|
|
@ -1,542 +0,0 @@
|
||||||
# Generated by pub
|
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
|
||||||
packages:
|
|
||||||
_fe_analyzer_shared:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: _fe_analyzer_shared
|
|
||||||
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "61.0.0"
|
|
||||||
analyzer:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: analyzer
|
|
||||||
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.13.0"
|
|
||||||
args:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: args
|
|
||||||
sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.1"
|
|
||||||
async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: async
|
|
||||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.11.0"
|
|
||||||
boolean_selector:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: boolean_selector
|
|
||||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.1"
|
|
||||||
build:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: build
|
|
||||||
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.1"
|
|
||||||
built_collection:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: built_collection
|
|
||||||
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.1.1"
|
|
||||||
built_value:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: built_value
|
|
||||||
sha256: d7a9cd57c215bdf8d502772447aa6b52a8ab3f956d25d5fdea6ef1df2d2dad60
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "8.4.1"
|
|
||||||
characters:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: characters
|
|
||||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
clock:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: clock
|
|
||||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.1"
|
|
||||||
code_builder:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: code_builder
|
|
||||||
sha256: "02ce3596b459c666530f045ad6f96209474e8fee6e4855940a3cee65fb872ec5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.3.0"
|
|
||||||
collection:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: collection
|
|
||||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.17.1"
|
|
||||||
convert:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: convert
|
|
||||||
sha256: "196284f26f69444b7f5c50692b55ec25da86d9e500451dc09333bf2e3ad69259"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.2"
|
|
||||||
crypto:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: crypto
|
|
||||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.2"
|
|
||||||
cupertino_icons:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: cupertino_icons
|
|
||||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.5"
|
|
||||||
dart_style:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dart_style
|
|
||||||
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.4"
|
|
||||||
fake_async:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fake_async
|
|
||||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.1"
|
|
||||||
ffi:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: ffi
|
|
||||||
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.2"
|
|
||||||
file:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file
|
|
||||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.1.4"
|
|
||||||
fixnum:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fixnum
|
|
||||||
sha256: "04be3e934c52e082558cc9ee21f42f5c1cd7a1262f4c63cd0357c08d5bba81ec"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
flutter:
|
|
||||||
dependency: "direct main"
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
flutter_data_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: "1.0.0"
|
|
||||||
resolved-ref: "500ed1d08095b33387ae3aa4ed1a2ad4d2fb2ac3"
|
|
||||||
url: "https://github.com/Iconica-Development/flutter_data_interface.git"
|
|
||||||
source: git
|
|
||||||
version: "1.0.0"
|
|
||||||
flutter_introduction:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
path: ".."
|
|
||||||
relative: true
|
|
||||||
source: path
|
|
||||||
version: "1.0.0"
|
|
||||||
flutter_introduction_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: "1.0.0"
|
|
||||||
resolved-ref: "2bb986c60a4ce7370a46c5db4cc3bc82a7f96884"
|
|
||||||
url: "https://github.com/Iconica-Development/flutter_introduction_interface.git"
|
|
||||||
source: git
|
|
||||||
version: "1.0.0"
|
|
||||||
flutter_introduction_service:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: "1.0.0"
|
|
||||||
resolved-ref: d8af4b73f1c951dd5fb72d24b07d854ee64a7ee1
|
|
||||||
url: "https://github.com/Iconica-Development/flutter_introduction_service.git"
|
|
||||||
source: git
|
|
||||||
version: "1.0.0"
|
|
||||||
flutter_introduction_shared_preferences:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: "1.0.0"
|
|
||||||
resolved-ref: fd976b68e0b44bc6fec7d6570f1e410a98ae3d61
|
|
||||||
url: "https://github.com/Iconica-Development/flutter_introduction_shared_preferences.git"
|
|
||||||
source: git
|
|
||||||
version: "1.0.0"
|
|
||||||
flutter_introduction_widget:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
path: "."
|
|
||||||
ref: "3.0.0"
|
|
||||||
resolved-ref: ae72ec10ea33eea5afbe62913992bf5215b4ad78
|
|
||||||
url: "https://github.com/Iconica-Development/flutter_introduction_widget.git"
|
|
||||||
source: git
|
|
||||||
version: "3.0.0"
|
|
||||||
flutter_lints:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description:
|
|
||||||
name: flutter_lints
|
|
||||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
flutter_test:
|
|
||||||
dependency: "direct dev"
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
flutter_web_plugins:
|
|
||||||
dependency: transitive
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
glob:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: glob
|
|
||||||
sha256: c51b4fdfee4d281f49b8c957f1add91b815473597f76bcf07377987f66a55729
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
js:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: js
|
|
||||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.6.7"
|
|
||||||
lints:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: lints
|
|
||||||
sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.0"
|
|
||||||
logging:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: logging
|
|
||||||
sha256: c0bbfe94d46aedf9b8b3e695cf3bd48c8e14b35e3b2c639e0aa7755d589ba946
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
matcher:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: matcher
|
|
||||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.12.15"
|
|
||||||
material_color_utilities:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: material_color_utilities
|
|
||||||
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.0"
|
|
||||||
meta:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: meta
|
|
||||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.9.1"
|
|
||||||
mockito:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: mockito
|
|
||||||
sha256: "8b46d7eb40abdda92d62edd01546051f0c27365e65608c284de336dccfef88cc"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.4.1"
|
|
||||||
package_config:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_config
|
|
||||||
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
path:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path
|
|
||||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.8.3"
|
|
||||||
path_provider_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_linux
|
|
||||||
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.11"
|
|
||||||
path_provider_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_platform_interface
|
|
||||||
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.6"
|
|
||||||
path_provider_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_windows
|
|
||||||
sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.6"
|
|
||||||
platform:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: platform
|
|
||||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0"
|
|
||||||
plugin_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: plugin_platform_interface
|
|
||||||
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.4"
|
|
||||||
process:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: process
|
|
||||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.2.4"
|
|
||||||
pub_semver:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pub_semver
|
|
||||||
sha256: "816c1a640e952d213ddd223b3e7aafae08cd9f8e1f6864eed304cc13b0272b07"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.1"
|
|
||||||
shared_preferences:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences
|
|
||||||
sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.1"
|
|
||||||
shared_preferences_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_android
|
|
||||||
sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.4"
|
|
||||||
shared_preferences_foundation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_foundation
|
|
||||||
sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.2"
|
|
||||||
shared_preferences_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_linux
|
|
||||||
sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
shared_preferences_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_platform_interface
|
|
||||||
sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
shared_preferences_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_web
|
|
||||||
sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
shared_preferences_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_windows
|
|
||||||
sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
|
||||||
sky_engine:
|
|
||||||
dependency: transitive
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.99"
|
|
||||||
source_gen:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: source_gen
|
|
||||||
sha256: "85f8c7d6425dff95475db618404732f034c87fe23efe05478cea50520a2517a3"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.5"
|
|
||||||
source_span:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: source_span
|
|
||||||
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.9.1"
|
|
||||||
stack_trace:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: stack_trace
|
|
||||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.11.0"
|
|
||||||
stream_channel:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: stream_channel
|
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.1"
|
|
||||||
string_scanner:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: string_scanner
|
|
||||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.0"
|
|
||||||
term_glyph:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: term_glyph
|
|
||||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.1"
|
|
||||||
test_api:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: test_api
|
|
||||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.5.1"
|
|
||||||
typed_data:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: typed_data
|
|
||||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.1"
|
|
||||||
vector_math:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: vector_math
|
|
||||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.4"
|
|
||||||
watcher:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: watcher
|
|
||||||
sha256: e42dfcc48f67618344da967b10f62de57e04bae01d9d3af4c2596f3712a88c99
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
win32:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: win32
|
|
||||||
sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.4"
|
|
||||||
xdg_directories:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: xdg_directories
|
|
||||||
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.0"
|
|
||||||
yaml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: yaml
|
|
||||||
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.1"
|
|
||||||
sdks:
|
|
||||||
dart: ">=3.0.0-0 <4.0.0"
|
|
||||||
flutter: ">=3.3.0"
|
|
BIN
flutter_introduction_widget.gif
Normal file
BIN
flutter_introduction_widget.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 MiB |
39
melos.yaml
Normal file
39
melos.yaml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
name: flutter_introduction
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- packages/**
|
||||||
|
|
||||||
|
command:
|
||||||
|
version:
|
||||||
|
branch: master
|
||||||
|
|
||||||
|
scripts:
|
||||||
|
lint:all:
|
||||||
|
run: dart run melos run analyze && dart run melos run format-check
|
||||||
|
description: Run all static analysis checks.
|
||||||
|
|
||||||
|
get:
|
||||||
|
run: |
|
||||||
|
melos exec -c 1 -- "flutter pub get"
|
||||||
|
melos exec --scope="*example*" -c 1 -- "flutter pub get"
|
||||||
|
|
||||||
|
upgrade:
|
||||||
|
run: melos exec -c 1 -- "flutter pub upgrade"
|
||||||
|
|
||||||
|
create:
|
||||||
|
# run create in the example folder of flutter_introduction, flutter_introduction_firebase
|
||||||
|
run: melos exec --scope="*example*" -c 1 -- "flutter create ."
|
||||||
|
|
||||||
|
analyze:
|
||||||
|
run: |
|
||||||
|
dart run melos exec -c 1 -- \
|
||||||
|
flutter analyze --fatal-infos
|
||||||
|
description: Run `flutter analyze` for all packages.
|
||||||
|
|
||||||
|
format:
|
||||||
|
run: dart run melos exec dart format .
|
||||||
|
description: Run `dart format` for all packages.
|
||||||
|
|
||||||
|
format-check:
|
||||||
|
run: dart run melos exec dart format . --set-exit-if-changed
|
||||||
|
description: Run `dart format` checks for all packages.
|
41
packages/flutter_introduction/.gitignore
vendored
Normal file
41
packages/flutter_introduction/.gitignore
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
# /pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.flutter-plugins
|
||||||
|
.metadata
|
||||||
|
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
pubspec_overrides.yaml
|
9
packages/flutter_introduction/analysis_options.yaml
Normal file
9
packages/flutter_introduction/analysis_options.yaml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
|
# Possible to overwrite the rules from the package
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
27
packages/flutter_introduction/pubspec.yaml
Normal file
27
packages/flutter_introduction/pubspec.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
name: flutter_introduction
|
||||||
|
description: Combined Package of Flutter Introduction Widget and Flutter Introduction Service
|
||||||
|
version: 1.0.0
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.18.0 <3.0.0"
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_introduction_widget:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_introduction_widget.git
|
||||||
|
ref: 3.0.0
|
||||||
|
flutter_introduction_service:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_introduction_service.git
|
||||||
|
ref: 1.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
41
packages/flutter_introduction_firebase/.gitignore
vendored
Normal file
41
packages/flutter_introduction_firebase/.gitignore
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
# /pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.flutter-plugins
|
||||||
|
.metadata
|
||||||
|
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
pubspec_overrides.yaml
|
0
packages/flutter_introduction_firebase/README.md
Normal file
0
packages/flutter_introduction_firebase/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
|
# Possible to overwrite the rules from the package
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
|
@ -0,0 +1,9 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
///
|
||||||
|
library flutter_introduction_firebase;
|
||||||
|
|
||||||
|
export 'src/firebase_service.dart';
|
||||||
|
export 'src/introduction_widget.dart';
|
|
@ -0,0 +1,70 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_introduction_firebase/src/introduction_page.dart';
|
||||||
|
|
||||||
|
const _introductionDocumentRef = 'introduction/introduction';
|
||||||
|
|
||||||
|
class FirebaseIntroductionService {
|
||||||
|
FirebaseIntroductionService({
|
||||||
|
DocumentReference<Map<String, dynamic>>? documentRef,
|
||||||
|
}) : _documentRef = documentRef ??
|
||||||
|
FirebaseFirestore.instance.doc(_introductionDocumentRef);
|
||||||
|
|
||||||
|
final DocumentReference<Map<String, dynamic>> _documentRef;
|
||||||
|
List<IntroductionPageData> _pages = [];
|
||||||
|
|
||||||
|
Future<List<IntroductionPageData>> getIntroductionPages() async {
|
||||||
|
if (_pages.isNotEmpty) return _pages;
|
||||||
|
var pagesDocuments =
|
||||||
|
await _documentRef.collection('pages').orderBy('order').get();
|
||||||
|
return _pages = pagesDocuments.docs.map((document) {
|
||||||
|
var data = document.data();
|
||||||
|
// convert Map<String, dynamic> to Map<String, String>
|
||||||
|
var title = data['title'] != null
|
||||||
|
? (data['title'] as Map<String, dynamic>).cast<String, String>()
|
||||||
|
: <String, String>{};
|
||||||
|
var content = data['content'] != null
|
||||||
|
? (data['content'] as Map<String, dynamic>).cast<String, String>()
|
||||||
|
: <String, String>{};
|
||||||
|
return IntroductionPageData(
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
contentImage: data['image'] as String?,
|
||||||
|
backgroundImage: data['background_image'] as String?,
|
||||||
|
// the color is stored as a hex string
|
||||||
|
backgroundColor: data['background_color'] != null
|
||||||
|
? Color(int.parse(data['background_color'] as String, radix: 16))
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> shouldAlwaysShowIntroduction() async {
|
||||||
|
var document = await _documentRef.get();
|
||||||
|
return document.data()!['always_show'] as bool? ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadIntroductionPages(
|
||||||
|
BuildContext context,
|
||||||
|
) async {
|
||||||
|
for (var page in _pages) {
|
||||||
|
if (context.mounted && page.backgroundImage != null) {
|
||||||
|
await precacheImage(
|
||||||
|
CachedNetworkImageProvider(page.backgroundImage!),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (context.mounted && page.contentImage != null) {
|
||||||
|
await precacheImage(
|
||||||
|
CachedNetworkImageProvider(page.contentImage!),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
|
class IntroductionPageData {
|
||||||
|
const IntroductionPageData({
|
||||||
|
required this.title,
|
||||||
|
required this.content,
|
||||||
|
this.contentImage,
|
||||||
|
this.backgroundImage,
|
||||||
|
this.backgroundColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The title of the introduction page in different languages
|
||||||
|
final Map<String, String> title;
|
||||||
|
|
||||||
|
/// The content of the introduction page in different languages
|
||||||
|
final Map<String, String> content;
|
||||||
|
|
||||||
|
/// The imageUrl of the graphic on the introduction page
|
||||||
|
final String? contentImage;
|
||||||
|
|
||||||
|
/// The imageUrl of the background image of the introduction page
|
||||||
|
final String? backgroundImage;
|
||||||
|
|
||||||
|
/// Optional background color of the introduction page
|
||||||
|
/// (defaults to transparent)
|
||||||
|
final Color? backgroundColor;
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// ignore_for_file: discarded_futures
|
||||||
|
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_introduction_firebase/flutter_introduction_firebase.dart';
|
||||||
|
import 'package:flutter_introduction_service/flutter_introduction_service.dart';
|
||||||
|
|
||||||
|
export 'package:flutter_introduction_firebase/src/introduction_page.dart';
|
||||||
|
export 'package:flutter_introduction_widget/flutter_introduction_widget.dart';
|
||||||
|
|
||||||
|
class IntroductionFirebase extends StatefulWidget {
|
||||||
|
const IntroductionFirebase({
|
||||||
|
required this.options,
|
||||||
|
required this.onComplete,
|
||||||
|
this.decoration,
|
||||||
|
this.layoutStyle,
|
||||||
|
this.titleBuilder,
|
||||||
|
this.contentBuilder,
|
||||||
|
this.imageBuilder,
|
||||||
|
this.onSkip,
|
||||||
|
this.firebaseService,
|
||||||
|
this.introductionService,
|
||||||
|
this.physics,
|
||||||
|
this.child,
|
||||||
|
this.languageCodeOverride,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The options used to build the introduction screen
|
||||||
|
final IntroductionOptions options;
|
||||||
|
|
||||||
|
/// The service used to determine if the introduction screen should be shown
|
||||||
|
final IntroductionService? introductionService;
|
||||||
|
|
||||||
|
/// The service used to get the introduction pages
|
||||||
|
final FirebaseIntroductionService? firebaseService;
|
||||||
|
|
||||||
|
/// A function called when the introductionSceen changes
|
||||||
|
final VoidCallback onComplete;
|
||||||
|
|
||||||
|
/// A function called when the introductionScreen is skipped
|
||||||
|
final VoidCallback? onSkip;
|
||||||
|
|
||||||
|
/// How the single child scroll view should respond to scrolling
|
||||||
|
final ScrollPhysics? physics;
|
||||||
|
|
||||||
|
/// The widget to show when the introduction screen is loading
|
||||||
|
final Widget? child;
|
||||||
|
|
||||||
|
/// The decoration of an introduction page if it doesn't have
|
||||||
|
/// a backgroundImage or backgroundColor
|
||||||
|
final BoxDecoration? decoration;
|
||||||
|
|
||||||
|
/// The layout style of all the introduction pages
|
||||||
|
final IntroductionLayoutStyle? layoutStyle;
|
||||||
|
|
||||||
|
/// The builder used to build the title of the introduction page
|
||||||
|
final Widget Function(String)? titleBuilder;
|
||||||
|
|
||||||
|
/// The builder used to build the content of the introduction page
|
||||||
|
final Widget Function(String)? contentBuilder;
|
||||||
|
|
||||||
|
/// The builder used to build the image of the introduction page
|
||||||
|
final Widget Function(String)? imageBuilder;
|
||||||
|
|
||||||
|
/// Use this to override the language code that is in the context
|
||||||
|
/// used for showing the introduction in a different language
|
||||||
|
final String? languageCodeOverride;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<IntroductionFirebase> createState() => _IntroductionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IntroductionState extends State<IntroductionFirebase> {
|
||||||
|
late IntroductionService _service;
|
||||||
|
late FirebaseIntroductionService _firebaseService;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (widget.introductionService == null) {
|
||||||
|
_service = IntroductionService();
|
||||||
|
} else {
|
||||||
|
_service = widget.introductionService!;
|
||||||
|
}
|
||||||
|
if (widget.firebaseService == null) {
|
||||||
|
_firebaseService = FirebaseIntroductionService();
|
||||||
|
} else {
|
||||||
|
_firebaseService = widget.firebaseService!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Future<bool> shouldShow() async =>
|
||||||
|
await _service.shouldShow() ||
|
||||||
|
await _firebaseService.shouldAlwaysShowIntroduction();
|
||||||
|
var languageCode = widget.languageCodeOverride ??
|
||||||
|
Localizations.localeOf(context).languageCode;
|
||||||
|
return FutureBuilder(
|
||||||
|
future: shouldShow(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData && snapshot.data != null && snapshot.data!) {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: _firebaseService.getIntroductionPages(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData &&
|
||||||
|
snapshot.data != null &&
|
||||||
|
snapshot.data is List<IntroductionPageData>) {
|
||||||
|
return IntroductionScreen(
|
||||||
|
options: widget.options.copyWith(
|
||||||
|
pages: snapshot.data?.map(
|
||||||
|
(e) {
|
||||||
|
var title = e.title.isEmpty
|
||||||
|
? ''
|
||||||
|
: e.title.containsKey(languageCode)
|
||||||
|
? e.title[languageCode]!
|
||||||
|
: e.title.values.first;
|
||||||
|
var content = e.content.isEmpty
|
||||||
|
? ''
|
||||||
|
: e.content.containsKey(languageCode)
|
||||||
|
? e.content[languageCode]!
|
||||||
|
: e.content.values.first;
|
||||||
|
return IntroductionPage(
|
||||||
|
title:
|
||||||
|
widget.titleBuilder?.call(title) ?? Text(title),
|
||||||
|
graphic: e.contentImage != null &&
|
||||||
|
e.contentImage!.isNotEmpty
|
||||||
|
? widget.imageBuilder?.call(e.contentImage!) ??
|
||||||
|
CachedNetworkImage(imageUrl: e.contentImage!)
|
||||||
|
: null,
|
||||||
|
text: widget.contentBuilder?.call(content) ??
|
||||||
|
Text(content),
|
||||||
|
decoration: widget.decoration?.copyWith(
|
||||||
|
color: e.backgroundColor,
|
||||||
|
image: e.backgroundImage != null &&
|
||||||
|
e.backgroundImage!.isNotEmpty
|
||||||
|
? DecorationImage(
|
||||||
|
image: CachedNetworkImageProvider(
|
||||||
|
e.backgroundImage!,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
layoutStyle: widget.layoutStyle,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
onComplete: () async {
|
||||||
|
await _service.onComplete();
|
||||||
|
widget.onComplete();
|
||||||
|
},
|
||||||
|
physics: widget.physics,
|
||||||
|
onSkip: () async {
|
||||||
|
await _service.onSkip();
|
||||||
|
widget.onComplete();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return widget.child ?? const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (snapshot.hasData && snapshot.data != null && !snapshot.data!) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
await _service.onComplete();
|
||||||
|
widget.onComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return widget.child ?? const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
32
packages/flutter_introduction_firebase/pubspec.yaml
Normal file
32
packages/flutter_introduction_firebase/pubspec.yaml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: flutter_introduction_firebase
|
||||||
|
description: Flutter Introduction Page that uses firebase for the pages and some settings
|
||||||
|
version: 1.0.0
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=3.1.5 <4.0.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
cloud_firestore: ^4.12.2
|
||||||
|
cached_network_image: ^3.3.0
|
||||||
|
|
||||||
|
flutter_introduction_widget:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_introduction_widget
|
||||||
|
ref: 4.1.0
|
||||||
|
flutter_introduction_service:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_introduction_service
|
||||||
|
ref: 1.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 6.0.0
|
||||||
|
|
||||||
|
flutter:
|
|
@ -0,0 +1,11 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('test', () {
|
||||||
|
expect(true, true);
|
||||||
|
});
|
||||||
|
}
|
32
packages/flutter_introduction_interface/.gitignore
vendored
Normal file
32
packages/flutter_introduction_interface/.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
|
||||||
|
.metadata
|
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
|
@ -0,0 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
export './src/introduction_interface.dart';
|
||||||
|
export './src/local_introduction.dart';
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter_data_interface/flutter_data_interface.dart';
|
||||||
|
import 'package:flutter_introduction_interface/src/local_introduction.dart';
|
||||||
|
|
||||||
|
abstract class IntroductionInterface extends DataInterface {
|
||||||
|
IntroductionInterface() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
static IntroductionInterface _instance = LocalIntroductionDataProvider();
|
||||||
|
|
||||||
|
static IntroductionInterface get instance => _instance;
|
||||||
|
|
||||||
|
static set instance(IntroductionInterface instance) {
|
||||||
|
DataInterface.verify(instance, _token);
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setCompleted([bool value = true]);
|
||||||
|
|
||||||
|
Future<bool> shouldShow();
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter_introduction_interface/src/introduction_interface.dart';
|
||||||
|
|
||||||
|
class LocalIntroductionDataProvider extends IntroductionInterface {
|
||||||
|
LocalIntroductionDataProvider();
|
||||||
|
|
||||||
|
bool hasViewed = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setCompleted([bool value = true]) async {
|
||||||
|
hasViewed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldShow() async {
|
||||||
|
return hasViewed;
|
||||||
|
}
|
||||||
|
}
|
23
packages/flutter_introduction_interface/pubspec.yaml
Normal file
23
packages/flutter_introduction_interface/pubspec.yaml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: flutter_introduction_interface
|
||||||
|
description: A new Flutter package project.
|
||||||
|
version: 1.0.0
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_data_interface:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_data_interface.git
|
||||||
|
ref: 1.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
32
packages/flutter_introduction_service/.gitignore
vendored
Normal file
32
packages/flutter_introduction_service/.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
|
||||||
|
.metadata
|
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
|
@ -0,0 +1,8 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
library flutter_introduction_service;
|
||||||
|
|
||||||
|
export './src/introduction_service.dart';
|
||||||
|
export 'package:flutter_introduction_interface/flutter_introduction_interface.dart';
|
|
@ -0,0 +1,24 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter_introduction_interface/flutter_introduction_interface.dart';
|
||||||
|
|
||||||
|
class IntroductionService {
|
||||||
|
IntroductionService([IntroductionInterface? dataProvider])
|
||||||
|
: _dataProvider = dataProvider ?? LocalIntroductionDataProvider();
|
||||||
|
|
||||||
|
late final IntroductionInterface _dataProvider;
|
||||||
|
|
||||||
|
Future<void> onSkip() {
|
||||||
|
return _dataProvider.setCompleted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onComplete() {
|
||||||
|
return _dataProvider.setCompleted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> shouldShow() {
|
||||||
|
return _dataProvider.shouldShow();
|
||||||
|
}
|
||||||
|
}
|
23
packages/flutter_introduction_service/pubspec.yaml
Normal file
23
packages/flutter_introduction_service/pubspec.yaml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: flutter_introduction_service
|
||||||
|
description: A new Flutter package project.
|
||||||
|
version: 1.0.0
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_introduction_interface:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_introduction_interface.git
|
||||||
|
ref: 1.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
34
packages/flutter_introduction_shared_preferences/.gitignore
vendored
Normal file
34
packages/flutter_introduction_shared_preferences/.gitignore
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
|
||||||
|
.metadata
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
|
@ -0,0 +1,35 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
library flutter_introduction_shared_preferences;
|
||||||
|
|
||||||
|
import 'package:flutter_introduction_interface/flutter_introduction_interface.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class SharedPreferencesIntroductionDataProvider extends IntroductionInterface {
|
||||||
|
SharedPreferencesIntroductionDataProvider();
|
||||||
|
|
||||||
|
SharedPreferences? _prefs;
|
||||||
|
String key = '_completedIntroduction';
|
||||||
|
|
||||||
|
_writeKeyValue(String key, bool value) async {
|
||||||
|
_prefs!.setBool(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_init() async {
|
||||||
|
_prefs ??= await SharedPreferences.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setCompleted([bool value = true]) async {
|
||||||
|
await _init();
|
||||||
|
_writeKeyValue(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> shouldShow() async {
|
||||||
|
await _init();
|
||||||
|
return !(_prefs!.getBool(key) ?? false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: flutter_introduction_shared_preferences
|
||||||
|
description: A new Flutter package project.
|
||||||
|
version: 1.0.0
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_introduction_interface:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_introduction_interface.git
|
||||||
|
ref: 1.0.0
|
||||||
|
shared_preferences: any
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
43
packages/flutter_introduction_widget/.gitignore
vendored
Normal file
43
packages/flutter_introduction_widget/.gitignore
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
|
||||||
|
example/android/
|
||||||
|
example/ios/
|
||||||
|
example/macos/
|
||||||
|
example/web/
|
||||||
|
example/windows/
|
||||||
|
example/linux/
|
||||||
|
example/.metadata
|
||||||
|
example/pubspec.lock
|
||||||
|
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.metadata
|
65
packages/flutter_introduction_widget/README.md
Normal file
65
packages/flutter_introduction_widget/README.md
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
[](https://github.com/Iconica-Development) [](https://github.com/Iconica-Development/flutter_introduction_widget/actions/new) [](https://github.com/tenhobi/effective_dart)
|
||||||
|
|
||||||
|
# Introduction Widget
|
||||||
|
Flutter Introduction Widget for showing a list of introduction pages on a single scrollable page or horizontal pageview.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
To use this package, add `flutter_introduction_widget` as a dependency in your pubspec.yaml file.
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
Simple way to use the introduction widget:
|
||||||
|
```dart
|
||||||
|
IntroductionScreen(
|
||||||
|
options: IntroductionOptions(
|
||||||
|
pages: [
|
||||||
|
IntroductionPage(
|
||||||
|
title: const Text('First page'),
|
||||||
|
text: const Text('Wow a page'),
|
||||||
|
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(),
|
||||||
|
backgroundImage: const AssetImage(
|
||||||
|
'assets/flutter_introduction_background.jpeg'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
introductionTranslations: const IntroductionTranslations(
|
||||||
|
skipButton: 'Skip it!',
|
||||||
|
nextButton: 'Next',
|
||||||
|
previousButton: 'Previous',
|
||||||
|
finishButton: 'Finish',
|
||||||
|
),
|
||||||
|
buttonMode: IntroductionScreenButtonMode.text,
|
||||||
|
buttonBuilder: (context, onPressed, child) =>
|
||||||
|
ElevatedButton(onPressed: onPressed, child: child),
|
||||||
|
),
|
||||||
|
onComplete: () {
|
||||||
|
debugPrint('We completed the cycle');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [Example Code](example/lib/main.dart) for an example on how to use this package.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/flutter_introduction_widget) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl).
|
||||||
|
|
||||||
|
## 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_widget/pulls).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
This `flutter_introduction_widget` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
|
15
packages/flutter_introduction_widget/analysis_options.yaml
Normal file
15
packages/flutter_introduction_widget/analysis_options.yaml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
include: package:flutter_lints/flutter.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
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
44
packages/flutter_introduction_widget/example/.gitignore
vendored
Normal file
44
packages/flutter_introduction_widget/example/.gitignore
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
0
packages/flutter_introduction_widget/example/README.md
Normal file
0
packages/flutter_introduction_widget/example/README.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# 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,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
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:
|
||||||
|
# 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
|
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
116
packages/flutter_introduction_widget/example/lib/main.dart
Normal file
116
packages/flutter_introduction_widget/example/lib/main.dart
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_introduction_widget/flutter_introduction_widget.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
// This widget is the root of your application.
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'Flutter Demo',
|
||||||
|
theme: ThemeData(
|
||||||
|
// This is the theme of your application.
|
||||||
|
//
|
||||||
|
// Try running your application with "flutter run". You'll see the
|
||||||
|
// application has a blue toolbar. Then, without quitting the app, try
|
||||||
|
// changing the primarySwatch below to Colors.green and then invoke
|
||||||
|
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||||
|
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||||
|
// Notice that the counter didn't reset back to zero; the application
|
||||||
|
// is not restarted.
|
||||||
|
primarySwatch: Colors.blue,
|
||||||
|
),
|
||||||
|
home: IntroductionScreen(
|
||||||
|
options: IntroductionOptions(
|
||||||
|
pages: [
|
||||||
|
IntroductionPage(
|
||||||
|
title: const Text('Basic Page'),
|
||||||
|
text: const Text(
|
||||||
|
'A page with some text and a widget in the middle.',
|
||||||
|
),
|
||||||
|
graphic: const FlutterLogo(size: 100),
|
||||||
|
),
|
||||||
|
IntroductionPage(
|
||||||
|
title: const Text('Layout Shift'),
|
||||||
|
text: const Text(
|
||||||
|
'You can change the layout of a page to mix things up.',
|
||||||
|
),
|
||||||
|
graphic: const FlutterLogo(size: 100),
|
||||||
|
layoutStyle: IntroductionLayoutStyle.imageTop,
|
||||||
|
),
|
||||||
|
IntroductionPage(
|
||||||
|
title: const Text(
|
||||||
|
'Decoration',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topRight,
|
||||||
|
end: Alignment.bottomLeft,
|
||||||
|
colors: [
|
||||||
|
Colors.yellow,
|
||||||
|
Colors.red,
|
||||||
|
Colors.indigo,
|
||||||
|
Colors.teal,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
text: const Text(
|
||||||
|
'Add a Decoration to make a custom background, like a LinearGradient',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
graphic: const FlutterLogo(
|
||||||
|
size: 100,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IntroductionPage(
|
||||||
|
title: const Text(
|
||||||
|
'Background Image',
|
||||||
|
),
|
||||||
|
text: const Text(
|
||||||
|
'Add a Decoration with a DecorationImage, to add an background image',
|
||||||
|
),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
image: AssetImage(
|
||||||
|
'assets/flutter_introduction_background.jpeg',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
introductionTranslations: const IntroductionTranslations(
|
||||||
|
skipButton: 'Skip it!',
|
||||||
|
nextButton: 'Next',
|
||||||
|
previousButton: 'Previous',
|
||||||
|
finishButton: 'Finish',
|
||||||
|
),
|
||||||
|
tapEnabled: true,
|
||||||
|
displayMode: IntroductionDisplayMode.multiPageHorizontal,
|
||||||
|
buttonMode: IntroductionScreenButtonMode.text,
|
||||||
|
indicatorMode: IndicatorMode.dash,
|
||||||
|
skippable: true,
|
||||||
|
buttonBuilder: (context, onPressed, child, buttonType) =>
|
||||||
|
ElevatedButton(onPressed: onPressed, child: child),
|
||||||
|
),
|
||||||
|
onComplete: () {
|
||||||
|
debugPrint('We completed the cycle');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
60
packages/flutter_introduction_widget/example/pubspec.yaml
Normal file
60
packages/flutter_introduction_widget/example/pubspec.yaml
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
name: example
|
||||||
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.1 <3.0.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_introduction_widget:
|
||||||
|
path: ../
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
|
||||||
|
# The following line ensures that the Material Icons font is
|
||||||
|
# included with your application, so that you can use the icons in
|
||||||
|
# the material Icons class.
|
||||||
|
uses-material-design: true
|
||||||
|
|
||||||
|
# To add assets to your application, add an assets section, like this:
|
||||||
|
assets:
|
||||||
|
- assets/
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
|
# For details regarding adding assets from package dependencies, see
|
||||||
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
|
||||||
|
# To add custom fonts to your application, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts from package dependencies,
|
||||||
|
# see https://flutter.dev/custom-fonts/#from-packages
|
|
@ -0,0 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
export 'src/config/introduction.dart';
|
||||||
|
export 'src/introduction.dart';
|
|
@ -0,0 +1,209 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum IntroductionScreenMode { showNever, showAlways, showOnce }
|
||||||
|
|
||||||
|
enum IntroductionScreenButtonMode { text, icon, disabled, singleFinish }
|
||||||
|
|
||||||
|
enum IntroductionLayoutStyle {
|
||||||
|
imageCenter,
|
||||||
|
imageTop,
|
||||||
|
imageBottom,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IndicatorMode { dot, dash, custom }
|
||||||
|
|
||||||
|
enum IntroductionDisplayMode {
|
||||||
|
singleScrollablePageVertical,
|
||||||
|
multiPageHorizontal
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IntroductionControlMode {
|
||||||
|
previousNextButton,
|
||||||
|
singleButton,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum IntroductionButtonType {
|
||||||
|
next,
|
||||||
|
previous,
|
||||||
|
finish,
|
||||||
|
skip,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
/// each page.
|
||||||
|
///
|
||||||
|
/// The values for [title] and [text] are optional in this, but will use a
|
||||||
|
/// default translation key when built.
|
||||||
|
///
|
||||||
|
/// The [background] is fully optional and if not provided will show the
|
||||||
|
/// [ThemeData.colorScheme.background] as default.
|
||||||
|
IntroductionPage({
|
||||||
|
this.title,
|
||||||
|
this.text,
|
||||||
|
this.graphic,
|
||||||
|
this.decoration,
|
||||||
|
this.layoutStyle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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({
|
||||||
|
this.introductionTranslations = const IntroductionTranslations(),
|
||||||
|
this.indicatorMode = IndicatorMode.dash,
|
||||||
|
this.indicatorBuilder,
|
||||||
|
this.layoutStyle = IntroductionLayoutStyle.imageCenter,
|
||||||
|
this.pages = const [],
|
||||||
|
this.buttonMode = IntroductionScreenButtonMode.disabled,
|
||||||
|
this.tapEnabled = false,
|
||||||
|
this.mode = IntroductionScreenMode.showNever,
|
||||||
|
this.textAlign = TextAlign.center,
|
||||||
|
this.displayMode = IntroductionDisplayMode.multiPageHorizontal,
|
||||||
|
this.skippable = false,
|
||||||
|
this.buttonBuilder,
|
||||||
|
this.controlMode = IntroductionControlMode.previousNextButton,
|
||||||
|
}) : assert(
|
||||||
|
!(identical(indicatorMode, IndicatorMode.custom) &&
|
||||||
|
indicatorBuilder == null),
|
||||||
|
'When indicator mode is set to custom, '
|
||||||
|
'make sure to define indicatorBuilder',
|
||||||
|
);
|
||||||
|
|
||||||
|
IntroductionOptions copyWith({
|
||||||
|
IntroductionScreenMode? mode,
|
||||||
|
List<IntroductionPage>? pages,
|
||||||
|
bool? tapEnabled,
|
||||||
|
IntroductionScreenButtonMode? buttonMode,
|
||||||
|
IntroductionLayoutStyle? layoutStyle,
|
||||||
|
IndicatorMode? indicatorMode,
|
||||||
|
Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
PageController,
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
)? indicatorBuilder,
|
||||||
|
bool? skippable,
|
||||||
|
TextAlign? textAlign,
|
||||||
|
IntroductionDisplayMode? displayMode,
|
||||||
|
IntroductionControlMode? controlMode,
|
||||||
|
Widget Function(BuildContext, VoidCallback, Widget, IntroductionButtonType)?
|
||||||
|
buttonBuilder,
|
||||||
|
IntroductionTranslations? introductionTranslations,
|
||||||
|
}) {
|
||||||
|
return IntroductionOptions(
|
||||||
|
mode: mode ?? this.mode,
|
||||||
|
pages: pages ?? this.pages,
|
||||||
|
tapEnabled: tapEnabled ?? this.tapEnabled,
|
||||||
|
buttonMode: buttonMode ?? this.buttonMode,
|
||||||
|
layoutStyle: layoutStyle ?? this.layoutStyle,
|
||||||
|
indicatorMode: indicatorMode ?? this.indicatorMode,
|
||||||
|
indicatorBuilder: indicatorBuilder ?? this.indicatorBuilder,
|
||||||
|
skippable: skippable ?? this.skippable,
|
||||||
|
textAlign: textAlign ?? this.textAlign,
|
||||||
|
displayMode: displayMode ?? this.displayMode,
|
||||||
|
controlMode: controlMode ?? this.controlMode,
|
||||||
|
buttonBuilder: buttonBuilder ?? this.buttonBuilder,
|
||||||
|
introductionTranslations:
|
||||||
|
introductionTranslations ?? this.introductionTranslations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
class IntroductionTranslations {
|
||||||
|
final String skipButton;
|
||||||
|
final String nextButton;
|
||||||
|
final String previousButton;
|
||||||
|
final String finishButton;
|
||||||
|
|
||||||
|
const IntroductionTranslations({
|
||||||
|
this.skipButton = 'skip',
|
||||||
|
this.nextButton = 'next',
|
||||||
|
this.previousButton = 'previous',
|
||||||
|
this.finishButton = 'finish',
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_introduction_widget/src/types/page_introduction.dart';
|
||||||
|
import 'package:flutter_introduction_widget/src/types/single_introduction.dart';
|
||||||
|
|
||||||
|
import 'config/introduction.dart';
|
||||||
|
|
||||||
|
const kAnimationDuration = Duration(milliseconds: 300);
|
||||||
|
|
||||||
|
class IntroductionScreen extends StatelessWidget {
|
||||||
|
const IntroductionScreen({
|
||||||
|
Key? key,
|
||||||
|
required this.options,
|
||||||
|
required this.onComplete,
|
||||||
|
this.physics,
|
||||||
|
this.onNext,
|
||||||
|
this.onPrevious,
|
||||||
|
this.onSkip,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
/// The options used to build the introduction screen
|
||||||
|
final IntroductionOptions options;
|
||||||
|
|
||||||
|
/// A function called when the introductionSceen changes
|
||||||
|
final VoidCallback onComplete;
|
||||||
|
|
||||||
|
/// A function called when the introductionScreen is skipped
|
||||||
|
final VoidCallback? onSkip;
|
||||||
|
final ScrollPhysics? physics;
|
||||||
|
|
||||||
|
/// A function called when the introductionScreen moved to the next page
|
||||||
|
/// where the page provided is the page where the user currently moved to
|
||||||
|
final void Function(IntroductionPage)? onNext;
|
||||||
|
|
||||||
|
/// A function called when the introductionScreen moved to the previous page
|
||||||
|
/// where the page provided is the page where the user currently moved to
|
||||||
|
final void Function(IntroductionPage)? onPrevious;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
body: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
switch (options.displayMode) {
|
||||||
|
case IntroductionDisplayMode.multiPageHorizontal:
|
||||||
|
return MultiPageIntroductionScreen(
|
||||||
|
onComplete: onComplete,
|
||||||
|
physics: physics,
|
||||||
|
onSkip: onSkip,
|
||||||
|
onPrevious: onPrevious,
|
||||||
|
onNext: onNext,
|
||||||
|
options: options,
|
||||||
|
);
|
||||||
|
case IntroductionDisplayMode.singleScrollablePageVertical:
|
||||||
|
return SingleIntroductionPage(
|
||||||
|
options: options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,590 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_introduction_widget/src/introduction.dart';
|
||||||
|
|
||||||
|
import '../config/introduction.dart';
|
||||||
|
import '../widgets/background.dart';
|
||||||
|
import '../widgets/indicator.dart';
|
||||||
|
import '../widgets/page_content.dart';
|
||||||
|
|
||||||
|
class MultiPageIntroductionScreen extends StatefulWidget {
|
||||||
|
const MultiPageIntroductionScreen({
|
||||||
|
required this.options,
|
||||||
|
required this.onComplete,
|
||||||
|
this.physics,
|
||||||
|
this.onNext,
|
||||||
|
this.onPrevious,
|
||||||
|
this.onSkip,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final VoidCallback onComplete;
|
||||||
|
final VoidCallback? onSkip;
|
||||||
|
final void Function(IntroductionPage)? onNext;
|
||||||
|
final void Function(IntroductionPage)? onPrevious;
|
||||||
|
final ScrollPhysics? physics;
|
||||||
|
|
||||||
|
final IntroductionOptions options;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MultiPageIntroductionScreen> createState() =>
|
||||||
|
_MultiPageIntroductionScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MultiPageIntroductionScreenState
|
||||||
|
extends State<MultiPageIntroductionScreen> {
|
||||||
|
final PageController _controller = PageController();
|
||||||
|
final ValueNotifier<int> _currentPage = ValueNotifier(0);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller.addListener(_handleScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleScroll() {
|
||||||
|
_currentPage.value = _controller.page?.round() ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.removeListener(_handleScroll);
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var pages = widget.options.pages;
|
||||||
|
var translations = widget.options.introductionTranslations;
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
NotificationListener<ScrollNotification>(
|
||||||
|
onNotification: (notification) {
|
||||||
|
if (notification is OverscrollNotification) {
|
||||||
|
if (notification.overscroll > 8) {
|
||||||
|
widget.onComplete.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add bouncing scroll physics support
|
||||||
|
if (notification is ScrollUpdateNotification) {
|
||||||
|
final offset = notification.metrics.pixels;
|
||||||
|
if (offset > notification.metrics.maxScrollExtent + 8) {
|
||||||
|
widget.onComplete.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: PageView(
|
||||||
|
controller: _controller,
|
||||||
|
physics: widget.physics,
|
||||||
|
children: List.generate(
|
||||||
|
pages.length,
|
||||||
|
(index) {
|
||||||
|
return ExplainerPage(
|
||||||
|
onTap: () {
|
||||||
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
|
},
|
||||||
|
controller: _controller,
|
||||||
|
page: pages[index],
|
||||||
|
options: widget.options,
|
||||||
|
index: index,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SafeArea(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (widget.options.controlMode ==
|
||||||
|
IntroductionControlMode.previousNextButton) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 16.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 64,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _controller,
|
||||||
|
builder: (context, _) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
if (widget.options.skippable &&
|
||||||
|
!_isLast(pages)) ...[
|
||||||
|
TextButton(
|
||||||
|
onPressed: widget.onComplete,
|
||||||
|
child: Text(translations.skipButton),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
const SizedBox.shrink()
|
||||||
|
],
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _currentPage,
|
||||||
|
builder: (context, _) => Indicator(
|
||||||
|
indicatorBuilder: widget.options.indicatorBuilder,
|
||||||
|
mode: widget.options.indicatorMode,
|
||||||
|
controller: _controller,
|
||||||
|
count: pages.length,
|
||||||
|
index: _currentPage.value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _controller,
|
||||||
|
builder: (context, _) {
|
||||||
|
if (widget.options.controlMode ==
|
||||||
|
IntroductionControlMode.singleButton) {
|
||||||
|
return IntroductionOneButton(
|
||||||
|
controller: _controller,
|
||||||
|
next: _isNext(pages),
|
||||||
|
previous: _isPrevious,
|
||||||
|
last: _isLast(pages),
|
||||||
|
options: widget.options,
|
||||||
|
onFinish: widget.onComplete,
|
||||||
|
onNext: () {
|
||||||
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
|
},
|
||||||
|
onPrevious: () {
|
||||||
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IntroductionTwoButtons(
|
||||||
|
controller: _controller,
|
||||||
|
next: _isNext(pages),
|
||||||
|
previous: _isPrevious,
|
||||||
|
last: _isLast(pages),
|
||||||
|
options: widget.options,
|
||||||
|
onFinish: widget.onComplete,
|
||||||
|
onNext: () {
|
||||||
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
|
},
|
||||||
|
onPrevious: () {
|
||||||
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _controller,
|
||||||
|
builder: (context, _) {
|
||||||
|
return IntroductionIconButtons(
|
||||||
|
controller: _controller,
|
||||||
|
next: _isNext(pages),
|
||||||
|
previous: _isPrevious,
|
||||||
|
last: _isLast(pages),
|
||||||
|
options: widget.options,
|
||||||
|
onFinish: widget.onComplete,
|
||||||
|
onNext: () {
|
||||||
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
|
},
|
||||||
|
onPrevious: () {
|
||||||
|
widget.onNext?.call(pages[_currentPage.value]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isLast(List<IntroductionPage> pages) =>
|
||||||
|
_currentPage.value == pages.length - 1;
|
||||||
|
|
||||||
|
bool get _isPrevious => _currentPage.value > 0;
|
||||||
|
|
||||||
|
bool _isNext(List<IntroductionPage> pages) =>
|
||||||
|
_currentPage.value < pages.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExplainerPage extends StatelessWidget {
|
||||||
|
const ExplainerPage({
|
||||||
|
required this.page,
|
||||||
|
required this.options,
|
||||||
|
required this.index,
|
||||||
|
required this.controller,
|
||||||
|
this.onTap,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final IntroductionPage page;
|
||||||
|
final IntroductionOptions options;
|
||||||
|
final PageController controller;
|
||||||
|
final int index;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
return Background(
|
||||||
|
background: page.decoration,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 64,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: IntroductionPageContent(
|
||||||
|
onTap: () {
|
||||||
|
if (options.tapEnabled) {
|
||||||
|
onTap?.call();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: theme.textTheme.displayMedium!,
|
||||||
|
child: page.title ?? Text('introduction.$index.title'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
text: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: theme.textTheme.bodyLarge!,
|
||||||
|
child: page.text ?? Text('introduction.$index.description'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
graphic: Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: page.graphic,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
layoutStyle: page.layoutStyle ?? options.layoutStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 144,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntroductionTwoButtons extends StatelessWidget {
|
||||||
|
const IntroductionTwoButtons({
|
||||||
|
required this.options,
|
||||||
|
required this.controller,
|
||||||
|
required this.next,
|
||||||
|
required this.last,
|
||||||
|
required this.previous,
|
||||||
|
required this.onFinish,
|
||||||
|
required this.onNext,
|
||||||
|
required this.onPrevious,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final IntroductionOptions options;
|
||||||
|
final PageController controller;
|
||||||
|
final VoidCallback? onFinish;
|
||||||
|
final VoidCallback? onNext;
|
||||||
|
final VoidCallback? onPrevious;
|
||||||
|
|
||||||
|
final bool previous;
|
||||||
|
final bool next;
|
||||||
|
final bool last;
|
||||||
|
|
||||||
|
void _previous() {
|
||||||
|
controller.previousPage(
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
onPrevious?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var translations = options.introductionTranslations;
|
||||||
|
var showFinishButton =
|
||||||
|
options.buttonMode == IntroductionScreenButtonMode.singleFinish;
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (options.buttonMode == IntroductionScreenButtonMode.text) ...[
|
||||||
|
if (previous) ...[
|
||||||
|
options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
_previous,
|
||||||
|
Text(translations.previousButton),
|
||||||
|
IntroductionButtonType.previous,
|
||||||
|
) ??
|
||||||
|
TextButton(
|
||||||
|
onPressed: _previous,
|
||||||
|
child: Text(translations.previousButton),
|
||||||
|
),
|
||||||
|
] else
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
if (next) ...[
|
||||||
|
options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
_next,
|
||||||
|
Text(translations.nextButton),
|
||||||
|
IntroductionButtonType.next,
|
||||||
|
) ??
|
||||||
|
TextButton(
|
||||||
|
onPressed: _next,
|
||||||
|
child: Text(translations.nextButton),
|
||||||
|
),
|
||||||
|
] else if (last) ...[
|
||||||
|
options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
() {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
Text(translations.finishButton),
|
||||||
|
IntroductionButtonType.finish,
|
||||||
|
) ??
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
child: Text(translations.finishButton),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
] else if (showFinishButton) ...[
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
Expanded(
|
||||||
|
child: Visibility(
|
||||||
|
visible: last,
|
||||||
|
maintainSize: true,
|
||||||
|
maintainAnimation: true,
|
||||||
|
maintainState: true,
|
||||||
|
maintainInteractivity: false,
|
||||||
|
child: Align(
|
||||||
|
child: options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
() {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
Text(translations.finishButton),
|
||||||
|
IntroductionButtonType.finish,
|
||||||
|
) ??
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
child: Text(translations.finishButton),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_next() {
|
||||||
|
controller.nextPage(
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
onNext?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntroductionOneButton extends StatelessWidget {
|
||||||
|
const IntroductionOneButton({
|
||||||
|
required this.options,
|
||||||
|
required this.controller,
|
||||||
|
required this.next,
|
||||||
|
required this.last,
|
||||||
|
required this.previous,
|
||||||
|
required this.onFinish,
|
||||||
|
required this.onNext,
|
||||||
|
required this.onPrevious,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final IntroductionOptions options;
|
||||||
|
final PageController controller;
|
||||||
|
final VoidCallback? onFinish;
|
||||||
|
final VoidCallback? onNext;
|
||||||
|
final VoidCallback? onPrevious;
|
||||||
|
|
||||||
|
final bool previous;
|
||||||
|
final bool next;
|
||||||
|
final bool last;
|
||||||
|
|
||||||
|
void _previous() {
|
||||||
|
controller.previousPage(
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
onPrevious?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var translations = options.introductionTranslations;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
if (options.buttonMode == IntroductionScreenButtonMode.text) ...[
|
||||||
|
if (last) ...[
|
||||||
|
options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
() {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
Text(translations.finishButton),
|
||||||
|
IntroductionButtonType.finish,
|
||||||
|
) ??
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
child: Text(translations.finishButton),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
_next,
|
||||||
|
Text(translations.nextButton),
|
||||||
|
IntroductionButtonType.next,
|
||||||
|
) ??
|
||||||
|
TextButton(
|
||||||
|
onPressed: _next,
|
||||||
|
child: Text(translations.nextButton),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (previous) ...[
|
||||||
|
options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
_previous,
|
||||||
|
Text(translations.previousButton),
|
||||||
|
IntroductionButtonType.previous,
|
||||||
|
) ??
|
||||||
|
TextButton(
|
||||||
|
onPressed: _previous,
|
||||||
|
child: Text(translations.previousButton),
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
options.buttonBuilder?.call(
|
||||||
|
context,
|
||||||
|
() {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
Text(translations.finishButton),
|
||||||
|
IntroductionButtonType.skip,
|
||||||
|
) ??
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
onFinish?.call();
|
||||||
|
},
|
||||||
|
child: Text(translations.finishButton),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_next() {
|
||||||
|
controller.nextPage(
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
onNext?.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntroductionIconButtons extends StatelessWidget {
|
||||||
|
const IntroductionIconButtons({
|
||||||
|
required this.options,
|
||||||
|
required this.controller,
|
||||||
|
required this.next,
|
||||||
|
required this.last,
|
||||||
|
required this.previous,
|
||||||
|
required this.onFinish,
|
||||||
|
required this.onNext,
|
||||||
|
required this.onPrevious,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final IntroductionOptions options;
|
||||||
|
final PageController controller;
|
||||||
|
final VoidCallback? onFinish;
|
||||||
|
final VoidCallback? onNext;
|
||||||
|
final VoidCallback? onPrevious;
|
||||||
|
|
||||||
|
final bool previous;
|
||||||
|
final bool next;
|
||||||
|
final bool last;
|
||||||
|
|
||||||
|
void _previous() {
|
||||||
|
controller.previousPage(
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
onPrevious?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (options.buttonMode == IntroductionScreenButtonMode.icon) ...[
|
||||||
|
if (previous) ...[
|
||||||
|
IconButton(
|
||||||
|
iconSize: 70,
|
||||||
|
onPressed: _previous,
|
||||||
|
icon: const Icon(Icons.chevron_left),
|
||||||
|
),
|
||||||
|
] else
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
IconButton(
|
||||||
|
iconSize: 70,
|
||||||
|
onPressed: () {
|
||||||
|
if (next) {
|
||||||
|
_next();
|
||||||
|
} else {
|
||||||
|
onFinish?.call();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.chevron_right),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_next() {
|
||||||
|
controller.nextPage(
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
onNext?.call();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
import '../config/introduction.dart';
|
||||||
|
|
||||||
|
class SingleIntroductionPage extends StatelessWidget {
|
||||||
|
const SingleIntroductionPage({
|
||||||
|
Key? key,
|
||||||
|
required this.options,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final IntroductionOptions options;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Background extends StatelessWidget {
|
||||||
|
const Background({
|
||||||
|
this.background,
|
||||||
|
required this.child,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final BoxDecoration? background;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var background = this.background ??
|
||||||
|
BoxDecoration(
|
||||||
|
color: theme.colorScheme.background,
|
||||||
|
);
|
||||||
|
var size = MediaQuery.of(context).size;
|
||||||
|
return Container(
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
decoration: background,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_introduction_widget/src/introduction.dart';
|
||||||
|
|
||||||
|
import '../config/introduction.dart';
|
||||||
|
|
||||||
|
class Indicator extends StatelessWidget {
|
||||||
|
const Indicator({
|
||||||
|
required this.mode,
|
||||||
|
required this.controller,
|
||||||
|
required this.count,
|
||||||
|
required this.index,
|
||||||
|
required this.indicatorBuilder,
|
||||||
|
Key? key,
|
||||||
|
}) : assert(
|
||||||
|
!(mode == IndicatorMode.custom && indicatorBuilder == null),
|
||||||
|
"When a custom indicator is used the indicatorBuilder must be provided",
|
||||||
|
),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final IndicatorMode mode;
|
||||||
|
final PageController controller;
|
||||||
|
final Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
PageController,
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
)? indicatorBuilder;
|
||||||
|
final int index;
|
||||||
|
final int count;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
switch (mode) {
|
||||||
|
case IndicatorMode.custom:
|
||||||
|
return indicatorBuilder!.call(context, controller, index, count);
|
||||||
|
case IndicatorMode.dot:
|
||||||
|
return DotsIndicator(
|
||||||
|
controller: controller,
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
dotcolor: theme.colorScheme.secondary,
|
||||||
|
itemCount: count,
|
||||||
|
onPageSelected: (int page) {
|
||||||
|
controller.animateToPage(
|
||||||
|
page,
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case IndicatorMode.dash:
|
||||||
|
return DashIndicator(
|
||||||
|
controller: controller,
|
||||||
|
selectedColor: theme.colorScheme.primary,
|
||||||
|
itemCount: count,
|
||||||
|
onPageSelected: (int page) {
|
||||||
|
controller.animateToPage(
|
||||||
|
page,
|
||||||
|
duration: kAnimationDuration,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DashIndicator extends AnimatedWidget {
|
||||||
|
final PageController controller;
|
||||||
|
final Color color;
|
||||||
|
final Color selectedColor;
|
||||||
|
final int itemCount;
|
||||||
|
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() {
|
||||||
|
try {
|
||||||
|
return controller.page?.round() ?? 0;
|
||||||
|
} catch (_) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
int index = _getPage();
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
for (int i = 0; i < itemCount; i++) ...[
|
||||||
|
buildDash(i, index == i),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildDash(int index, bool selected) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 20,
|
||||||
|
child: Center(
|
||||||
|
child: Material(
|
||||||
|
color: selected ? color : color.withAlpha(125),
|
||||||
|
type: MaterialType.card,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(2.5),
|
||||||
|
),
|
||||||
|
width: 16,
|
||||||
|
height: 5,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => onPageSelected.call(index),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An indicator showing the currently selected page of a PageController
|
||||||
|
class DotsIndicator extends AnimatedWidget {
|
||||||
|
const DotsIndicator({
|
||||||
|
this.color = Colors.white,
|
||||||
|
required this.controller,
|
||||||
|
this.dotcolor = Colors.green,
|
||||||
|
this.itemCount,
|
||||||
|
this.onPageSelected,
|
||||||
|
Key? key,
|
||||||
|
}) : super(
|
||||||
|
listenable: controller,
|
||||||
|
key: key,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// The PageController that this DotsIndicator is representing.
|
||||||
|
final Color? dotcolor;
|
||||||
|
final PageController controller;
|
||||||
|
|
||||||
|
/// The number of items managed by the PageController
|
||||||
|
final int? itemCount;
|
||||||
|
|
||||||
|
/// Called when a dot is tapped
|
||||||
|
final ValueChanged<int>? onPageSelected;
|
||||||
|
|
||||||
|
/// The color of the dots.
|
||||||
|
///
|
||||||
|
/// Defaults to `Colors.white`.
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
// The base size of the dots
|
||||||
|
static const double _kDotSize = 4.0;
|
||||||
|
|
||||||
|
// The increase in the size of the selected dot
|
||||||
|
static const double _kMaxZoom = 2.0;
|
||||||
|
|
||||||
|
// The distance between the center of each dot
|
||||||
|
static const double _kDotSpacing = 12.0;
|
||||||
|
|
||||||
|
Widget _buildDot(int index) {
|
||||||
|
double selectedness = Curves.easeOut.transform(
|
||||||
|
max(
|
||||||
|
0.0,
|
||||||
|
1.0 -
|
||||||
|
((controller.page ?? controller.initialPage).round() - index).abs(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
|
||||||
|
|
||||||
|
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
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: List<Widget>.generate(itemCount!, _buildDot),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../config/introduction.dart';
|
||||||
|
|
||||||
|
class IntroductionPageContent extends StatelessWidget {
|
||||||
|
const IntroductionPageContent({
|
||||||
|
required this.title,
|
||||||
|
required this.text,
|
||||||
|
required this.graphic,
|
||||||
|
required this.layoutStyle,
|
||||||
|
required this.onTap,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Widget? title;
|
||||||
|
final Widget? text;
|
||||||
|
final Widget? graphic;
|
||||||
|
final IntroductionLayoutStyle layoutStyle;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
if (graphic != null &&
|
||||||
|
layoutStyle == IntroductionLayoutStyle.imageTop)
|
||||||
|
graphic!,
|
||||||
|
if (title != null) title!,
|
||||||
|
if (graphic != null &&
|
||||||
|
layoutStyle == IntroductionLayoutStyle.imageCenter)
|
||||||
|
graphic!,
|
||||||
|
if (text != null) text!,
|
||||||
|
if (graphic != null &&
|
||||||
|
layoutStyle == IntroductionLayoutStyle.imageBottom)
|
||||||
|
graphic!,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
19
packages/flutter_introduction_widget/pubspec.yaml
Normal file
19
packages/flutter_introduction_widget/pubspec.yaml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
name: flutter_introduction_widget
|
||||||
|
description: Flutter Introduction Widget for showing a list of introduction pages on a single scrollable page or horizontal pageview
|
||||||
|
version: 4.0.0
|
||||||
|
homepage: https://github.com/Iconica-Development/flutter_introduction_widget
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.18.0 <3.0.0"
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
|
@ -0,0 +1,11 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('test', () {
|
||||||
|
expect(true, true);
|
||||||
|
});
|
||||||
|
}
|
27
pubspec.yaml
27
pubspec.yaml
|
@ -1,27 +1,6 @@
|
||||||
name: flutter_introduction
|
name: flutter_introduction_workspace
|
||||||
description: Combined Package of Flutter Introduction Widget and Flutter Introduction Service
|
|
||||||
version: 1.0.0
|
|
||||||
publish_to: none
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.18.0 <3.0.0"
|
sdk: '>=3.1.0 <4.0.0'
|
||||||
flutter: ">=1.17.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_introduction_widget:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_introduction_widget.git
|
|
||||||
ref: 3.0.0
|
|
||||||
flutter_introduction_service:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_introduction_service.git
|
|
||||||
ref: 1.0.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
melos: ^3.0.1
|
||||||
sdk: flutter
|
|
||||||
flutter_lints: ^2.0.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
|
|
Loading…
Reference in a new issue