Compare commits

..

22 commits

Author SHA1 Message Date
Kiril Tijsma
87bf58e6e7
Merge pull request #40 from Iconica-Development/fix/splashscreen-future
fix: always call the splashHandler with the splashScreenFuture and killswitchService when a splashScreenBuilder is provided
2025-04-30 10:01:30 +02:00
Freek van de Ven
ada23abbc5 fix: always call the splashHandler with the splashScreenFuture and killswitchService when a splashScreenBuilder is provided 2025-04-30 09:50:45 +02:00
Jacques
2c61f8d7db fix: Check with the service if the introduction should be shown before actually showing
When removing this, the introduction is briefly shown before calling the onComplete
2025-04-29 15:16:35 +02:00
Freek van de Ven
2a66e11611 chore: add component release workflow 2025-04-22 10:34:27 +02:00
0976083a9f feat(navigator): add navigator widget to allow for app independent navigation 2025-03-17 15:46:44 +01:00
6f24e0521d chore(example): add standard flutter readme 2025-03-17 15:46:44 +01:00
328a75b5a2 chore(example): add web platform to gitignore 2025-03-17 15:46:44 +01:00
Gorter-dev
ed8b5b401a
Merge pull request #37 from Iconica-Development/bugfix/introduction
fix: introduction
2024-07-30 13:19:20 +02:00
mike doornenbal
7635b8a4cc fix: introduction 2024-07-30 13:12:39 +02:00
Gorter-dev
d0b0f88ad3
Merge pull request #35 from Iconica-Development/chore/deploy
chore: ready the package for deployment to the pub server
2024-07-22 15:11:28 +02:00
Bart Ribbers
62161aa60b chore: ready the package for deployment to the pub server 2024-07-22 15:09:14 +02:00
Bart Ribbers
bf018a3ce6 chore: add fvm configuration to gitignore 2024-07-22 15:08:05 +02:00
mike doornenbal
c521be374d
Merge pull request #36 from Iconica-Development/bugfix/default_style
fix: default style and flutter_introduction to new version
2024-07-18 13:15:23 +02:00
mike doornenbal
c558c8dbc7 fix: default style and flutter_introduction to new version 2024-07-12 16:06:01 +02:00
mike doornenbal
c51c7de53a
Merge pull request #34 from Iconica-Development/bugfix/feedback
fix: feedback on userstory
2024-06-06 13:31:23 +02:00
mike doornenbal
84e629d5b2 fix: feedback on userstory 2024-06-05 10:37:20 +02:00
Gorter-dev
e7419bc8c1
Merge pull request #27 from Iconica-Development/feature/default_styling
feat: add default styling
2024-04-19 11:21:47 +02:00
mike doornenbal
90eb8119d8 feat: add default styling 2024-04-19 10:04:03 +02:00
Gorter-dev
fc65e98f96
Merge pull request #26 from Iconica-Development/feature/default_configuration
feat: add default configuration
2024-04-18 09:33:49 +02:00
mike doornenbal
07e1ad0990 feat: add default configuration 2024-04-17 13:49:43 +02:00
mike doornenbal
e357f2fd0f
Merge pull request #25 from Iconica-Development/fix/use-correct-intro-screen-check-boolean
fix: use startWithIntroScreen instead of showIntroduction boolean for…
2024-04-15 10:11:47 +02:00
bd1d6e5bf4 fix: use startWithIntroScreen instead of showIntroduction boolean for checking on which screen to start 2024-04-05 11:44:59 +02:00
14 changed files with 220 additions and 156 deletions

14
.github/workflows/release.yml vendored Normal file
View file

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

4
.gitignore vendored
View file

@ -44,3 +44,7 @@ app.*.map.json
ios
.metadata
pubspec.lock
# FVM Version Cache
.fvm/
.fvmrc

View file

@ -1,3 +1,25 @@
## 4.2.4
- Fixed the userstory to always call the splashScreenFuture and killswitchservice logic when a custom splashScreenBuilder is provided
## 4.2.3
- Added check if introduction should be shown according to the service before showing the introduction at all
## 4.2.2
- Added custom navigator in the root of the navigator user-story
## 4.2.1
- Updated flutter_introduction to 5.0.0
## 4.1.0
- Updated README
- Removed check if the introductions should be shown.
- Updated flutter_introduction to 3.1.0
## 4.0.0
- Added default introduction page.
- Added default splash screen.
- Changed the way the splash screen is enabled/disabled.
## 3.0.0
BREAKING:

