any_refreshable_widget 0.1.1 copy "any_refreshable_widget: ^0.1.1" to clipboard
any_refreshable_widget: ^0.1.1 copied to clipboard

A powerful Flutter package providing pull-to-refresh functionality for any widget, with support for single/multiple futures, custom indicators, and comprehensive error handling.

Any Refreshable Widget #

pub package pub points popularity likes

A powerful and flexible Flutter package that provides pull-to-refresh functionality for any widget, with support for single and multiple futures, custom indicators, and comprehensive error handling.

Any Refreshable Widget Demo

Features #

  • πŸ”„ Single & Multiple Future Support - Handle one or multiple asynchronous operations
  • 🎨 Customizable Refresh Indicator - Full control over appearance and behavior
  • πŸ“± Universal Widget Support - Works with any widget, automatically makes content scrollable
  • 🎯 Smart Error Handling - Comprehensive error states and callbacks
  • πŸ”§ Highly Configurable - Colors, displacement, stroke width, trigger modes, and more
  • ⚑ Lifecycle Callbacks - onBeforeRefresh and onAfterRefresh hooks with sync/async support
  • πŸ”€ Flexible Concurrency - Choose between concurrent (parallel) or sequential execution
  • πŸš€ Production Ready - Thoroughly tested and optimized for real-world applications

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  any_refreshable_widget: ^0.0.1

Then run:

flutter pub get

Quick Start #

Import the package #

import 'package:any_refreshable_widget/any_refreshable_widget.dart';

Basic Usage - Single Future #

AnyRefreshableWidget.single(
  onRefresh: () async {
    // Your refresh logic here
    await fetchUserData();
  },
  builder: (context, isLoading, error) {
    if (error != null) {
      return Center(child: Text('Error: $error'));
    }
    if (isLoading) {
      return const Center(child: CircularProgressIndicator());
    }
    return const Center(child: Text('Pull down to refresh!'));
  },
)

Advanced Usage - Multiple Futures #

AnyRefreshableWidget(
  onRefresh: [
    () => fetchUserData(),
    () => fetchNotifications(),
    () => fetchSettings(),
  ],
  builder: (context, isLoading, error) {
    if (error != null) {
      return ErrorWidget(error);
    }
    if (isLoading) {
      return const LoadingWidget();
    }
    return const ContentWidget();
  },
)

Concurrency Control #

Control how multiple futures are executed:

Concurrent Execution (Default)

AnyRefreshableWidget(
  concurrency: RefreshConcurrency.concurrent,
  onRefresh: [
    () => fetchUserData(),      // These run simultaneously
    () => fetchNotifications(), // for faster completion
    () => fetchSettings(),
  ],
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

Sequential Execution

AnyRefreshableWidget(
  concurrency: RefreshConcurrency.sequential, // Default
  onRefresh: [
    () => authenticateUser(),   // Runs first
    () => fetchUserData(),      // Then this
    () => fetchNotifications(), // Finally this
  ],
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

Comprehensive Examples #

Custom Refresh Indicator #

AnyRefreshableWidget.single(
  onRefresh: () => performRefresh(),
  refreshColor: Colors.blue,
  backgroundColor: Colors.white,
  displacement: 60.0,
  strokeWidth: 3.0,
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

Custom Indicator Widget #

AnyRefreshableWidget.single(
  onRefresh: () => performRefresh(),
  customIndicator: Container(
    padding: const EdgeInsets.all(16),
    child: const Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        CircularProgressIndicator(strokeWidth: 2),
        SizedBox(width: 16),
        Text('Refreshing...'),
      ],
    ),
  ),
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

Error Handling #

AnyRefreshableWidget.single(
  onRefresh: () async {
    // Simulate potential error
    if (Random().nextBool()) {
      throw Exception('Network error occurred');
    }
    await fetchData();
  },
  builder: (context, isLoading, error) {
    if (error != null) {
      return Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Icon(Icons.error, color: Colors.red, size: 48),
            const SizedBox(height: 16),
            Text('Error: ${error.toString()}'),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                // Trigger refresh programmatically
              },
              child: const Text('Retry'),
            ),
          ],
        ),
      );
    }
    
    if (isLoading) {
      return const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircularProgressIndicator(),
            SizedBox(height: 16),
            Text('Loading...'),
          ],
        ),
      );
    }
    
    return YourDataWidget();
  },
)

