Skip to content

Commit 3fc50c8

Browse files
committed
Update version to 0.1.0 and enhance functionality in CHANGELOG.md, README.md, and any_refreshable_widget.dart
- Introduced lifecycle callbacks `onBeforeRefresh` and `onAfterRefresh` for better control during refresh operations. - Added `RefreshConcurrency` enum to manage execution of multiple futures (concurrent or sequential). - Updated README.md with new features, usage examples, and detailed API reference. - Enhanced CHANGELOG.md to reflect the new version and features.
1 parent cc69448 commit 3fc50c8

File tree

6 files changed

+232
-19
lines changed

6 files changed

+232
-19
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,8 @@ app.*.symbols
118118
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
119119
!/dev/ci/**/Gemfile.lock
120120
.DS_Store
121+
122+
# Example related
123+
example/.vscode/launch.json
124+
example/.metadata
125+
example/analysis_options.yaml

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 0.1.0
2+
3+
* **Lifecycle callbacks** - Added `onBeforeRefresh` and `onAfterRefresh` callbacks with flexible sync/async support
4+
* **Concurrency control** - Added `RefreshConcurrency` enum to control future execution (sequential/concurrent)
5+
* **Sequential execution** - New option to execute multiple futures one after another (now default behavior)
6+
* **Enhanced documentation** - Comprehensive examples for lifecycle callbacks and concurrency modes
7+
* **Improved examples** - Updated example app with better UI and clearer demonstrations
8+
19
## 0.0.2
210

311
* **Improved compatibility** - Updated SDK constraint to support broader range of Dart versions (>=2.17.0 <4.0.0)

README.md

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ A powerful and flexible Flutter package that provides pull-to-refresh functional
1111

1212
## Features
1313

14-
- 🔄 **Single & Multiple Future Support** - Handle one or multiple asynchronous operations concurrently
14+
- 🔄 **Single & Multiple Future Support** - Handle one or multiple asynchronous operations
1515
- 🎨 **Customizable Refresh Indicator** - Full control over appearance and behavior
1616
- 📱 **Universal Widget Support** - Works with any widget, automatically makes content scrollable
1717
- 🎯 **Smart Error Handling** - Comprehensive error states and callbacks
1818
- 🔧 **Highly Configurable** - Colors, displacement, stroke width, trigger modes, and more
19+
-**Lifecycle Callbacks** - `onBeforeRefresh` and `onAfterRefresh` hooks with sync/async support
20+
- 🔀 **Flexible Concurrency** - Choose between concurrent (parallel) or sequential execution
1921
- 🚀 **Production Ready** - Thoroughly tested and optimized for real-world applications
2022

2123
## Installation
@@ -82,6 +84,40 @@ AnyRefreshableWidget(
8284
)
8385
```
8486

87+
### Concurrency Control
88+
89+
Control how multiple futures are executed:
90+
91+
#### Concurrent Execution (Default)
92+
```dart
93+
AnyRefreshableWidget(
94+
concurrency: RefreshConcurrency.concurrent,
95+
onRefresh: [
96+
() => fetchUserData(), // These run simultaneously
97+
() => fetchNotifications(), // for faster completion
98+
() => fetchSettings(),
99+
],
100+
builder: (context, isLoading, error) {
101+
return YourContentWidget();
102+
},
103+
)
104+
```
105+
106+
#### Sequential Execution
107+
```dart
108+
AnyRefreshableWidget(
109+
concurrency: RefreshConcurrency.sequential, // Default
110+
onRefresh: [
111+
() => authenticateUser(), // Runs first
112+
() => fetchUserData(), // Then this
113+
() => fetchNotifications(), // Finally this
114+
],
115+
builder: (context, isLoading, error) {
116+
return YourContentWidget();
117+
},
118+
)
119+
```
120+
85121
## Comprehensive Examples
86122

87123
### Custom Refresh Indicator
@@ -192,6 +228,47 @@ AnyRefreshableWidget.single(
192228
)
193229
```
194230