100
README.md
View file

@ -6,60 +6,65 @@ Flutter_start is a package that allows you to jumpstart your application with a
To use this package, add flutter_start as a dependency in your pubspec.yaml file:
```
```yaml
flutter_start:
git:
url: https://github.com/Iconica-Development/flutter_start
ref: <Version>
ref: 4.1.0
```
To use the module within your Flutter-application with predefined `Go_router` routes you should add the following:
## go_router
Add `go_router` as dependency to your project.
Add go_router as dependency to your project.
Add the following configuration to your flutter_application:
```
StartUserStoryConfiguration startUserStoryConfiguration = const StartUserStoryConfiguration();
List<GoRoute> getStartStoryRoutes() => getStartStoryRoutes();
```
and set the values as you wish.
Or with options:
Next add the `StartUserStoryConfiguration` to `getStartStoryRoutes` Like so:
Place the following code somewhere in your project where it can be accessed globally:
```
List<GoRoute> getStartRoutes() => getStartStoryRoutes(
startUserStoryConfiguration,
var startUserStoryConfiguration = const StartUserStoryConfiguration();
```
```
List<GoRoute> getStartStoryRoutes() => getStartStoryRoutes(
configuration: startUserStoryConfiguration,
);
```
Finally add the `getStartRoutes` to your `Go_router` routes like so:
Finally add the `getStartRoutes` to your `go_router` routes like so:
```
final GoRouter _router = GoRouter(
routes: <RouteBase>[
...getStartRoutes()
...getStartStoryRoutes()
],
);
```
The routes that can be used to navigate are:
For routing to the `SplashScreen`:
For routing to the `splashScreen`:
```
static const String splashScreen = '/splashScreen';
static const String splashScreen = '/splashScreen';
```
For routing to the `Introduction`:
For routing to the `introduction`:
```
static const String introduction = '/introduction';
static const String introduction = '/introduction';
```
For routing to the `HomeEntry`:
For routing to the `home`:
```
static const String home = '/home';
static const String home = '/home';
```
If you don't want a SplashScreen in your application set your initialRoute to `Introduction`:
@ -73,33 +78,64 @@ final GoRouter _router = GoRouter(
);
```
To use the module within your Flutter-application without predefined `Go_router` routes but with `Navigator` routes add the following :
Add the following configuration to your flutter_application:
## Navigator
Add the following code to the build-method of a chosen widget like so:
```
StartUserStoryConfiguration startUserStoryConfiguration = const StartUserStoryConfiguration();
class Example extends StatelessWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
return NavigatorStartUserStory(
onComplete: (context) {},
);
}
}
```
Add the following code to the build-method of a chosen widget:
or with options:
Place the following code somewhere in your project where it can be accessed globally:
```
startNavigatorUserStory(startUserStoryConfiguration, context);
var startUserStoryConfiguration = const StartUserStoryConfiguration();
```
If the splashScreenBuilder is not used the SplashScreen will be skipped.
```
class Example extends StatelessWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
return NavigatorStartUserStory(
configuration: startUserStoryConfiguration,
onComplete: (context) {},
);
}
}
```
The `StartUserStoryConfiguration` has its own parameters, as specified below:
| Parameter | Explanation |
|-----------|-------------|
| splashScreenBuilder | The builder for the splashScreen. |
| introductionOptions | The options for the introduction. |
| introductionService | The service for the introduction. Default IntroductionService (SharedPreferencesIntroductionDataProvider()) |
| homeEntry | The widget that will be shown after the introduction. |
| introductionFallbackScreen | The widget that will be shown when the introduction is skipped. |
| introductionScrollPhysics | The scrollPhysics for the introduction. |
| showIntroduction | Whether or not the introduction should be shown. |
| useKillswitch | Whether or not the killswitch should be used. This will only work when you use the splashScreen and you need to have a active internet connection|
| `splashScreenBuilder` | The builder to override the default splashScreen |
| `introductionBuilder` | A builder to wrap the introductions in your own page |
| `introductionOptionsBuilder` | The builder to override the default `introductionOptions` |
|`introductionService` | The service to override the default `introductionService` |
| `homeScreenRoute` | The route to navigate to after the introduction or splashScreen is completed |
| `introductionFallbackScreen` | The screen that is shown when something goes wrong fetching data for the introduction |
| `introductionScrollPhysics` | The scroll physics for the introduction |
| `showIntroduction` | A boolean to show the introduction or not. Defaults to true |
| `useKillswitch` | A boolean to use the killswitch or not. Defaults to false |
| `minimumSplashScreenDuration` | The minimum duration the splashScreen should be shown. Defaults to 3 seconds |
| `splashScreenFuture` | The future to be completed before the splashScreen is completed |
| `splashScreenCenterWidget` | The widget to be shown in the center of the splashScreen |
| `splashScreenBackgroundColor` | The color of the splashScreen background. Defaults to Color(0xff212121) |
| `canPopFromIntroduction` | Allow popping from introduction, defaults to true. Defaults to true |
| `killswitchService` | The service to override the default killswitch service |
| `showSplashScreen` | A boolean to show the splashScreen or not. Defaults to true |
## Issues

3
example/.gitignore vendored
View file

@ -31,6 +31,9 @@ migrate_working_dir/
.pub/
/build/
# platforms
/web
# Symbolication related
app.*.symbols

16
example/README.md Normal file
View file

@ -0,0 +1,16 @@
# example
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View file

@ -31,7 +31,7 @@ class Home extends StatelessWidget {
}
List<GoRoute> getStartRoutes() => getStartStoryRoutes(
config,
configuration: config,
);
StartUserStoryConfiguration config = StartUserStoryConfiguration(
@ -39,38 +39,6 @@ StartUserStoryConfiguration config = StartUserStoryConfiguration(
splashScreenBuilder: (context, onFinish) => SplashScreen(
onFinish: onFinish,
),
introductionOptionsBuilder: (ctx) => 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(),
),
],
introductionTranslations: const IntroductionTranslations(
skipButton: 'Skip it!',
nextButton: 'Next',
previousButton: 'Previous',
finishButton: 'To the app!',
),
tapEnabled: true,
displayMode: IntroductionDisplayMode.multiPageHorizontal,
buttonMode: IntroductionScreenButtonMode.text,
indicatorMode: IndicatorMode.dash,
skippable: true,
buttonBuilder: (context, onPressed, child, type) =>
ElevatedButton(onPressed: onPressed, child: child),
),
);
class SplashScreen extends StatefulWidget {
@ -114,17 +82,3 @@ class HomeEntry extends StatelessWidget {
body: const Center(child: Text('HomeEntry')),
);
}
class ExampleIntroductionDataProvider
implements SharedPreferencesIntroductionDataProvider {
@override
String key = 'example';
@override
Future<void> setCompleted({bool value = true}) async =>
// ignore: void_checks
false;
@override
Future<bool> shouldShow() async => true;
}

