{"id":32268776,"url":"https://github.com/yama-roni/any_refreshable_widget","last_synced_at":"2026-02-22T18:01:26.882Z","repository":{"id":309836036,"uuid":"1037702608","full_name":"Yama-Roni/any_refreshable_widget","owner":"Yama-Roni","description":"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.","archived":false,"fork":false,"pushed_at":"2025-09-17T03:55:15.000Z","size":395,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-17T05:45:56.954Z","etag":null,"topics":["dart","flutter","mobile-component","pull-to-refresh","refresh"],"latest_commit_sha":null,"homepage":"https://www.logique.co.id","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Yama-Roni.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-14T01:53:26.000Z","updated_at":"2025-09-17T03:52:43.000Z","dependencies_parsed_at":"2025-09-17T05:33:57.657Z","dependency_job_id":"2f24a4fc-f9e5-4071-b102-325e0e6dab98","html_url":"https://github.com/Yama-Roni/any_refreshable_widget","commit_stats":null,"previous_names":["yama-roni/refreshable-widget","yama-roni/any_refreshable_widget"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/Yama-Roni/any_refreshable_widget","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yama-Roni%2Fany_refreshable_widget","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yama-Roni%2Fany_refreshable_widget/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yama-Roni%2Fany_refreshable_widget/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yama-Roni%2Fany_refreshable_widget/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Yama-Roni","download_url":"https://codeload.github.com/Yama-Roni/any_refreshable_widget/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yama-Roni%2Fany_refreshable_widget/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29721046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T15:10:41.462Z","status":"ssl_error","status_checked_at":"2026-02-22T15:10:04.636Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","mobile-component","pull-to-refresh","refresh"],"created_at":"2025-10-22T22:17:50.099Z","updated_at":"2026-02-22T18:01:26.865Z","avatar_url":"https://github.com/Yama-Roni.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Any Refreshable Widget\n\n[![pub package](https://img.shields.io/pub/v/any_refreshable_widget.svg)](https://pub.dev/packages/any_refreshable_widget)\n[![pub points](https://img.shields.io/pub/points/any_refreshable_widget)](https://pub.dev/packages/any_refreshable_widget/score)\n[![popularity](https://img.shields.io/pub/popularity/any_refreshable_widget)](https://pub.dev/packages/any_refreshable_widget/score)\n[![likes](https://img.shields.io/pub/likes/any_refreshable_widget)](https://pub.dev/packages/any_refreshable_widget/score)\n\nA 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.\n\n![Any Refreshable Widget Demo](https://raw.githubusercontent.com/Yama-Roni/any_refreshable_widget/main/snapshot/example.gif)\n\n## Features\n\n- 🔄 **Single \u0026 Multiple Future Support** - Handle one or multiple asynchronous operations\n- 🎮 **Controller Support** - Programmatic refresh triggering with state management\n- 🎨 **Customizable Refresh Indicator** - Full control over appearance and behavior\n- 📱 **Universal Widget Support** - Works with any widget, automatically makes content scrollable\n- 🎯 **Smart Error Handling** - Comprehensive error states and callbacks\n- 🔧 **Highly Configurable** - Colors, displacement, stroke width, trigger modes, and more\n- ⚡ **Lifecycle Callbacks** - `onBeforeRefresh` and `onAfterRefresh` hooks with sync/async support\n- 🔀 **Flexible Concurrency** - Choose between concurrent (parallel) or sequential execution\n- 🚀 **Production Ready** - Thoroughly tested and optimized for real-world applications\n\n## Installation\n\nAdd this to your package's `pubspec.yaml` file:\n\n```yaml\ndependencies:\n  any_refreshable_widget: ^0.2.0\n```\n\nThen run:\n\n```bash\nflutter pub get\n```\n\n## Quick Start\n\n### Import the package\n\n```dart\nimport 'package:any_refreshable_widget/any_refreshable_widget.dart';\n```\n\n### Basic Usage - Single Future\n\n```dart\nAnyRefreshableWidget.single(\n  onRefresh: () async {\n    // Your refresh logic here\n    await fetchUserData();\n  },\n  builder: (context, isLoading, error) {\n    if (error != null) {\n      return Center(child: Text('Error: $error'));\n    }\n    if (isLoading) {\n      return const Center(child: CircularProgressIndicator());\n    }\n    return const Center(child: Text('Pull down to refresh!'));\n  },\n)\n```\n\n### Advanced Usage - Multiple Futures\n\n```dart\nAnyRefreshableWidget(\n  onRefresh: [\n    () =\u003e fetchUserData(),\n    () =\u003e fetchNotifications(),\n    () =\u003e fetchSettings(),\n  ],\n  builder: (context, isLoading, error) {\n    if (error != null) {\n      return ErrorWidget(error);\n    }\n    if (isLoading) {\n      return const LoadingWidget();\n    }\n    return const ContentWidget();\n  },\n)\n```\n\n### Controller State Management\n\nAccess refresh state and errors through the controller:\n\n```dart\nfinal controller = AnyRefreshableController();\n\n// Check if controller is attached to a widget\nif (controller.isAttached) {\n  // Check if refresh is in progress\n  bool isRefreshing = controller.isRefreshing;\n  \n  // Get current error, if any\n  Object? currentError = controller.error;\n  \n  // Trigger refresh programmatically\n  await controller.refresh();\n}\n```\n\n### Concurrency Control\n\nControl how multiple futures are executed:\n\n#### Concurrent Execution (Default)\n```dart\nAnyRefreshableWidget(\n  concurrency: RefreshConcurrency.concurrent,\n  onRefresh: [\n    () =\u003e fetchUserData(),      // These run simultaneously\n    () =\u003e fetchNotifications(), // for faster completion\n    () =\u003e fetchSettings(),\n  ],\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n#### Sequential Execution\n```dart\nAnyRefreshableWidget(\n  concurrency: RefreshConcurrency.sequential, // Default\n  onRefresh: [\n    () =\u003e authenticateUser(),   // Runs first\n    () =\u003e fetchUserData(),      // Then this\n    () =\u003e fetchNotifications(), // Finally this\n  ],\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n## Comprehensive Examples\n\n### Custom Refresh Indicator\n\n```dart\nAnyRefreshableWidget.single(\n  onRefresh: () =\u003e performRefresh(),\n  refreshColor: Colors.blue,\n  backgroundColor: Colors.white,\n  displacement: 60.0,\n  strokeWidth: 3.0,\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n### Custom Indicator Widget\n\n```dart\nAnyRefreshableWidget.single(\n  onRefresh: () =\u003e performRefresh(),\n  customIndicator: Container(\n    padding: const EdgeInsets.all(16),\n    child: const Row(\n      mainAxisAlignment: MainAxisAlignment.center,\n      children: [\n        CircularProgressIndicator(strokeWidth: 2),\n        SizedBox(width: 16),\n        Text('Refreshing...'),\n      ],\n    ),\n  ),\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n### Error Handling\n\n```dart\nAnyRefreshableWidget.single(\n  onRefresh: () async {\n    // Simulate potential error\n    if (Random().nextBool()) {\n      throw Exception('Network error occurred');\n    }\n    await fetchData();\n  },\n  builder: (context, isLoading, error) {\n    if (error != null) {\n      return Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            const Icon(Icons.error, color: Colors.red, size: 48),\n            const SizedBox(height: 16),\n            Text('Error: ${error.toString()}'),\n            const SizedBox(height: 16),\n            ElevatedButton(\n              onPressed: () {\n                // Trigger refresh programmatically\n              },\n              child: const Text('Retry'),\n            ),\n          ],\n        ),\n      );\n    }\n    \n    if (isLoading) {\n      return const Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            CircularProgressIndicator(),\n            SizedBox(height: 16),\n            Text('Loading...'),\n          ],\n        ),\n      );\n    }\n    \n    return YourDataWidget();\n  },\n)\n```\n\n### With ListView\n\n```dart\nAnyRefreshableWidget.single(\n  onRefresh: () =\u003e refreshListData(),\n  builder: (context, isLoading, error) {\n    if (error != null) return ErrorWidget(error);\n    \n    return ListView.builder(\n      itemCount: items.length,\n      itemBuilder: (context, index) {\n        return ListTile(\n          title: Text(items[index].title),\n          subtitle: Text(items[index].description),\n        );\n      },\n    );\n  },\n)\n```\n\n### Lifecycle Callbacks\n\nThe package supports `onBeforeRefresh` and `onAfterRefresh` callbacks that can be either synchronous or asynchronous:\n\n#### Synchronous Callbacks\n```dart\nAnyRefreshableWidget.single(\n  onBeforeRefresh: () {\n    print('Starting refresh...');\n    // Synchronous setup logic\n  },\n  onRefresh: () =\u003e fetchData(),\n  onAfterRefresh: () {\n    print('Refresh completed!');\n    // Synchronous cleanup logic\n  },\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n#### Asynchronous Callbacks\n```dart\nAnyRefreshableWidget.single(\n  onBeforeRefresh: () async {\n    print('Starting refresh...');\n    await prepareForRefresh();\n    // Asynchronous setup logic\n  },\n  onRefresh: () =\u003e fetchData(),\n  onAfterRefresh: () {\n    print('Refresh completed!');\n    // Cleanup logic (always sync)\n  },\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n## API Reference\n\n### AnyRefreshableWidget\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `onRefresh` | `List\u003cFuture\u003cvoid\u003e Function()\u003e` | ✅ | - | List of async functions to execute on refresh |\n| `builder` | `Widget Function(BuildContext, bool, Object?)` | ✅ | - | Builder function with loading and error states |\n| `controller` | `AnyRefreshableController?` | ❌ | `null` | Controller for programmatic refresh triggering |\n| `concurrency` | `RefreshConcurrency` | ❌ | `sequential` | How futures should be executed (concurrent/sequential) |\n| `onBeforeRefresh` | `FutureOr\u003cvoid\u003e Function()?` | ❌ | `null` | Callback executed before refresh starts (sync/async) |\n| `onAfterRefresh` | `VoidCallback?` | ❌ | `null` | Callback executed after refresh completes |\n| `refreshColor` | `Color?` | ❌ | `null` | Color of the refresh indicator |\n| `backgroundColor` | `Color?` | ❌ | `null` | Background color of the refresh indicator |\n| `displacement` | `double` | ❌ | `40.0` | Distance from top to show indicator |\n| `strokeWidth` | `double` | ❌ | `2.0` | Stroke width of the progress indicator |\n| `customIndicator` | `Widget?` | ❌ | `null` | Custom refresh indicator widget |\n| `triggerMode` | `RefreshIndicatorTriggerMode?` | ❌ | `anywhere` | When the indicator should trigger |\n| `notificationPredicate` | `bool Function(ScrollNotification)?` | ❌ | `null` | Custom scroll notification predicate |\n\n### AnyRefreshableWidget.single\n\nSame parameters as `AnyRefreshableWidget`, but `onRefresh` takes a single `Future\u003cvoid\u003e Function()` instead of a list. The `concurrency` parameter is not applicable for single futures.\n\n### RefreshConcurrency Enum\n\n| Value | Description | Use Case |\n|-------|-------------|----------|\n| `RefreshConcurrency.concurrent` | Execute all futures simultaneously using `Future.wait` | fastest refresh when futures are independent |\n| `RefreshConcurrency.sequential` | Execute futures one by one in order | When futures depend on each other or to limit resource usage |\n\n## Callback Execution Order\n\nWhen a refresh is triggered, the callbacks execute in this order:\n\n1. **`onBeforeRefresh`** - Called first, awaited if async\n2. **Loading state** - `isLoading` becomes `true`, UI updates\n3. **`onRefresh`** - All futures execute concurrently\n4. **Loading state** - `isLoading` becomes `false`, UI updates  \n5. **`onAfterRefresh`** - Called last, always synchronous\n\n## Advanced Configuration\n\n### Custom Scroll Notification Predicate\n\n```dart\nAnyRefreshableWidget.single(\n  onRefresh: () =\u003e performRefresh(),\n  notificationPredicate: (ScrollNotification notification) {\n    // Custom logic to determine when refresh should trigger\n    return notification.depth == 0 \u0026\u0026 notification.metrics.pixels \u003c= 0;\n  },\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n### Trigger Modes\n\n```dart\nAnyRefreshableWidget.single(\n  onRefresh: () =\u003e performRefresh(),\n  triggerMode: RefreshIndicatorTriggerMode.onEdge, // or .anywhere\n  builder: (context, isLoading, error) {\n    return YourContentWidget();\n  },\n)\n```\n\n## Contributing\n\nContributions 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.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Issues\n\nIf you encounter any issues or have suggestions, please file them in the [GitHub Issues](https://github.com/Yama-Roni/any_refreshable_widget/issues).\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for a detailed changelog.\n\n---\n\nMade with ❤️ by [Yama-Roni](https://github.com/Yama-Roni)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyama-roni%2Fany_refreshable_widget","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyama-roni%2Fany_refreshable_widget","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyama-roni%2Fany_refreshable_widget/lists"}