231+
### Lifecycle Callbacks
232+
233+
The package supports `onBeforeRefresh` and `onAfterRefresh` callbacks that can be either synchronous or asynchronous:
234+
235+
#### Synchronous Callbacks
236+
```dart
237+
AnyRefreshableWidget.single(
238+
onBeforeRefresh: () {
239+
print('Starting refresh...');
240+
// Synchronous setup logic
241+
},
242+
onRefresh: () => fetchData(),
243+
onAfterRefresh: () {
244+
print('Refresh completed!');
245+
// Synchronous cleanup logic
246+
},
247+
builder: (context, isLoading, error) {
248+
return YourContentWidget();
249+
},
250+
)
251+
```
252+
253+
#### Asynchronous Callbacks
254+
```dart
255+
AnyRefreshableWidget.single(
256+
onBeforeRefresh: () async {
257+
print('Starting refresh...');
258+
await prepareForRefresh();
259+
// Asynchronous setup logic
260+
},
261+
onRefresh: () => fetchData(),
262+
onAfterRefresh: () {
263+
print('Refresh completed!');
264+
// Cleanup logic (always sync)
265+
},
266+
builder: (context, isLoading, error) {
267+
return YourContentWidget();
268+
},
269+
)
270+
```
271+
195272
## API Reference
196273

