diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8bc2888..7c7d8ff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,55 +1,57 @@
-## [3.0.2]
+## 4.0.0
+
+* Added support for using the loader in `StatelessWidget` too
+* Using [`stream_mixin`](https://pub.dev/packages/stream_mixin/) instead of state to show/hide loader
+* **BREAKING CHANGE**: Use `build` instead of `screen` function
+* **BREAKING CHANGE**: `ScreenLoaderApp` widget is removed, use `configScreenLoader` function instead
+
+## 3.0.2
+
* Fixed: [Issue #6](https://github.com/arnold-parge/screen_loader/issues/6): Screen is always blurry
-## [3.0.1]
+## 3.0.1
+
* Updated README
-## [3.0.0]
+## 3.0.0
+
* Migrating to null safety
-## [2.0.1]
-* Updated readme as per the previous version's breaking changes
+## 2.0.1
-## [2.0.0]
+* Updated readme as per the previous version's breaking changes
-### BREAKING CHANGE
-* `screenWrapper` function removed
+## 2.0.0
+* **BREAKING CHANGE**: `screenWrapper` function removed
* Now just override `screen` function instead of `build` function in your `StatefulWidget`s
-## [1.1.1]
+## 1.1.1
* Updated readme
+## 1.1.0
-## [1.1.0]
-
-### Breaking changes
-* Removed state parameter from `screenWrapper` function
+* **BREAKING CHANGE**: Removed state parameter from `screenWrapper` function
* Added performFuture
* Added BasicScreen in example
* Added loadingBgBlur in ScreenLoader
-
-## [1.0.4]
+## 1.0.4
* Updated description
-
-## [1.0.3]
+## 1.0.3
* Added example
-
-## [1.0.2]
+## 1.0.2
* Updated description in pubspec.yaml
-
-## [1.0.1]
+## 1.0.1
* Updated description in pubspec.yaml
-
-## [1.0.0]
+## 1.0.0
* Initial release
diff --git a/README.md b/README.md
index a667f5c..0aca71d 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,25 @@
# screen_loader
-Using `showDialog` for showing loader is a **BAD IDEA**. You may end up messing the navigation stack and `context` due to pushing and popping the loader(dialog). Hence, I have come up with an easy to use mixin `ScreenLoader`, which will handle the loading on the screen. You can customise the loading as well, check below how it is done.
-
-## Important
-Replace your `build(BuildContext context)` function with `screen(BuildContext context)`
-
-```dart
-@override
-Widget screen(BuildContext context) {
- return Scaffold(
- appBar: _buildAppBar(),
- body: _buildBody(),
- );
-}
-```
-
-## Basic Usage
-
-Extend your screen(`StatefulWidget`) with `ScreenLoader`. Use `performFuture` to show loader while your future us being performed. That's it!
+## Why ScreenLoader?
+1. With the help of [`stream_mixin`](https://pub.dev/packages/stream_mixin/), it shows and hides the loader without updating the state of the widget which increases the performance
+1. It does not push any sort of widget to navigation stack, this helps in not messing up the navigation stack and context
+1. Easy to use, just use your screen with `ScreenLoader` mixin and wrap the widget with `loadableWidget`
+1. The loader is customizable. You can use any widget as a loader
+1. You can configure same loader for all the screens at once
+1. You can also configure different loader for different screens
+
+---
+## Basic usage
+1. Use your screen with the `ScreenLoader` mixin
+1. Wrap the widget with `loadableWidget`
+1. Use `performFuture` function to show loader while your future is being performed
-## Override Loader
-
-Simply overide `loader()` method in your `_ScreenState` class
+---
+## Configure different loader for individual screen
+Simply overide `loader()` method
```
loader() {
// here any widget would do
@@ -36,39 +31,113 @@ loader() {
-## Override Loader Gobally
+---
+## Configure the loader for all screens at once
```dart
-void main() => runApp(MyApp());
-
-class MyApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return ScreenLoaderApp(
- app: MaterialApp(
- title: AppStrings.yapChat,
- theme: ThemeData(
- primarySwatch: Colors.blue,
- ),
- home: Screen(),
- ),
- globalLoader: AlertDialog(
- title: Text('Gobal Loader..'),
- ),
- );
- }
+void main() {
+ configScreenLoader(
+ loader: AlertDialog(
+ title: Text('Gobal Loader..'),
+ ),
+ bgBlur: 20.0,
+ );
+ runApp(MyApp());
}
-
```
-## Priority of loaders
+---
+## Priority of loaders (_highest first_)
-- **Local loader**: the one you override in the `_ScreenState` class
-- **Global loader**: the one you specify in `ScreenLoaderApp`. Note: if you don't override `local()`, this loader will be used.
+- **Local loader**: the one you override in your widget
+- **Global loader**: the one you specify in `configScreenLoader`. Note: if you don't override `local()`, this loader will be used
- **Default loader**: if you don't specify global loader or override local loader, this loader will be used
+---
+# Migration guide to 4.0.0
+1. `ScreenLoaderApp` widget is removed. Now no need to wrap your App around any widget just to set the global configurations. Instead call `configScreenLoader()` before `runApp()`
+```diff
+-void main() => runApp(MyApp());
++void main() {
++ configScreenLoader(
++ loader: AlertDialog(
++ title: Text('Gobal Loader..'),
++ ),
++ bgBlur: 20.0,
++ );
++ runApp(MyApp());
++}
+
+ class MyApp extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+- return ScreenLoaderApp(
+- app: MaterialApp(
+- debugShowCheckedModeBanner: false,
+- title: 'Screen Loader',
+- theme: ThemeData(
+- primarySwatch: Colors.blue,
+- ),
+- home: Screen(),
+- ),
+- globalLoader: AlertDialog(
+- title: Text('Gobal Loader..'),
++ return MaterialApp(
++ debugShowCheckedModeBanner: false,
++ title: 'Screen Loader',
++ theme: ThemeData(
++ primarySwatch: Colors.blue,
+ ),
+- globalLoadingBgBlur: 20.0,
++ home: Screen(),
+ );
+ }
+ }
+```
+2. Instead of replacing the `build` function with `screen` use the `build` function itself and wrap the widget you want to show loader on with the `loadableWidget`.
+```diff
+
+-class _ScreenState extends State with ScreenLoader {
++class _ScreenState extends State with ScreenLoader {
+ @override
+ loader() {
+ return AlertDialog(
+@@ -49,17 +51,19 @@ class _ScreenState extends State with ScreenLoader {
+ }
+
+ @override
+- Widget screen(BuildContext context) {
+- return Scaffold(
+- appBar: AppBar(
+- title: Text('ScreenLoader Example'),
+- ),
+- body: _buildBody(),
+- floatingActionButton: FloatingActionButton(
+- onPressed: () async {
+- await this.performFuture(NetworkService.getData);
+- },
+- child: Icon(Icons.refresh),
++ Widget build(BuildContext context) {
++ return loadableWidget(
++ child: Scaffold(
++ appBar: AppBar(
++ title: Text('ScreenLoader Example'),
++ ),
++ body: _buildBody(),
++ floatingActionButton: FloatingActionButton(
++ onPressed: () async {
++ await this.performFuture(NetworkService.getData);
++ },
++ child: Icon(Icons.refresh),
++ ),
+ ),
+ );
+ }
+```
+
+---
### PS
- PRs are welcome
- Please raise issues on https://github.com/arnold-parge/screen_loader.
diff --git a/example/main.dart b/example/main.dart
index bf6876b..6ef2745 100644
--- a/example/main.dart
+++ b/example/main.dart
@@ -1,24 +1,26 @@
import 'package:flutter/material.dart';
import 'package:screen_loader/screen_loader.dart';
-void main() => runApp(MyApp());
+void main() {
+ configScreenLoader(
+ loader: AlertDialog(
+ title: Text('Gobal Loader..'),
+ ),
+ bgBlur: 20.0,
+ );
+ runApp(MyApp());
+}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return ScreenLoaderApp(
- app: MaterialApp(
- debugShowCheckedModeBanner: false,
- title: 'Screen Loader',
- theme: ThemeData(
- primarySwatch: Colors.blue,
- ),
- home: Screen(),
+ return MaterialApp(
+ debugShowCheckedModeBanner: false,
+ title: 'Screen Loader',
+ theme: ThemeData(
+ primarySwatch: Colors.blue,
),
- globalLoader: AlertDialog(
- title: Text('Gobal Loader..'),
- ),
- globalLoadingBgBlur: 20.0,
+ home: Screen(),
);
}
}
@@ -28,7 +30,8 @@ class Screen extends StatefulWidget {
_ScreenState createState() => _ScreenState();
}
-class _ScreenState extends State with ScreenLoader {
+/// A Stateful screen
+class _ScreenState extends State with ScreenLoader {
@override
loader() {
return AlertDialog(
@@ -49,46 +52,47 @@ class _ScreenState extends State with ScreenLoader {
}
@override
- Widget screen(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('ScreenLoader Example'),
- ),
- body: _buildBody(),
- floatingActionButton: FloatingActionButton(
- onPressed: () async {
- await this.performFuture(NetworkService.getData);
- },
- child: Icon(Icons.refresh),
+ Widget build(BuildContext context) {
+ return loadableWidget(
+ child: Scaffold(
+ appBar: AppBar(
+ title: Text('ScreenLoader Example'),
+ ),
+ body: _buildBody(),
+ floatingActionButton: FloatingActionButton(
+ onPressed: () async {
+ await this.performFuture(NetworkService.getData);
+ },
+ child: Icon(Icons.refresh),
+ ),
),
);
}
}
-class BasicScreen extends StatefulWidget {
- @override
- _BasicScreenState createState() => _BasicScreenState();
-}
+/// A Stateless screen
+class BasicScreen extends StatelessWidget with ScreenLoader {
+ BasicScreen({ Key? key }) : super(key: key);
-class _BasicScreenState extends State
- with ScreenLoader {
@override
- Widget screen(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Basic ScreenLoader Example'),
- ),
- body: Center(
- child: Icon(
- Icons.home,
- size: MediaQuery.of(context).size.width,
+ Widget build(BuildContext context) {
+ return loadableWidget(
+ child: Scaffold(
+ appBar: AppBar(
+ title: Text('Basic ScreenLoader Example'),
+ ),
+ body: Center(
+ child: Icon(
+ Icons.home,
+ size: MediaQuery.of(context).size.width,
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: () async {
+ await this.performFuture(NetworkService.getData);
+ },
+ child: Icon(Icons.refresh),
),
- ),
- floatingActionButton: FloatingActionButton(
- onPressed: () async {
- await this.performFuture(NetworkService.getData);
- },
- child: Icon(Icons.refresh),
),
);
}
diff --git a/lib/screen_loader.dart b/lib/screen_loader.dart
index d4d8707..0462a1d 100644
--- a/lib/screen_loader.dart
+++ b/lib/screen_loader.dart
@@ -2,32 +2,46 @@ library screen_loader;
import 'dart:ui';
import 'package:flutter/material.dart';
+import 'package:stream_mixin/stream_mixin.dart';
-mixin ScreenLoader on State {
- bool isLoading = false;
- static Widget? _globalLoader;
- static double? _globalLoadingBgBlur = 5.0;
+void configScreenLoader({
+ required Widget? loader,
+ required double bgBlur,
+}) {
+ GlobalScreenLoader.loader = loader;
+ GlobalScreenLoader.bgBlur = bgBlur;
+}
+
+abstract class GlobalScreenLoader {
+ static Widget? loader;
+ static double bgBlur = 5.0;
+}
+
+class ScreenLoaderStream with StreamMixin {
+ @override
+ bool get lastUpdate => false;
+}
+
+
+mixin ScreenLoader {
+ final ScreenLoaderStream _screenLoaderStream = ScreenLoaderStream();
/// starts the [loader]
startLoading() {
- setState(() {
- isLoading = true;
- });
+ _screenLoaderStream.update(true);
}
/// stops the [loader]
stopLoading() {
- setState(() {
- isLoading = false;
- });
+ _screenLoaderStream.update(false);
}
-
- /// DO NOT use this method in FutureBuilder because this methods
- /// updates the state which will make future builder to call
- /// this function again and it will go in loop
- Future performFuture(Function futureCallback) async {
+
+ /// To avoid use of [startLoading] and [stopLoading] you use use
+ /// [performFuture] which will show the loader until the passed future call
+ /// is done executing
+ Future performFuture(Function futureCallback) async {
startLoading();
- T? data = await futureCallback();
+ U? data = await futureCallback();
stopLoading();
return data;
}
@@ -38,7 +52,7 @@ mixin ScreenLoader on State {
}
double _loadingBgBlur() {
- return loadingBgBlur() ?? ScreenLoader._globalLoadingBgBlur ?? 5.0;
+ return loadingBgBlur() ?? GlobalScreenLoader.bgBlur;
}
/// override [loader] if you wish to add custom loader in specific view
@@ -48,7 +62,7 @@ mixin ScreenLoader on State {
Widget _loader() {
return loader() ??
- ScreenLoader._globalLoader ??
+ GlobalScreenLoader.loader ??
const CircularProgressIndicator();
}
@@ -61,43 +75,28 @@ mixin ScreenLoader on State {
);
}
- Widget screen(BuildContext context);
-
- @override
- Widget build(BuildContext context) {
+ Widget loadableWidget({required Widget child}) {
return Stack(
children: [
- screen(context),
- if (isLoading)
- BackdropFilter(
- child: _buildLoader(),
- filter: ImageFilter.blur(
- sigmaX: _loadingBgBlur(),
- sigmaY: _loadingBgBlur(),
- ),
- ),
+ child,
+ StreamBuilder(
+ initialData: false,
+ stream: _screenLoaderStream.onChange,
+ builder: (ctx, snap) {
+ if (!(snap.data ?? false)) {
+ return const SizedBox();
+ } else {
+ return BackdropFilter(
+ child: _buildLoader(),
+ filter: ImageFilter.blur(
+ sigmaX: _loadingBgBlur(),
+ sigmaY: _loadingBgBlur(),
+ ),
+ );
+ }
+ },
+ ),
],
);
}
}
-
-/// [ScreenLoaderApp] is used to provide global settings for the screen loader
-class ScreenLoaderApp extends StatelessWidget {
- final MaterialApp app;
- final Widget? globalLoader;
- final double? globalLoadingBgBlur;
-
- const ScreenLoaderApp({
- Key? key,
- required this.app,
- this.globalLoader,
- this.globalLoadingBgBlur,
- }) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- ScreenLoader._globalLoader = globalLoader;
- ScreenLoader._globalLoadingBgBlur = globalLoadingBgBlur;
- return app;
- }
-}
diff --git a/pubspec.lock b/pubspec.lock
index 8077d23..5789602 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -107,6 +107,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
+ stream_mixin:
+ dependency: "direct main"
+ description:
+ name: stream_mixin
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "3.0.4"
string_scanner:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index ca50190..e80f294 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: screen_loader
-description: Easy to use mixin ScreenLoader, which will handle the loading on the screen. You can customise the loading as well.
-version: 3.0.2
+description: Easy to use mixin ScreenLoader, which will handle the loading on the screen without using state or navigation stack. You can customise the loading as well.
+version: 4.0.0
homepage: https://github.com/arnold-parge/screen_loader
environment:
@@ -9,6 +9,7 @@ environment:
dependencies:
flutter:
sdk: flutter
+ stream_mixin: ^3.0.4
dev_dependencies:
flutter_test: