{"id":19273034,"url":"https://github.com/fluttercandies/pull_to_refresh_notification","last_synced_at":"2025-04-06T20:12:18.568Z","repository":{"id":45058239,"uuid":"170714556","full_name":"fluttercandies/pull_to_refresh_notification","owner":"fluttercandies","description":"Flutter plugin for building pull to refresh effects with PullToRefreshNotification and PullToRefreshContainer quickly.","archived":false,"fork":false,"pushed_at":"2024-06-24T10:16:29.000Z","size":3167,"stargazers_count":188,"open_issues_count":8,"forks_count":32,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-30T18:08:36.697Z","etag":null,"topics":["flutter","pull-to-refresh"],"latest_commit_sha":null,"homepage":null,"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/fluttercandies.png","metadata":{"files":{"readme":"README-ZH.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"custom":"http://zmtzawqlp.gitee.io/my_images/images/qrcode.png"}},"created_at":"2019-02-14T15:36:28.000Z","updated_at":"2024-12-16T17:41:26.000Z","dependencies_parsed_at":"2024-06-21T03:52:12.798Z","dependency_job_id":"6a51f46c-499e-4c71-87f1-2fe7db3849c4","html_url":"https://github.com/fluttercandies/pull_to_refresh_notification","commit_stats":{"total_commits":38,"total_committers":2,"mean_commits":19.0,"dds":0.02631578947368418,"last_synced_commit":"163844bf0a8ba09c2c056b324aef01e77f95c0ff"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fpull_to_refresh_notification","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fpull_to_refresh_notification/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fpull_to_refresh_notification/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluttercandies%2Fpull_to_refresh_notification/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fluttercandies","download_url":"https://codeload.github.com/fluttercandies/pull_to_refresh_notification/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247543593,"owners_count":20955865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["flutter","pull-to-refresh"],"created_at":"2024-11-09T20:40:22.718Z","updated_at":"2025-04-06T20:12:18.540Z","avatar_url":"https://github.com/fluttercandies.png","language":"Dart","readme":"# pull_to_refresh_notification\n\n[![pub package](https://img.shields.io/pub/v/pull_to_refresh_notification.svg)](https://pub.dartlang.org/packages/pull_to_refresh_notification) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/pull_to_refresh_notification)](https://github.com/fluttercandies/pull_to_refresh_notification/stargazers) [![GitHub forks](https://img.shields.io/github/forks/fluttercandies/pull_to_refresh_notification)](https://github.com/fluttercandies/pull_to_refresh_notification/network)  [![GitHub license](https://img.shields.io/github/license/fluttercandies/pull_to_refresh_notification)](https://github.com/fluttercandies/pull_to_refresh_notification/blob/master/LICENSE)  [![GitHub issues](https://img.shields.io/github/issues/fluttercandies/pull_to_refresh_notification)](https://github.com/fluttercandies/pull_to_refresh_notification/issues) \u003ca target=\"_blank\" href=\"https://jq.qq.com/?_wv=1027\u0026k=5bcc0gy\"\u003e\u003cimg border=\"0\" src=\"https://pub.idqqimg.com/wpa/images/group.png\" alt=\"flutter-candies\" title=\"flutter-candies\"\u003e\u003c/a\u003e\n\n文档语言: [English](README.md) | [中文简体](README-ZH.md)\n\n自定义下拉刷新动画.\n\n[Web demo for PullToRefreshNotification](https://fluttercandies.github.io/pull_to_refresh_notification/)\n\n[掘金](https://juejin.im/post/5bebcc44f265da61682aedb8)\n\n- [pull_to_refresh_notification](#pull_to_refresh_notification)\n- [RefreshIndicatorMode](#refreshindicatormode)\n- [Sample 1 appbar](#sample-1-appbar)\n- [Sample 2 header](#sample-2-header)\n- [Sample 3 image](#sample-3-image)\n- [Sample 4 candies](#sample-4-candies)\n- [Sample 5 candies](#sample-5-candies)\n- [refresh with code](#refresh-with-code)\n- [☕️Buy me a coffee](#️buy-me-a-coffee)\n\n# RefreshIndicatorMode\n\n```dart\nenum RefreshIndicatorMode {\n  drag, // Pointer is down.\n  armed, // Dragged far enough that an up event will run the onRefresh callback.\n  snap, // Animating to the indicator's final \"displacement\".\n  refresh, // Running the refresh callback.\n  done, // Animating the indicator's fade-out after refreshing.\n  canceled, // Animating the indicator's fade-out after not arming.\n  error, //refresh failed\n}\n```\n\n# Sample 1 appbar\n创建一个Appbar的下拉刷新动画\n\n![](https://github.com/fluttercandies/Flutter_Candies/tree/master/gif/pull_to_refresh/appbar.gif)\n```dart\n   Widget build(BuildContext context) {\n return PullToRefreshNotification(\n      color: Colors.blue,\n      pullBackOnRefresh: true,\n      onRefresh: onRefresh,\n      child: CustomScrollView(\n        slivers: \u003cWidget\u003e[\n          PullToRefreshContainer(buildPulltoRefreshAppbar),\n          SliverList(\n              delegate:\n                  SliverChildBuilderDelegate((BuildContext context, int index) {\n            return Container(\n                padding: EdgeInsets.only(bottom: 4.0),\n                child: Column(\n                  children: \u003cWidget\u003e[\n                    Text(\n                      \"List item : ${listlength - index}\",\n                      style: TextStyle(fontSize: 15.0, inherit: false),\n                    ),\n                    Divider(\n                      color: Colors.grey,\n                      height: 2.0,\n                    )\n                  ],\n                ));\n          }, childCount: listlength)),\n        ],\n      ),\n    );\n}\n    \n     Widget buildPulltoRefreshAppbar(PullToRefreshScrollNotificationInfo info) {\n        var action = Padding(\n          child: info?.refreshWidget ?? Icon(Icons.more_horiz),\n          padding: EdgeInsets.all(15.0),\n        );\n        var offset = info?.dragOffset ?? 0.0;\n        return SliverAppBar(\n            pinned: true,\n            title: Text(\"PullToRefreshAppbar\"),\n            centerTitle: true,\n            expandedHeight: 200.0 + offset,\n            actions: \u003cWidget\u003e[action],\n            flexibleSpace: FlexibleSpaceBar(\n                //centerTitle: true,\n                title: Text(\n                  info?.mode?.toString() ?? \"\",\n                  style: TextStyle(fontSize: 10.0),\n                ),\n                collapseMode: CollapseMode.pin,\n                background: Image.asset(\n                  \"assets/467141054.jpg\",\n                  //fit: offset \u003e 0.0 ? BoxFit.cover : BoxFit.fill,\n                  fit: BoxFit.cover,\n                )));\n      }\n```\n\n# Sample 2 header\n创建下拉刷新头，你可以轻松处理状态\n\n![](https://github.com/fluttercandies/Flutter_Candies/tree/master/gif/pull_to_refresh/header.gif)\n```dart\n  Widget build(BuildContext context) {\n    return PullToRefreshNotification(\n       color: Colors.blue,\n       onRefresh: onRefresh,\n       maxDragOffset: 80.0,\n       child: CustomScrollView(\n         slivers: \u003cWidget\u003e[\n           SliverAppBar(\n             pinned: true,\n             title: Text(\"PullToRefreshHeader\"),\n           ),\n           PullToRefreshContainer(buildPulltoRefreshHeader),\n           SliverList(\n               delegate:\n                   SliverChildBuilderDelegate((BuildContext context, int index) {\n             return Container(\n                 padding: EdgeInsets.only(bottom: 4.0),\n                 child: Column(\n                   children: \u003cWidget\u003e[\n                     Text(\n                       \"List item : ${listlength - index}\",\n                       style: TextStyle(fontSize: 15.0, inherit: false),\n                     ),\n                     Divider(\n                       color: Colors.grey,\n                       height: 2.0,\n                     )\n                   ],\n                 ));\n           }, childCount: listlength)),\n         ],\n       ),\n     );\n   }\n \n   Widget buildPulltoRefreshHeader(PullToRefreshScrollNotificationInfo info) {\n\n     var offset = info?.dragOffset ?? 0.0;\n     var mode = info?.mode;\n     Widget refreshWidget = Container();\n     //it should more than 18, so that RefreshProgressIndicator can be shown fully\n     if (info?.refreshWidget != null \u0026\u0026\n         offset \u003e 18.0 \u0026\u0026\n         mode != RefreshIndicatorMode.error) {\n       refreshWidget = info.refreshWidget;\n     }\n \n     Widget child = null;\n     if (mode == RefreshIndicatorMode.error) {\n       child = GestureDetector(\n           onTap: () {\n             // refreshNotification;\n             info?.pullToRefreshNotificationState?.show();\n           },\n           child: Container(\n             color: Colors.grey,\n             alignment: Alignment.bottomCenter,\n             height: offset,\n             width: double.infinity,\n             //padding: EdgeInsets.only(top: offset),\n             child: Container(\n               padding: EdgeInsets.only(left: 5.0),\n               alignment: Alignment.center,\n               child: Text(\n                 mode?.toString() + \"  click to retry\" ?? \"\",\n                 style: TextStyle(fontSize: 12.0, inherit: false),\n               ),\n             ),\n           ));\n     } else {\n       child = Container(\n         color: Colors.grey,\n         alignment: Alignment.bottomCenter,\n         height: offset,\n         width: double.infinity,\n         //padding: EdgeInsets.only(top: offset),\n         child: Row(\n           mainAxisAlignment: MainAxisAlignment.center,\n           children: \u003cWidget\u003e[\n             refreshWidget,\n             Container(\n               padding: EdgeInsets.only(left: 5.0),\n               alignment: Alignment.center,\n               child: Text(\n                 mode?.toString() ?? \"\",\n                 style: TextStyle(fontSize: 12.0, inherit: false),\n               ),\n             )\n           ],\n         ),\n       );\n     }\n \n     return SliverToBoxAdapter(\n       child: child,\n     );\n   }\n \n```\n\n# Sample 3 image\n\n创建一个图片缩放的下拉刷新动画\n\n![](https://github.com/fluttercandies/Flutter_Candies/tree/master/gif/pull_to_refresh/image.gif)\n```dart\n Widget build(BuildContext context) {\n     return PullToRefreshNotification(\n       color: Colors.blue,\n       pullBackOnRefresh: true,\n       onRefresh: onRefresh,\n       child: CustomScrollView(\n         slivers: \u003cWidget\u003e[\n           SliverAppBar(\n             title: Text(\"PullToRefreshImage\"),\n           ),\n           PullToRefreshContainer(buildPulltoRefreshImage),\n           SliverList(\n               delegate:\n                   SliverChildBuilderDelegate((BuildContext context, int index) {\n             return Container(\n                 padding: EdgeInsets.only(bottom: 4.0),\n                 child: Column(\n                   children: \u003cWidget\u003e[\n                     Text(\n                       \"List item : ${listlength - index}\",\n                       style: TextStyle(fontSize: 15.0, inherit: false),\n                     ),\n                     Divider(\n                       color: Colors.grey,\n                       height: 2.0,\n                     )\n                   ],\n                 ));\n           }, childCount: listlength)),\n         ],\n       ),\n     );\n   }\n   \n   Widget buildPulltoRefreshImage(PullToRefreshScrollNotificationInfo info) {\n     var offset = info?.dragOffset ?? 0.0;\n     Widget refreshWidget = Container();\n     if (info?.refreshWidget != null) {\n       refreshWidget = Material(\n         type: MaterialType.circle,\n         color: Theme.of(context).canvasColor,\n         elevation: 2.0,\n         child: Padding(\n           padding: EdgeInsets.all(12),\n           child: info.refreshWidget,\n         ),\n       );\n     }\n \n     return SliverToBoxAdapter(\n       child: Stack(\n         alignment: Alignment.center,\n         children: \u003cWidget\u003e[\n           Container(\n               height: 200.0 + offset,\n               width: double.infinity,\n               child: Image.asset(\n                 \"assets/467141054.jpg\",\n                 //fit: offset \u003e 0.0 ? BoxFit.cover : BoxFit.fill,\n                 fit: BoxFit.cover,\n               )),\n           Center(\n             child: Row(\n               mainAxisAlignment: MainAxisAlignment.center,\n               children: \u003cWidget\u003e[\n                 refreshWidget,\n                 Container(\n                   padding: EdgeInsets.only(left: 5.0),\n                   alignment: Alignment.center,\n                   child: Text(\n                     info?.mode?.toString() ?? \"\",\n                     style: TextStyle(fontSize: 12.0, inherit: false),\n                   ),\n                 )\n               ],\n             ),\n           )\n         ],\n       ),\n     );\n   }\n```\n\n# Sample 4 candies\n\n创建一个棒棒糖下拉刷新动画\n\n![](https://github.com/fluttercandies/Flutter_Candies/tree/master/gif/pull_to_refresh/candies.gif)\n```dart\n  Widget build(BuildContext context) {\n    return Material(\n      child: Stack(\n        children: \u003cWidget\u003e[\n          PullToRefreshNotification(\n            color: Colors.blue,\n            onRefresh: onRefresh,\n            maxDragOffset: maxDragOffset,\n            armedDragUpCancel: false,\n            key: key,\n            child: CustomScrollView(\n              ///in case list is not full screen and remove ios Bouncing\n              physics: AlwaysScrollableClampingScrollPhysics(),\n              slivers: \u003cWidget\u003e[\n                SliverAppBar(\n                  title: Text(\"PullToRefreshCandies\"),\n                ),\n                PullToRefreshContainer((info) {\n                  var offset = info?.dragOffset ?? 0.0;\n                  Widget child = Container(\n                    alignment: Alignment.center,\n                    height: offset,\n                    width: double.infinity,\n                    child: RefreshLogo(\n                      mode: info?.mode,\n                      offset: offset,\n                    ),\n                  );\n\n                  return SliverToBoxAdapter(\n                    child: child,\n                  );\n                }),\n                SliverList(\n                    delegate: SliverChildBuilderDelegate(\n                        (BuildContext context, int index) {\n                  return Container(\n                      padding: EdgeInsets.only(bottom: 4.0),\n                      child: Column(\n                        children: \u003cWidget\u003e[\n                          Text(\n                            \"List item : ${listlength - index}\",\n                            style: TextStyle(fontSize: 15.0),\n                          ),\n                          Divider(\n                            color: Colors.grey,\n                            height: 2.0,\n                          )\n                        ],\n                      ));\n                }, childCount: listlength)),\n              ],\n            ),\n          ),\n          Positioned(\n            right: 20.0,\n            bottom: 20.0,\n            child: FloatingActionButton(\n              child: Icon(Icons.refresh),\n              onPressed: () {\n                key.currentState.show(notificationDragOffset: maxDragOffset);\n              },\n            ),\n          )\n        ],\n      ),\n    );\n  }\n```\n\n# Sample 5 candies\n在一个反转的列表里面怎么实现下拉刷新(比如聊天列表)\n\n```dart\n        PullToRefreshNotification(\n          onRefresh: onRefresh,\n          maxDragOffset: 48,\n          armedDragUpCancel: false,\n          reverse: true,\n          child: Column(\n            children: \u003cWidget\u003e[\n              PullToRefreshContainer(\n                  (PullToRefreshScrollNotificationInfo info) {\n                final double offset = info?.dragOffset ?? 0.0;\n\n                //load history data\n                return Container(\n                  height: offset,\n                  child: const RefreshProgressIndicator(\n                    valueColor: AlwaysStoppedAnimation\u003cColor\u003e(Colors.blue),\n                    strokeWidth: 2.0,\n                  ),\n                );\n              }),\n              Expanded(\n                child: ExtendedListView.builder(\n                  ///in case list is not full screen and remove ios Bouncing\n                  physics: const AlwaysScrollableClampingScrollPhysics(),\n                  reverse: true,\n                  extendedListDelegate:\n                      const ExtendedListDelegate(closeToTrailing: true),\n                  itemBuilder: (BuildContext context, int index) {\n                    List\u003cWidget\u003e children = \u003cWidget\u003e[\n                      Text('$index. ${chats[index]}'),\n                      Image.asset(\n                        'assets/avatar.jpg',\n                        width: 30,\n                        height: 30,\n                      ),\n                    ];\n                    if (index % 2 == 0) {\n                      children = children.reversed.toList();\n                    }\n                    return Row(\n                      mainAxisAlignment: index % 2 == 0\n                          ? MainAxisAlignment.start\n                          : MainAxisAlignment.end,\n                      children: children,\n                    );\n                  },\n                  itemCount: chats.length,\n                ),\n              )\n            ],\n          ),\n        ),\n```\n\n# refresh with code\n\n使用代码来执行刷新动画\n\n* 定义key\n``` dart\n  final GlobalKey\u003cPullToRefreshNotificationState\u003e key =\n      new GlobalKey\u003cPullToRefreshNotificationState\u003e();\n\n        PullToRefreshNotification(\n          key: key,    \n```\n\n* 使用\n\n如果你的刷新头是根据DragOffset来设置高度，那么你用代码执行刷新的时候。你记得要设置notificationDragOffset\n\n ``` dart \n  key.currentState.show(notificationDragOffset: maxDragOffset);\n ```\n# ☕️Buy me a coffee\n\n![img](http://zmtzawqlp.gitee.io/my_images/images/qrcode.png)\n","funding_links":["http://zmtzawqlp.gitee.io/my_images/images/qrcode.png"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fpull_to_refresh_notification","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffluttercandies%2Fpull_to_refresh_notification","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluttercandies%2Fpull_to_refresh_notification/lists"}