From 23c6261a37ba4d79092bd84da6daeb792b1c08ba Mon Sep 17 00:00:00 2001 From: krawieck Date: Sat, 9 Jan 2021 01:35:43 +0100 Subject: [PATCH 1/7] add pull to refresh to infinite scroll --- lib/widgets/infinite_scroll.dart | 83 ++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/lib/widgets/infinite_scroll.dart b/lib/widgets/infinite_scroll.dart index a1c1b9e..9e170e3 100644 --- a/lib/widgets/infinite_scroll.dart +++ b/lib/widgets/infinite_scroll.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import '../hooks/ref.dart'; @@ -27,8 +28,10 @@ class InfiniteScroll extends HookWidget { final InfiniteScrollController controller; final Widget prepend; final EdgeInsetsGeometry padding; + final GlobalKey _refreshIndicatorKey = + GlobalKey(); - const InfiniteScroll({ + InfiniteScroll({ this.batchSize = 10, this.prepend = const SizedBox.shrink(), this.padding, @@ -61,42 +64,50 @@ class InfiniteScroll extends HookWidget { final page = data.value.length ~/ batchSize + 1; - return ListView.builder( - padding: padding, - // +2 for the loading widget and prepend widget - itemCount: data.value.length + 2, - itemBuilder: (_, i) { - if (i == 0) { - return prepend; - } - i -= 1; - - // reached the bottom, fetch more - if (i == data.value.length) { - // if there are no more, skip - if (!hasMore.current) { - return const SizedBox.shrink(); - } - - // if it's already fetching more, skip - if (!isFetching.current) { - isFetching.current = true; - fetchMore(page, batchSize).then((newData) { - // if got less than the batchSize, mark the list as done - if (newData.length < batchSize) { - hasMore.current = false; - } - // append new data - data.value = [...data.value, ...newData]; - }).whenComplete(() => isFetching.current = false); - } - - return loadingWidget; - } - - // not last element, render list item - return builder(data.value[i]); + return RefreshIndicator( + key: _refreshIndicatorKey, + onRefresh: () async { + controller.clear(); + await HapticFeedback.mediumImpact(); + await Future.delayed(const Duration(seconds: 1)); }, + child: ListView.builder( + padding: padding, + // +2 for the loading widget and prepend widget + itemCount: data.value.length + 2, + itemBuilder: (_, i) { + if (i == 0) { + return prepend; + } + i -= 1; + + // reached the bottom, fetch more + if (i == data.value.length) { + // if there are no more, skip + if (!hasMore.current) { + return const SizedBox.shrink(); + } + + // if it's already fetching more, skip + if (!isFetching.current) { + isFetching.current = true; + fetchMore(page, batchSize).then((newData) { + // if got less than the batchSize, mark the list as done + if (newData.length < batchSize) { + hasMore.current = false; + } + // append new data + data.value = [...data.value, ...newData]; + }).whenComplete(() => isFetching.current = false); + } + + return loadingWidget; + } + + // not last element, render list item + return builder(data.value[i]); + }, + ), ); } } From 51f0fe4ef4a11d3eb650a9c7c9e021fa1f519de0 Mon Sep 17 00:00:00 2001 From: krawieck Date: Sat, 9 Jan 2021 02:11:30 +0100 Subject: [PATCH 2/7] add pull to refresh to full post --- lib/pages/full_post.dart | 74 ++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/lib/pages/full_post.dart b/lib/pages/full_post.dart index cc8f238..5aed33b 100644 --- a/lib/pages/full_post.dart +++ b/lib/pages/full_post.dart @@ -1,6 +1,7 @@ import 'package:esys_flutter_share/esys_flutter_share.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; @@ -18,8 +19,10 @@ class FullPostPage extends HookWidget { final int id; final String instanceHost; final PostView post; + final GlobalKey _refreshIndicatorKey = + GlobalKey(); - const FullPostPage({@required this.id, @required this.instanceHost}) + FullPostPage({@required this.id, @required this.instanceHost}) : assert(id != null), assert(instanceHost != null), post = null; @@ -35,7 +38,7 @@ class FullPostPage extends HookWidget { .getPost(id: id, auth: accStore.defaultTokenFor(instanceHost)?.raw)); final loggedInAction = useLoggedInAction(instanceHost); final newComments = useState(const []); - + final updatedPost = useState(null); // FALLBACK VIEW if (!fullPostSnap.hasData && this.post == null) { @@ -63,6 +66,21 @@ class FullPostPage extends HookWidget { // FUNCTIONS + refresh() async { + await HapticFeedback.mediumImpact(); + + try { + return await LemmyApi(instanceHost) + .v1 + .getPost(id: id, auth: accStore.defaultTokenFor(instanceHost)?.raw); + // ignore: avoid_catches_without_on_clauses + } catch (e) { + Scaffold.of(context).showSnackBar(SnackBar( + content: Text(e.toString()), + )); + } + } + sharePost() => Share.text('Share post', post.apId, 'text/plain'); comment() async { @@ -89,31 +107,35 @@ class FullPostPage extends HookWidget { floatingActionButton: FloatingActionButton( onPressed: loggedInAction((_) => comment()), child: const Icon(Icons.comment)), - body: ListView( - physics: const AlwaysScrollableScrollPhysics(), - children: [ - Post(post, fullPost: true), - if (fullPostSnap.hasData) - CommentSection( - newComments.value.followedBy(fullPost.comments).toList(), - postCreatorId: fullPost.post.creatorId) - else if (fullPostSnap.hasError) - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 10, vertical: 30), - child: Column( - children: [ - const Icon(Icons.error), - Text('Error: ${fullPostSnap.error}') - ], + body: RefreshIndicator( + onRefresh: refresh, + key: _refreshIndicatorKey, + child: ListView( + physics: const AlwaysScrollableScrollPhysics(), + children: [ + Post(post, fullPost: true), + if (fullPostSnap.hasData) + CommentSection( + newComments.value.followedBy(fullPost.comments).toList(), + postCreatorId: fullPost.post.creatorId) + else if (fullPostSnap.hasError) + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 30), + child: Column( + children: [ + const Icon(Icons.error), + Text('Error: ${fullPostSnap.error}') + ], + ), + ) + else + const Padding( + padding: EdgeInsets.only(top: 40), + child: Center(child: CircularProgressIndicator()), ), - ) - else - const Padding( - padding: EdgeInsets.only(top: 40), - child: Center(child: CircularProgressIndicator()), - ), - ], + ], + ), )); } } From 527b0adfb6e95a92f9fa552788deeb09e94ee11f Mon Sep 17 00:00:00 2001 From: krawieck Date: Sat, 9 Jan 2021 02:21:58 +0100 Subject: [PATCH 3/7] implement the damn thing --- lib/pages/full_post.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/full_post.dart b/lib/pages/full_post.dart index 5aed33b..8e93902 100644 --- a/lib/pages/full_post.dart +++ b/lib/pages/full_post.dart @@ -60,9 +60,10 @@ class FullPostPage extends HookWidget { // VARIABLES - final post = fullPostSnap.hasData ? fullPostSnap.data.post : this.post; + final post = updatedPost.value?.post ?? + (fullPostSnap.hasData ? fullPostSnap.data.post : this.post); - final fullPost = fullPostSnap.data; + final fullPost = updatedPost.value ?? fullPostSnap.data; // FUNCTIONS From 82ba55cbe337d5ecc0637831fc16b8259c41082b Mon Sep 17 00:00:00 2001 From: krawieck Date: Sat, 9 Jan 2021 02:31:53 +0100 Subject: [PATCH 4/7] add pull to refresh to communities tab --- lib/pages/communities_tab.dart | 201 ++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 88 deletions(-) diff --git a/lib/pages/communities_tab.dart b/lib/pages/communities_tab.dart index 433676f..7676d69 100644 --- a/lib/pages/communities_tab.dart +++ b/lib/pages/communities_tab.dart @@ -29,8 +29,7 @@ class CommunitiesTab extends HookWidget { useMemoized(() => accountsStore.loggedInInstances.length); final isCollapsed = useState(List.filled(amountOfDisplayInstances, false)); - // TODO: rebuild when instances/accounts change - final instancesSnap = useMemoFuture(() { + getInstances() { final futures = accountsStore.loggedInInstances .map( (instanceHost) => @@ -39,8 +38,9 @@ class CommunitiesTab extends HookWidget { .toList(); return Future.wait(futures); - }); - final communitiesSnap = useMemoFuture(() { + } + + getCommunities() { final futures = accountsStore.loggedInInstances .map( (instanceHost) => LemmyApi(instanceHost) @@ -56,7 +56,15 @@ class CommunitiesTab extends HookWidget { .toList(); return Future.wait(futures); - }); + } + + // TODO: rebuild when instances/accounts change + final instancesSnap = useMemoFuture(getInstances); + final communitiesSnap = useMemoFuture(getCommunities); + + final updatedCommunities = + useState>>(null); + final updatedInstances = useState>(null); if (communitiesSnap.hasError || instancesSnap.hasError) { return Scaffold( @@ -85,8 +93,22 @@ class CommunitiesTab extends HookWidget { ); } - final instances = instancesSnap.data; - final communities = communitiesSnap.data + refresh() async { + try { + final i = getInstances(); + final c = getCommunities(); + await Future.wait([i, c]); + updatedInstances.value = await i; + updatedCommunities.value = await c; + // ignore: avoid_catches_without_on_clauses + } catch (e) { + Scaffold.of(context) + .showSnackBar(SnackBar(content: Text(e.toString()))); + } + } + + final instances = updatedInstances.value ?? instancesSnap.data; + final communities = updatedCommunities.value ?? communitiesSnap.data ..forEach( (e) => e.sort((a, b) => a.communityName.compareTo(b.communityName))); @@ -136,91 +158,94 @@ class CommunitiesTab extends HookWidget { ), ), ), - body: ListView( - children: [ - for (var i = 0; i < amountOfDisplayInstances; i++) - Column( - children: [ - ListTile( - onTap: () => goToInstance( - context, accountsStore.loggedInInstances.elementAt(i)), - onLongPress: () => toggleCollapse(i), - leading: instances[i].icon != null - ? CachedNetworkImage( - height: 50, - width: 50, - imageUrl: instances[i].icon, - imageBuilder: (context, imageProvider) => Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - image: DecorationImage( - fit: BoxFit.cover, image: imageProvider), + body: RefreshIndicator( + onRefresh: refresh, + child: ListView( + children: [ + for (var i = 0; i < amountOfDisplayInstances; i++) + Column( + children: [ + ListTile( + onTap: () => goToInstance( + context, accountsStore.loggedInInstances.elementAt(i)), + onLongPress: () => toggleCollapse(i), + leading: instances[i].icon != null + ? CachedNetworkImage( + height: 50, + width: 50, + imageUrl: instances[i].icon, + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.cover, image: imageProvider), + ), ), + errorWidget: (_, __, ___) => + const SizedBox(width: 50), + ) + : const SizedBox(width: 50), + title: Text( + instances[i].name, + style: theme.textTheme.headline6, + ), + trailing: IconButton( + icon: Icon(isCollapsed.value[i] + ? Icons.keyboard_arrow_up + : Icons.keyboard_arrow_down), + onPressed: () => toggleCollapse(i), + ), + ), + if (!isCollapsed.value[i]) + for (final comm in filterCommunities(communities[i])) + Padding( + padding: const EdgeInsets.only(left: 17), + child: ListTile( + onTap: () => goToCommunity.byId( + context, + accountsStore.loggedInInstances.elementAt(i), + comm.communityId), + dense: true, + leading: VerticalDivider( + color: theme.hintColor, ), - errorWidget: (_, __, ___) => - const SizedBox(width: 50), - ) - : const SizedBox(width: 50), - title: Text( - instances[i].name, - style: theme.textTheme.headline6, - ), - trailing: IconButton( - icon: Icon(isCollapsed.value[i] - ? Icons.keyboard_arrow_up - : Icons.keyboard_arrow_down), - onPressed: () => toggleCollapse(i), - ), - ), - if (!isCollapsed.value[i]) - for (final comm in filterCommunities(communities[i])) - Padding( - padding: const EdgeInsets.only(left: 17), - child: ListTile( - onTap: () => goToCommunity.byId( - context, - accountsStore.loggedInInstances.elementAt(i), - comm.communityId), - dense: true, - leading: VerticalDivider( - color: theme.hintColor, - ), - title: Row( - children: [ - if (comm.communityIcon != null) - CachedNetworkImage( - height: 30, - width: 30, - imageUrl: comm.communityIcon, - imageBuilder: (context, imageProvider) => - Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - image: DecorationImage( - fit: BoxFit.cover, - image: imageProvider), + title: Row( + children: [ + if (comm.communityIcon != null) + CachedNetworkImage( + height: 30, + width: 30, + imageUrl: comm.communityIcon, + imageBuilder: (context, imageProvider) => + Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.cover, + image: imageProvider), + ), ), - ), - errorWidget: (_, __, ___) => - const SizedBox(width: 30), - ) - else - const SizedBox(width: 30), - const SizedBox(width: 10), - Text( - '''!${comm.communityName}${comm.isLocal ? '' : '@${comm.originInstanceHost}'}''', - ), - ], + errorWidget: (_, __, ___) => + const SizedBox(width: 30), + ) + else + const SizedBox(width: 30), + const SizedBox(width: 10), + Text( + '''!${comm.communityName}${comm.isLocal ? '' : '@${comm.originInstanceHost}'}''', + ), + ], + ), + trailing: _CommunitySubscribeToggle( + instanceHost: comm.instanceHost, + communityId: comm.communityId, + ), ), - trailing: _CommunitySubscribeToggle( - instanceHost: comm.instanceHost, - communityId: comm.communityId, - ), - ), - ) - ], - ), - ], + ) + ], + ), + ], + ), ), ); } From 0bef4fe1afc7e190d3d0027cfc8374b6ba26b9e6 Mon Sep 17 00:00:00 2001 From: krawieck Date: Sat, 9 Jan 2021 02:36:27 +0100 Subject: [PATCH 5/7] add vibration of refresh to communities tab --- lib/pages/communities_tab.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pages/communities_tab.dart b/lib/pages/communities_tab.dart index 7676d69..39f98eb 100644 --- a/lib/pages/communities_tab.dart +++ b/lib/pages/communities_tab.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fuzzy/fuzzy.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; @@ -94,6 +95,7 @@ class CommunitiesTab extends HookWidget { } refresh() async { + await HapticFeedback.mediumImpact(); try { final i = getInstances(); final c = getCommunities(); From 0d434fafccb13f873146c31c195c79189e276c02 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 9 Jan 2021 16:34:24 +0000 Subject: [PATCH 6/7] Add new useRefreshable hook --- lib/hooks/refreshable.dart | 37 ++++++++++++++++++++++++++++++++ lib/pages/communities_tab.dart | 39 +++++++++++++++++----------------- lib/pages/full_post.dart | 35 +++++++++++++----------------- 3 files changed, 71 insertions(+), 40 deletions(-) create mode 100644 lib/hooks/refreshable.dart diff --git a/lib/hooks/refreshable.dart b/lib/hooks/refreshable.dart new file mode 100644 index 0000000..196da2f --- /dev/null +++ b/lib/hooks/refreshable.dart @@ -0,0 +1,37 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +import 'memo_future.dart'; + +class Refreshable { + const Refreshable({@required this.snapshot, @required this.refresh}) + : assert(snapshot != null), + assert(refresh != null); + + final AsyncSnapshot snapshot; + final AsyncCallback refresh; +} + +Refreshable useRefreshable(AsyncValueGetter fetcher, + [List keys = const []]) { + final newData = useState(null); + final snapshot = useMemoFuture(() async { + newData.value = null; + return fetcher(); + }, keys); + + final outSnapshot = () { + if (newData.value != null) { + return AsyncSnapshot.withData(ConnectionState.done, newData.value); + } + return snapshot; + }(); + + return Refreshable( + snapshot: outSnapshot, + refresh: () async { + newData.value = await fetcher(); + }, + ); +} diff --git a/lib/pages/communities_tab.dart b/lib/pages/communities_tab.dart index 39f98eb..7915fbf 100644 --- a/lib/pages/communities_tab.dart +++ b/lib/pages/communities_tab.dart @@ -8,7 +8,7 @@ import 'package:fuzzy/fuzzy.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; import '../hooks/delayed_loading.dart'; -import '../hooks/memo_future.dart'; +import '../hooks/refreshable.dart'; import '../hooks/stores.dart'; import '../util/extensions/api.dart'; import '../util/extensions/iterators.dart'; @@ -60,14 +60,11 @@ class CommunitiesTab extends HookWidget { } // TODO: rebuild when instances/accounts change - final instancesSnap = useMemoFuture(getInstances); - final communitiesSnap = useMemoFuture(getCommunities); + final instancesRefreshable = useRefreshable(getInstances); + final communitiesRefreshable = useRefreshable(getCommunities); - final updatedCommunities = - useState>>(null); - final updatedInstances = useState>(null); - - if (communitiesSnap.hasError || instancesSnap.hasError) { + if (communitiesRefreshable.snapshot.hasError || + instancesRefreshable.snapshot.hasError) { return Scaffold( appBar: AppBar(), body: Center( @@ -77,15 +74,16 @@ class CommunitiesTab extends HookWidget { Padding( padding: const EdgeInsets.all(8), child: Text( - communitiesSnap.error?.toString() ?? - instancesSnap.error?.toString(), + communitiesRefreshable.snapshot.error?.toString() ?? + instancesRefreshable.snapshot.error?.toString(), ), ) ], ), ), ); - } else if (!communitiesSnap.hasData || !instancesSnap.hasData) { + } else if (!communitiesRefreshable.snapshot.hasData || + !instancesRefreshable.snapshot.hasData) { return Scaffold( appBar: AppBar(), body: const Center( @@ -97,11 +95,10 @@ class CommunitiesTab extends HookWidget { refresh() async { await HapticFeedback.mediumImpact(); try { - final i = getInstances(); - final c = getCommunities(); - await Future.wait([i, c]); - updatedInstances.value = await i; - updatedCommunities.value = await c; + await Future.wait([ + instancesRefreshable.refresh(), + communitiesRefreshable.refresh(), + ]); // ignore: avoid_catches_without_on_clauses } catch (e) { Scaffold.of(context) @@ -109,8 +106,8 @@ class CommunitiesTab extends HookWidget { } } - final instances = updatedInstances.value ?? instancesSnap.data; - final communities = updatedCommunities.value ?? communitiesSnap.data + final instances = instancesRefreshable.snapshot.data; + final communities = communitiesRefreshable.snapshot.data ..forEach( (e) => e.sort((a, b) => a.communityName.compareTo(b.communityName))); @@ -239,6 +236,7 @@ class CommunitiesTab extends HookWidget { ], ), trailing: _CommunitySubscribeToggle( + key: ValueKey(comm.communityId), instanceHost: comm.instanceHost, communityId: comm.communityId, ), @@ -258,9 +256,10 @@ class _CommunitySubscribeToggle extends HookWidget { final String instanceHost; const _CommunitySubscribeToggle( - {@required this.instanceHost, @required this.communityId}) + {@required this.instanceHost, @required this.communityId, Key key}) : assert(instanceHost != null), - assert(communityId != null); + assert(communityId != null), + super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/pages/full_post.dart b/lib/pages/full_post.dart index 8e93902..4478421 100644 --- a/lib/pages/full_post.dart +++ b/lib/pages/full_post.dart @@ -6,7 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; import '../hooks/logged_in_action.dart'; -import '../hooks/memo_future.dart'; +import '../hooks/refreshable.dart'; import '../hooks/stores.dart'; import '../util/more_icon.dart'; import '../widgets/comment_section.dart'; @@ -19,10 +19,8 @@ class FullPostPage extends HookWidget { final int id; final String instanceHost; final PostView post; - final GlobalKey _refreshIndicatorKey = - GlobalKey(); - FullPostPage({@required this.id, @required this.instanceHost}) + const FullPostPage({@required this.id, @required this.instanceHost}) : assert(id != null), assert(instanceHost != null), post = null; @@ -33,23 +31,22 @@ class FullPostPage extends HookWidget { @override Widget build(BuildContext context) { final accStore = useAccountsStore(); - final fullPostSnap = useMemoFuture(() => LemmyApi(instanceHost) + final fullPostRefreshable = useRefreshable(() => LemmyApi(instanceHost) .v1 .getPost(id: id, auth: accStore.defaultTokenFor(instanceHost)?.raw)); final loggedInAction = useLoggedInAction(instanceHost); final newComments = useState(const []); - final updatedPost = useState(null); - // FALLBACK VIEW - if (!fullPostSnap.hasData && this.post == null) { + // FALLBACK VIEW + if (!fullPostRefreshable.snapshot.hasData && this.post == null) { return Scaffold( appBar: AppBar(), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (fullPostSnap.hasError) - Text(fullPostSnap.error.toString()) + if (fullPostRefreshable.snapshot.hasError) + Text(fullPostRefreshable.snapshot.error.toString()) else const CircularProgressIndicator(), ], @@ -60,10 +57,11 @@ class FullPostPage extends HookWidget { // VARIABLES - final post = updatedPost.value?.post ?? - (fullPostSnap.hasData ? fullPostSnap.data.post : this.post); + final post = fullPostRefreshable.snapshot.hasData + ? fullPostRefreshable.snapshot.data.post + : this.post; - final fullPost = updatedPost.value ?? fullPostSnap.data; + final fullPost = fullPostRefreshable.snapshot.data; // FUNCTIONS @@ -71,9 +69,7 @@ class FullPostPage extends HookWidget { await HapticFeedback.mediumImpact(); try { - return await LemmyApi(instanceHost) - .v1 - .getPost(id: id, auth: accStore.defaultTokenFor(instanceHost)?.raw); + await fullPostRefreshable.refresh(); // ignore: avoid_catches_without_on_clauses } catch (e) { Scaffold.of(context).showSnackBar(SnackBar( @@ -110,23 +106,22 @@ class FullPostPage extends HookWidget { child: const Icon(Icons.comment)), body: RefreshIndicator( onRefresh: refresh, - key: _refreshIndicatorKey, child: ListView( physics: const AlwaysScrollableScrollPhysics(), children: [ Post(post, fullPost: true), - if (fullPostSnap.hasData) + if (fullPostRefreshable.snapshot.hasData) CommentSection( newComments.value.followedBy(fullPost.comments).toList(), postCreatorId: fullPost.post.creatorId) - else if (fullPostSnap.hasError) + else if (fullPostRefreshable.snapshot.hasError) Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 30), child: Column( children: [ const Icon(Icons.error), - Text('Error: ${fullPostSnap.error}') + Text('Error: ${fullPostRefreshable.snapshot.error}') ], ), ) From a994d7ca308547bfe9e6e6146a3df0f73b8ebe38 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 9 Jan 2021 17:25:34 +0000 Subject: [PATCH 7/7] Document `useRefreshable` hook --- lib/hooks/refreshable.dart | 7 +++++++ lib/widgets/infinite_scroll.dart | 5 +---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/hooks/refreshable.dart b/lib/hooks/refreshable.dart index 196da2f..2378370 100644 --- a/lib/hooks/refreshable.dart +++ b/lib/hooks/refreshable.dart @@ -13,6 +13,13 @@ class Refreshable { final AsyncCallback refresh; } +/// Similar to [useMemoFuture] but adds a `.refresh` method which +/// allows to re-run the fetcher. Calling `.refresh` will not +/// turn AsyncSnapshot into a loading state. Instead it will +/// replace the ready state with the new data when available +/// +/// `keys` will re-run the initial fetching thus yielding a +/// loading state in the AsyncSnapshot Refreshable useRefreshable(AsyncValueGetter fetcher, [List keys = const []]) { final newData = useState(null); diff --git a/lib/widgets/infinite_scroll.dart b/lib/widgets/infinite_scroll.dart index 9e170e3..a9a03d2 100644 --- a/lib/widgets/infinite_scroll.dart +++ b/lib/widgets/infinite_scroll.dart @@ -28,10 +28,8 @@ class InfiniteScroll extends HookWidget { final InfiniteScrollController controller; final Widget prepend; final EdgeInsetsGeometry padding; - final GlobalKey _refreshIndicatorKey = - GlobalKey(); - InfiniteScroll({ + const InfiniteScroll({ this.batchSize = 10, this.prepend = const SizedBox.shrink(), this.padding, @@ -65,7 +63,6 @@ class InfiniteScroll extends HookWidget { final page = data.value.length ~/ batchSize + 1; return RefreshIndicator( - key: _refreshIndicatorKey, onRefresh: () async { controller.clear(); await HapticFeedback.mediumImpact();