View file

@ -12,10 +12,8 @@ dependencies:
flutter_start:
path: ../
flutter_introduction_shared_preferences:
git:
url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.1.0
path: packages/flutter_introduction_shared_preferences
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub/
version: ^5.0.0
dev_dependencies:
flutter_test:

View file

@ -20,9 +20,10 @@ class StartUserStoryConfiguration {
this.minimumSplashScreenDuration = 3,
this.splashScreenFuture,
this.splashScreenCenterWidget,
this.splashScreenBackgroundColor,
this.splashScreenBackgroundColor = const Color(0xff212121),
this.canPopFromIntroduction = true,
this.killswitchService,
this.showSplashScreen = true,
});
/// You can use this to build your own splash screen.
@ -68,9 +69,6 @@ class StartUserStoryConfiguration {
/// Allow popping from introduction, defaults to true
final bool canPopFromIntroduction;
/// returns true if the userstory should start with the introduction screen
bool get startWithIntroScreen =>
splashScreenBuilder == null &&
splashScreenCenterWidget == null &&
splashScreenBackgroundColor == null;
/// If the splash screen should be shown or not.
final bool showSplashScreen;
}

View file

@ -11,19 +11,19 @@ import 'package:flutter_start/src/go_router.dart';
import 'package:flutter_start/src/models/start_configuration.dart';
import 'package:flutter_start/src/routes.dart';
import 'package:flutter_start/src/services/killswitch_service.dart';
import 'package:flutter_start/src/widgets/default_splash_screen.dart';
import 'package:go_router/go_router.dart';
List<GoRoute> getStartStoryRoutes(
StartUserStoryConfiguration configuration,
) =>
List<GoRoute> getStartStoryRoutes({
StartUserStoryConfiguration? configuration =
const StartUserStoryConfiguration(),
}) =>
<GoRoute>[
GoRoute(
path: StartUserStoryRoutes.splashScreen,
pageBuilder: (context, state) {
var go = context.go;
var isAllowedToPassThrough = false;
var introductionSeen = false;
String? routeAfterSplash;
Future<void> splashLoadingMethod() async {
await Future.wait<void>(
@ -31,7 +31,7 @@ List<GoRoute> getStartStoryRoutes(
Future.delayed(
Duration.zero,
() async {
if (configuration.useKillswitch) {
if (configuration!.useKillswitch) {
var killswitchService = configuration.killswitchService ??
DefaultKillswitchService();
@ -39,11 +39,6 @@ List<GoRoute> getStartStoryRoutes(
await killswitchService.isKillswitchActive();
}
var introService = configuration.introductionService ??
IntroductionService(
SharedPreferencesIntroductionDataProvider(),
);
introductionSeen = !await introService.shouldShow();
if (context.mounted)
routeAfterSplash = await configuration.splashScreenFuture
?.call(context) ??
@ -52,7 +47,7 @@ List<GoRoute> getStartStoryRoutes(
),
Future.delayed(
Duration(
seconds: configuration.minimumSplashScreenDuration,
seconds: configuration!.minimumSplashScreenDuration,
),
() async {},
),
@ -61,7 +56,7 @@ List<GoRoute> getStartStoryRoutes(
if (configuration.useKillswitch && isAllowedToPassThrough) return;
if (!configuration.showIntroduction || introductionSeen) {
if ((!configuration.showIntroduction) && context.mounted) {
return go(
routeAfterSplash ?? StartUserStoryRoutes.home,
);
@ -69,7 +64,7 @@ List<GoRoute> getStartStoryRoutes(
return go(StartUserStoryRoutes.introduction);
}
if (configuration.splashScreenBuilder == null) {
if (configuration!.splashScreenBuilder == null) {
unawaited(splashLoadingMethod());
}
return buildScreenWithoutTransition(
@ -84,7 +79,7 @@ List<GoRoute> getStartStoryRoutes(
body: Center(
child:
configuration.splashScreenCenterWidget?.call(context) ??
const SizedBox.shrink(),
defaultSplashScreen(context),
),
),
);
@ -94,7 +89,7 @@ List<GoRoute> getStartStoryRoutes(
path: StartUserStoryRoutes.introduction,
pageBuilder: (context, state) {
var introduction = Introduction(
service: configuration.introductionService ??
service: configuration!.introductionService ??
IntroductionService(
SharedPreferencesIntroductionDataProvider(),
),

View file

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_start/flutter_start.dart';
import 'package:flutter_start/src/services/killswitch_service.dart';
import 'package:flutter_start/src/widgets/default_splash_screen.dart';
/// Initial screen of the user story.
///
@ -18,13 +19,11 @@ class NavigatorStartUserStory extends StatelessWidget {
final void Function(BuildContext context) onComplete;
@override
Widget build(BuildContext context) {
if (configuration.showIntroduction) {
return _introduction(configuration, context, onComplete);
}
return _splashScreen(configuration, context, onComplete);
}
Widget build(BuildContext context) => Navigator(
onGenerateInitialRoutes: (_, __) => [
_getInitialRoute(configuration, onComplete),
],
);
}
/// Enter the start user story with the Navigator 1.0 API.
@ -41,6 +40,15 @@ Future<void> startNavigatorUserStory(
StartUserStoryConfiguration configuration, {
required void Function(BuildContext context) onComplete,
}) async {
var initialRoute = _getInitialRoute(configuration, onComplete);
await Navigator.of(context).push(initialRoute);
}
MaterialPageRoute<dynamic> _getInitialRoute(
StartUserStoryConfiguration configuration,
void Function(BuildContext context) onComplete,
) {
var initialRoute = MaterialPageRoute(
builder: (context) => _splashScreen(
configuration,
@ -49,7 +57,7 @@ Future<void> startNavigatorUserStory(
),
);
if (configuration.startWithIntroScreen) {
if (!configuration.showSplashScreen && configuration.showIntroduction) {
initialRoute = MaterialPageRoute(
builder: (context) => _introduction(
configuration,
@ -58,8 +66,7 @@ Future<void> startNavigatorUserStory(
),
);
}
await Navigator.of(context).push(initialRoute);
return initialRoute;
}
Widget _splashScreen(
@ -70,7 +77,6 @@ Widget _splashScreen(
var navigator = Navigator.of(context);
var isAllowedToPassThrough = false;
var introductionSeen = false;
Future<void> splashHandler() async {
await Future.wait<void>(
@ -86,12 +92,6 @@ Widget _splashScreen(
isAllowedToPassThrough =
await killswitchService.isKillswitchActive();
}
var introService = configuration.introductionService ??
IntroductionService(
SharedPreferencesIntroductionDataProvider(),
);
introductionSeen = !await introService.shouldShow();
},
),
Future.delayed(
@ -104,34 +104,37 @@ Widget _splashScreen(
if (configuration.useKillswitch && isAllowedToPassThrough) return;
if ((!configuration.showIntroduction || introductionSeen) &&
context.mounted) {
onComplete(context);
return;
}
var introService = configuration.introductionService ??
IntroductionService(SharedPreferencesIntroductionDataProvider());
if (context.mounted) {
await navigator.pushReplacement(
MaterialPageRoute(
builder: (context) => _introduction(
configuration,
context,
onComplete,
),
var shouldShowIntroduction =
configuration.showIntroduction && await introService.shouldShow();
if (!context.mounted) return;
if (!shouldShowIntroduction) return onComplete(context);
await navigator.pushReplacement(
MaterialPageRoute(
builder: (context) => _introduction(
configuration,
context,
onComplete,
),
);
}
),
);
}
unawaited(splashHandler());
var builder = configuration.splashScreenBuilder;
if (builder == null) {
unawaited(splashHandler());
return Scaffold(
backgroundColor: configuration.splashScreenBackgroundColor,
body: Center(
child: configuration.splashScreenCenterWidget?.call(context) ??
const SizedBox.shrink(),
defaultSplashScreen(context),
),
);
}
@ -158,8 +161,12 @@ Widget _introduction(
);
return PopScope(
canPop: configuration.canPopFromIntroduction,
child: Scaffold(
body: introduction,
),
child: configuration.introductionBuilder?.call(
context,
introduction,
) ??
Scaffold(
body: introduction,
),
);
}

View file

@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
Text defaultSplashScreen(BuildContext context) => Text(
'iconinstagram',
style: Theme.of(context).textTheme.headlineLarge,
);

View file

@ -1,7 +1,8 @@
name: flutter_start
description: "Flutter_start is a package that allows you to jumpstart your application with a splashScreen, introduction and a home."
publish_to: "none"
version: 3.0.0
version: 4.2.4
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
environment:
sdk: ">=3.2.5 <4.0.0"
@ -9,20 +10,16 @@ environment:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
go_router: any
http: any
package_info_plus: any
cupertino_icons: ">=1.0.2 <2.0.0"
go_router: ">=14.2.0 <15.0.0"
http: ">=1.2.1 <2.0.0"
package_info_plus: ">=8.0.0 <9.0.0"
flutter_introduction:
git:
url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.1.0
path: packages/flutter_introduction
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
version: ^5.0.0
flutter_introduction_shared_preferences:
git:
url: https://github.com/Iconica-Development/flutter_introduction
ref: 2.1.0
path: packages/flutter_introduction_shared_preferences
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub/
version: ^5.0.0
dev_dependencies:
flutter_test:

14
test/_test.dart Normal file
View file

@ -0,0 +1,14 @@
// This is an example unit test.
//
// A unit test tests a single function, method, or class. To learn more about
// writing unit tests, visit
// https://flutter.dev/docs/cookbook/testing/unit/introduction
import 'package:flutter_test/flutter_test.dart';
void main() {
group('Plus Operator', () {
test('should add two numbers together', () {
expect(1 + 1, 2);
});
});
}