197274
### AnyRefreshableWidget
@@ -200,6 +277,9 @@ AnyRefreshableWidget.single(
200277
|-----------|------|----------|---------|-------------|
201278
| `onRefresh` | `List<Future<void> Function()>` || - | List of async functions to execute on refresh |
202279
| `builder` | `Widget Function(BuildContext, bool, Object?)` || - | Builder function with loading and error states |
280+
| `concurrency` | `RefreshConcurrency` || `concurrent` | How futures should be executed (concurrent/sequential) |
281+
| `onBeforeRefresh` | `FutureOr<void> Function()?` || `null` | Callback executed before refresh starts (sync/async) |
282+
| `onAfterRefresh` | `VoidCallback?` || `null` | Callback executed after refresh completes |
203283
| `refreshColor` | `Color?` || `null` | Color of the refresh indicator |
204284
| `backgroundColor` | `Color?` || `null` | Background color of the refresh indicator |
205285
| `displacement` | `double` || `40.0` | Distance from top to show indicator |
@@ -210,7 +290,24 @@ AnyRefreshableWidget.single(
210290

211291
### AnyRefreshableWidget.single
212292

213-
Same parameters as `AnyRefreshableWidget`, but `onRefresh` takes a single `Future<void> Function()` instead of a list.
293+
Same parameters as `AnyRefreshableWidget`, but `onRefresh` takes a single `Future<void> Function()` instead of a list. The `concurrency` parameter is not applicable for single futures.
294+
295+
### RefreshConcurrency Enum
296+
297+
| Value | Description | Use Case |
298+
|-------|-------------|----------|
299+
| `RefreshConcurrency.concurrent` | Execute all futures simultaneously using `Future.wait` | fastest refresh when futures are independent |
300+
| `RefreshConcurrency.sequential` | Execute futures one by one in order | When futures depend on each other or to limit resource usage |
301+
302+
## Callback Execution Order
303+
304+
When a refresh is triggered, the callbacks execute in this order:
305+
306+
1. **`onBeforeRefresh`** - Called first, awaited if async
307+
2. **Loading state** - `isLoading` becomes `true`, UI updates
308+
3. **`onRefresh`** - All futures execute concurrently
309+
4. **Loading state** - `isLoading` becomes `false`, UI updates
310+
5. **`onAfterRefresh`** - Called last, always synchronous
214311

215312
## Advanced Configuration
216313

example/lib/main.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:developer';
2+
13
import 'package:flutter/material.dart';
24
import 'package:any_refreshable_widget/any_refreshable_widget.dart';
35

@@ -16,6 +18,7 @@ class MainApp extends StatelessWidget {
1618
_refreshWithCustomIndicator(),
1719
_refreshWithCustomWidget(),
1820
_refreshWithErrorCallback(),
21+
_refreshWithBeforeAfterCallback(),
1922
];
2023

2124
return MaterialApp(
@@ -102,3 +105,21 @@ Widget _refreshWithErrorCallback() {
102105
},
103106
);
104107
}
108+
109+
/// Refresh with Before After Callback
110+
Widget _refreshWithBeforeAfterCallback() {
111+
return AnyRefreshableWidget.single(
112+
onBeforeRefresh: () {
113+
log('onBeforeRefresh');
114+
},
115+
onRefresh: () async {
116+
await Future.delayed(const Duration(seconds: 2));
117+
},
118+
onAfterRefresh: () {
119+
log('onAfterRefresh');
120+
},
121+
builder: (context, isLoading, error) {
122+
return Center(child: Text('Refresh with Before After Callback'));
123+
},
124+
);
125+
}

lib/any_refreshable_widget.dart

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
1+
import 'dart:async';
12
import 'package:flutter/material.dart';
23

4+
/// A type-safe callback that can handle both synchronous and asynchronous operations.
5+
///
6+
/// This typedef uses FutureOr<void> which is the proper way to represent a function
7+
/// that can return either void (synchronous) or Future<void> (asynchronous).
8+
/// FutureOr is part of Dart's type system and provides better type safety than dynamic.
9+
typedef FlexibleCallback = FutureOr<void> Function();
10+
11+
/// Defines how multiple futures should be executed during refresh.
12+
enum RefreshConcurrency {
13+
/// Execute all futures concurrently (in parallel).
14+
/// This is the default and fastest option as all operations run simultaneously.
15+
concurrent,
16+
17+
/// Execute futures sequentially (one after another).
18+
/// This is useful when futures depend on each other or when you want to limit resource usage.
19+
sequential,
20+
}
21+
322
/// A widget that provides pull-to-refresh functionality for any content.
423
///
524
/// This widget can handle multiple futures, has customizable refresh indicator,
@@ -186,14 +205,24 @@ class _RefreshWidgetState extends State<_RefreshWidget>
186205
/// concurrently and provides unified loading and error states. It extends
187206
/// [ChangeNotifier] to notify listeners when the state changes.
188207
///
189-
/// The handler automatically executes all futures using [Future.wait] to
190-
/// ensure they run concurrently rather than sequentially. If any future
191-
/// throws an error, the error is captured and made available through the
192-
/// [error] getter.
208+
/// The handler executes all futures according to the specified concurrency mode.
209+
/// By default, futures run sequentially, but can be configured
210+
/// to run concurrently. If any future throws an error, the error is captured and
211+
/// made available through the [error] getter.
193212
class _MultiFutureRefreshHandler<T> extends ChangeNotifier {
194213
/// List of future functions to execute when refreshing.
195214
final List<Future<void> Function()> _futureFunctions;
196215

216+
/// Callback function called before starting the refresh operation.
217+
/// Can be either synchronous (void) or asynchronous (Future<void>).
218+
final FlexibleCallback? _onBeforeRefresh;
219+
220+
/// Callback function called after completing the refresh operation.
221+
final VoidCallback? _onAfterRefresh;
222+
223+
/// How the futures should be executed (concurrent or sequential).
224+
final RefreshConcurrency _concurrency;
225+
197226
/// Whether the futures are currently being executed.
198227
bool _isLoading = false;
199228

@@ -206,24 +235,46 @@ class _MultiFutureRefreshHandler<T> extends ChangeNotifier {
206235
/// Creates a [_MultiFutureRefreshHandler] with the given future functions.
207236
///
208237
/// [futureFunctions] is a list of functions that return futures to be
209-
/// executed concurrently when [initialize] or [refresh] is called.
210-
_MultiFutureRefreshHandler(List<Future<void> Function()> futureFunctions)
211-
: _futureFunctions = futureFunctions;
238+
/// executed when [initialize] or [refresh] is called.
239+
/// [onBeforeRefresh] is called before starting the refresh operation.
240+
/// Can be either synchronous or asynchronous.
241+
/// [onAfterRefresh] is called after completing the refresh operation.
242+
/// [concurrency] determines whether futures are executed concurrently or sequentially.
243+
_MultiFutureRefreshHandler(
244+
List<Future<void> Function()> futureFunctions, {
245+
FlexibleCallback? onBeforeRefresh,
246+
VoidCallback? onAfterRefresh,
247+
RefreshConcurrency concurrency = RefreshConcurrency.concurrent,
248+
}) : _futureFunctions = futureFunctions,
249+
_onBeforeRefresh = onBeforeRefresh,
250+
_onAfterRefresh = onAfterRefresh,
251+
_concurrency = concurrency;
212252

213253
/// Refresh all futures by re-executing them.
214254
///
215-
/// This is an alias for [initialize] that provides a more semantic
216-
/// method name for refresh operations. It executes all futures
217-
/// and updates the loading and error states.
255+
/// This method executes all futures according to the specified concurrency mode.
256+
/// It executes all futures and updates the loading and error states.
218257
Future<void> refresh() async {
258+
// Call onBeforeRefresh callback before starting the refresh
259+
if (_onBeforeRefresh != null) {
260+
await _onBeforeRefresh!.call();
261+
}
262+
219263
_isLoading = true;
220264
_error = null;
221265
_safeNotifyListeners();
222266

223267
try {
224-
for (int i = 0; i < _futureFunctions.length; i++) {
225-
if (_disposed) return;
226-
await _futureFunctions[i]();
268+
if (_concurrency == RefreshConcurrency.concurrent) {
269+
// Execute all futures concurrently using Future.wait
270+
final futures = _futureFunctions.map((fn) => fn()).toList();
271+
await Future.wait(futures);
272+
} else {
273+
// Execute futures sequentially one by one
274+
for (int i = 0; i < _futureFunctions.length; i++) {
275+
if (_disposed) return;
276+
await _futureFunctions[i]();
277+
}
227278
}
228279
} catch (e) {
229280
if (!_disposed) {
@@ -233,6 +284,9 @@ class _MultiFutureRefreshHandler<T> extends ChangeNotifier {
233284
if (!_disposed) {
234285
_isLoading = false;
235286
_safeNotifyListeners();
287+
288+
// Call onAfterRefresh callback after completing the refresh
289+
_onAfterRefresh?.call();
236290
}
237291
}
238292
}
@@ -293,6 +347,19 @@ class AnyRefreshableWidget<T> extends StatefulWidget {
293347
/// return a [Future<void>] that completes when its operation is done.
294348
final List<Future<void> Function()> onRefresh;
295349

350+
/// Callback function called before starting the refresh operation.
351+
/// Can be either synchronous (void) or asynchronous (Future<void>).
352+
final FlexibleCallback? onBeforeRefresh;
353+
354+
/// Callback function called after completing the refresh operation.
355+
final VoidCallback? onAfterRefresh;
356+
357+
/// How the futures should be executed during refresh.
358+
///
359+
/// - [RefreshConcurrency.concurrent]: All futures execute simultaneously
360+
/// - [RefreshConcurrency.sequential] (default): Futures execute one after another
361+
final RefreshConcurrency concurrency;
362+
296363
/// Builder function that creates the widget content.
297364
///
298365
/// Parameters:
@@ -351,7 +418,10 @@ class AnyRefreshableWidget<T> extends StatefulWidget {
351418
const AnyRefreshableWidget({
352419
super.key,
353420
required this.onRefresh,
421+
this.onBeforeRefresh,
422+
this.onAfterRefresh,
354423
required this.builder,
424+
this.concurrency = RefreshConcurrency.sequential,
355425
this.notificationPredicate,
356426
this.refreshColor,
357427
this.backgroundColor,
@@ -392,6 +462,8 @@ class AnyRefreshableWidget<T> extends StatefulWidget {
392462
AnyRefreshableWidget.single({
393463
super.key,
394464
required Future<void> Function() onRefresh,
465+
FlexibleCallback? onBeforeRefresh,
466+
VoidCallback? onAfterRefresh,
395467
required Widget Function(BuildContext, bool, Object?) builder,
396468
this.notificationPredicate,
397469
this.refreshColor,
@@ -405,20 +477,30 @@ class AnyRefreshableWidget<T> extends StatefulWidget {
405477
await onRefresh();
406478
},
407479
],
480+
onBeforeRefresh = onBeforeRefresh,
481+
onAfterRefresh = onAfterRefresh,
482+
concurrency = RefreshConcurrency
483+
.sequential, // Single future doesn't need concurrency option
408484
builder =
409485
((context, isLoading, error) => builder(context, isLoading, error));
410486

411487
@override
412-
State<AnyRefreshableWidget<T>> createState() => _RefreshableWidgetState<T>();
488+
State<AnyRefreshableWidget<T>> createState() =>
489+
_AnyRefreshableWidgetState<T>();
413490
}
414491

415-
class _RefreshableWidgetState<T> extends State<AnyRefreshableWidget<T>> {
492+
class _AnyRefreshableWidgetState<T> extends State<AnyRefreshableWidget<T>> {
416493
late _MultiFutureRefreshHandler<T> _handler;
417494

418495
@override
419496
void initState() {
420497
super.initState();
421-
_handler = _MultiFutureRefreshHandler<T>(widget.onRefresh);
498+
_handler = _MultiFutureRefreshHandler<T>(
499+
widget.onRefresh,
500+
onBeforeRefresh: widget.onBeforeRefresh,
501+
onAfterRefresh: widget.onAfterRefresh,
502+
concurrency: widget.concurrency,
503+
);
422504
_handler.addListener(_handleStateChange);
423505
}
424506

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: any_refreshable_widget
22
description: A powerful Flutter package providing pull-to-refresh functionality for any widget, with support for single/multiple futures, custom indicators, and comprehensive error handling.
3-
version: 0.0.2
3+
version: 0.1.0
44
homepage: https://github.com/Yama-Roni/any_refreshable_widget
55
repository: https://github.com/Yama-Roni/any_refreshable_widget
66
issue_tracker: https://github.com/Yama-Roni/any_refreshable_widget/issues

0 commit comments

Comments
 (0)