With ListView #

AnyRefreshableWidget.single(
  onRefresh: () => refreshListData(),
  builder: (context, isLoading, error) {
    if (error != null) return ErrorWidget(error);
    
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(items[index].title),
          subtitle: Text(items[index].description),
        );
      },
    );
  },
)

Lifecycle Callbacks #

The package supports onBeforeRefresh and onAfterRefresh callbacks that can be either synchronous or asynchronous:

Synchronous Callbacks

AnyRefreshableWidget.single(
  onBeforeRefresh: () {
    print('Starting refresh...');
    // Synchronous setup logic
  },
  onRefresh: () => fetchData(),
  onAfterRefresh: () {
    print('Refresh completed!');
    // Synchronous cleanup logic
  },
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

Asynchronous Callbacks

AnyRefreshableWidget.single(
  onBeforeRefresh: () async {
    print('Starting refresh...');
    await prepareForRefresh();
    // Asynchronous setup logic
  },
  onRefresh: () => fetchData(),
  onAfterRefresh: () {
    print('Refresh completed!');
    // Cleanup logic (always sync)
  },
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

API Reference #

AnyRefreshableWidget #

Parameter Type Required Default Description
onRefresh List<Future<void> Function()> βœ… - List of async functions to execute on refresh
builder Widget Function(BuildContext, bool, Object?) βœ… - Builder function with loading and error states
concurrency RefreshConcurrency ❌ concurrent How futures should be executed (concurrent/sequential)
onBeforeRefresh FutureOr<void> Function()? ❌ null Callback executed before refresh starts (sync/async)
onAfterRefresh VoidCallback? ❌ null Callback executed after refresh completes
refreshColor Color? ❌ null Color of the refresh indicator
backgroundColor Color? ❌ null Background color of the refresh indicator
displacement double ❌ 40.0 Distance from top to show indicator
strokeWidth double ❌ 2.0 Stroke width of the progress indicator
customIndicator Widget? ❌ null Custom refresh indicator widget
triggerMode RefreshIndicatorTriggerMode? ❌ anywhere When the indicator should trigger
notificationPredicate bool Function(ScrollNotification)? ❌ null Custom scroll notification predicate

AnyRefreshableWidget.single #

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.

RefreshConcurrency Enum #

Value Description Use Case
RefreshConcurrency.concurrent Execute all futures simultaneously using Future.wait fastest refresh when futures are independent
RefreshConcurrency.sequential Execute futures one by one in order When futures depend on each other or to limit resource usage

Callback Execution Order #

When a refresh is triggered, the callbacks execute in this order:

  1. onBeforeRefresh - Called first, awaited if async
  2. Loading state - isLoading becomes true, UI updates
  3. onRefresh - All futures execute concurrently
  4. Loading state - isLoading becomes false, UI updates
  5. onAfterRefresh - Called last, always synchronous

Advanced Configuration #

Custom Scroll Notification Predicate #

AnyRefreshableWidget.single(
  onRefresh: () => performRefresh(),
  notificationPredicate: (ScrollNotification notification) {
    // Custom logic to determine when refresh should trigger
    return notification.depth == 0 && notification.metrics.pixels <= 0;
  },
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

Trigger Modes #

AnyRefreshableWidget.single(
  onRefresh: () => performRefresh(),
  triggerMode: RefreshIndicatorTriggerMode.onEdge, // or .anywhere
  builder: (context, isLoading, error) {
    return YourContentWidget();
  },
)

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

Issues #

If you encounter any issues or have suggestions, please file them in the GitHub Issues.

Changelog #

See CHANGELOG.md for a detailed changelog.


Made with ❀️ by Yama-Roni

6
likes
160
points
369
downloads
screenshot

Publisher

verified publisherlogique.co.id

Weekly Downloads

A powerful Flutter package providing pull-to-refresh functionality for any widget, with support for single/multiple futures, custom indicators, and comprehensive error handling.

Repository (GitHub)
View/report issues

Topics

#flutter #widget #refresh #pull-to-refresh #ui

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on any_refreshable_widget