Extended refresh indicator (keyboard based)

This commit is contained in:
2025-09-06 13:52:20 +08:00
parent 573b76d3ff
commit ecc100ac45
8 changed files with 89 additions and 9 deletions

View File

@@ -9,6 +9,7 @@ import 'package:island/widgets/poll/poll_feedback.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart'; import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
part 'poll_list.g.dart'; part 'poll_list.g.dart';
@@ -86,7 +87,7 @@ class CreatorPollListScreen extends HookConsumerWidget {
onPressed: () => _createPoll(context), onPressed: () => _createPoll(context),
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),
body: RefreshIndicator( body: ExtendedRefreshIndicator(
onRefresh: () => ref.refresh(pollListNotifierProvider(pubName).future), onRefresh: () => ref.refresh(pollListNotifierProvider(pubName).future),
child: CustomScrollView( child: CustomScrollView(
slivers: [ slivers: [

View File

@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:island/pods/webfeed.dart'; import 'package:island/pods/webfeed.dart';
import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/empty_state.dart'; import 'package:island/widgets/empty_state.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
class WebFeedListScreen extends ConsumerWidget { class WebFeedListScreen extends ConsumerWidget {
@@ -20,7 +21,10 @@ class WebFeedListScreen extends ConsumerWidget {
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
child: const Icon(Symbols.add), child: const Icon(Symbols.add),
onPressed: () { onPressed: () {
context.pushNamed('creatorFeedNew', pathParameters: {'name': pubName}); context.pushNamed(
'creatorFeedNew',
pathParameters: {'name': pubName},
);
}, },
), ),
body: feedsAsync.when( body: feedsAsync.when(
@@ -32,7 +36,7 @@ class WebFeedListScreen extends ConsumerWidget {
description: 'Add a new web feed to get started', description: 'Add a new web feed to get started',
); );
} }
return RefreshIndicator( return ExtendedRefreshIndicator(
onRefresh: () => ref.refresh(webFeedListProvider(pubName).future), onRefresh: () => ref.refresh(webFeedListProvider(pubName).future),
child: ListView.builder( child: ListView.builder(
padding: EdgeInsets.only(top: 8), padding: EdgeInsets.only(top: 8),
@@ -62,7 +66,10 @@ class WebFeedListScreen extends ConsumerWidget {
), ),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
onTap: () { onTap: () {
context.pushNamed('creatorFeedEdit', pathParameters: {'name': pubName, 'feedId': feed.id}); context.pushNamed(
'creatorFeedEdit',
pathParameters: {'name': pubName, 'feedId': feed.id},
);
}, },
), ),
); );

View File

@@ -9,6 +9,7 @@ import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/response.dart'; import 'package:island/widgets/response.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
part 'bots.g.dart'; part 'bots.g.dart';
@@ -60,7 +61,7 @@ class BotsScreen extends HookConsumerWidget {
), ),
); );
} }
return RefreshIndicator( return ExtendedRefreshIndicator(
onRefresh: onRefresh:
() => ref.refresh(botsProvider(publisherName, projectId).future), () => ref.refresh(botsProvider(publisherName, projectId).future),
child: ListView.builder( child: ListView.builder(

View File

@@ -27,6 +27,7 @@ import 'package:island/pods/network.dart';
import 'package:island/widgets/realm/realm_card.dart'; import 'package:island/widgets/realm/realm_card.dart';
import 'package:island/widgets/publisher/publisher_card.dart'; import 'package:island/widgets/publisher/publisher_card.dart';
import 'package:island/widgets/web_article_card.dart'; import 'package:island/widgets/web_article_card.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
part 'explore.g.dart'; part 'explore.g.dart';
@@ -368,7 +369,7 @@ class ExploreScreen extends HookConsumerWidget {
final isWide = isWideScreen(context); final isWide = isWideScreen(context);
return RefreshIndicator( return ExtendedRefreshIndicator(
onRefresh: () => Future.sync(activitiesNotifier.forceRefresh), onRefresh: () => Future.sync(activitiesNotifier.forceRefresh),
child: PagingHelperView( child: PagingHelperView(
provider: activityListNotifierProvider(filter), provider: activityListNotifierProvider(filter),

View File

@@ -23,6 +23,7 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/realm/realm_list_tile.dart'; import 'package:island/widgets/realm/realm_list_tile.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
part 'realms.g.dart'; part 'realms.g.dart';
@@ -90,7 +91,7 @@ class RealmListScreen extends HookConsumerWidget {
}, },
), ),
floatingActionButtonLocation: TabbedFabLocation(context), floatingActionButtonLocation: TabbedFabLocation(context),
body: RefreshIndicator( body: ExtendedRefreshIndicator(
child: realms.when( child: realms.when(
data: data:
(value) => Column( (value) => Column(

View File

@@ -12,6 +12,7 @@ import 'package:island/widgets/content/sheet.dart';
import 'package:island/widgets/response.dart'; import 'package:island/widgets/response.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
part 'account_devices.g.dart'; part 'account_devices.g.dart';
@@ -177,7 +178,7 @@ class AccountSessionSheet extends HookConsumerWidget {
titleText: 'authSessions'.tr(), titleText: 'authSessions'.tr(),
child: authDevices.when( child: authDevices.when(
data: data:
(data) => RefreshIndicator( (data) => ExtendedRefreshIndicator(
onRefresh: onRefresh:
() => Future.sync(() => ref.invalidate(authDevicesProvider)), () => Future.sync(() => ref.invalidate(authDevicesProvider)),
child: ListView.builder( child: ListView.builder(

View File

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class RefreshIntent extends Intent {
const RefreshIntent();
}
class ExtendedRefreshIndicator extends StatefulWidget {
final Widget child;
final RefreshCallback onRefresh;
const ExtendedRefreshIndicator({
super.key,
required this.child,
required this.onRefresh,
});
@override
State<ExtendedRefreshIndicator> createState() =>
_ExtendedRefreshIndicatorState();
}
class _ExtendedRefreshIndicatorState extends State<ExtendedRefreshIndicator> {
late final FocusNode _focusNode;
@override
void initState() {
super.initState();
_focusNode = FocusNode();
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Shortcuts(
shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyR):
const RefreshIntent(),
LogicalKeySet(LogicalKeyboardKey.meta, LogicalKeyboardKey.keyR):
const RefreshIntent(),
LogicalKeySet(LogicalKeyboardKey.f5): const RefreshIntent(),
},
child: Actions(
actions: <Type, Action<Intent>>{
RefreshIntent: CallbackAction<RefreshIntent>(
onInvoke: (RefreshIntent intent) => widget.onRefresh(),
),
},
child: Focus(
focusNode: _focusNode,
child: RefreshIndicator(
onRefresh: widget.onRefresh,
child: widget.child,
),
),
),
);
}
}

View File

@@ -12,6 +12,7 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:flutter_popup_card/flutter_popup_card.dart'; import 'package:flutter_popup_card/flutter_popup_card.dart';
import 'package:island/widgets/extended_refresh_indicator.dart';
part 'picker.g.dart'; part 'picker.g.dart';
@@ -208,7 +209,7 @@ class _PackSwitcherState extends State<_PackSwitcher> {
// Content // Content
Expanded( Expanded(
child: RefreshIndicator( child: ExtendedRefreshIndicator(
onRefresh: widget.onRefresh, onRefresh: widget.onRefresh,
child: _StickersGrid( child: _StickersGrid(
pack: selectedPack, pack: selectedPack,