mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Add pull-to-refresh to the notifications list
The devices list already supported pull-to-refresh; mirror that on the notifications list by wrapping its CustomScrollView in a RefreshIndicator with AlwaysScrollableScrollPhysics. Keep the existing list visible during a refresh (_isLoading = _items.isEmpty) instead of flashing the skeleton, matching the devices list behaviour. Add widget tests covering pull-to-refresh for both lists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7b8a33606c
commit
8b8fa5ce89
@@ -126,7 +126,7 @@ class _NotificationsListState extends State<NotificationsList>
|
||||
final token = CancelToken();
|
||||
_fetchToken = token;
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_isLoading = _items.isEmpty;
|
||||
_error = null;
|
||||
});
|
||||
try {
|
||||
@@ -217,12 +217,16 @@ class _NotificationsListState extends State<NotificationsList>
|
||||
children: [
|
||||
_buildNotificationsHeader(context),
|
||||
Expanded(
|
||||
child: CustomScrollView(
|
||||
controller: _scrollController,
|
||||
slivers: [
|
||||
..._buildNotificationSlivers(context),
|
||||
...widget.trailingSlivers,
|
||||
],
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () => _fetchPage(_currentPage),
|
||||
child: CustomScrollView(
|
||||
controller: _scrollController,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
..._buildNotificationSlivers(context),
|
||||
...widget.trailingSlivers,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -100,4 +100,30 @@ void main() {
|
||||
|
||||
await tearDownTree(tester);
|
||||
});
|
||||
|
||||
testWidgets('pulling the list down refetches devices', (tester) async {
|
||||
// A single reply list whose contents are swapped before the pull, so the
|
||||
// same route re-serializes fresh data on the refresh request.
|
||||
final devices = <Map<String, dynamic>>[
|
||||
deviceJson(macAddress: '00:00:00:00:00:01'),
|
||||
];
|
||||
adapter.onGet('/devices', (server) => server.reply(200, devices));
|
||||
|
||||
await pumpScreen(tester, const DeviceList());
|
||||
await pumpUntilFound(tester, find.byType(DeviceRowWide));
|
||||
expect(find.byType(DeviceRowWide), findsOneWidget);
|
||||
|
||||
// The next fetch returns an extra device; a pull-down should pick it up.
|
||||
devices.add(deviceJson(macAddress: '00:00:00:00:00:02'));
|
||||
await tester.fling(
|
||||
find.byType(CustomScrollView),
|
||||
const Offset(0, 300),
|
||||
1000,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(DeviceRowWide), findsNWidgets(2));
|
||||
|
||||
await tearDownTree(tester);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,4 +105,46 @@ void main() {
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
||||
testWidgets('pulling the list down refetches notifications', (tester) async {
|
||||
tester.view.physicalSize = const Size(400, 600);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
|
||||
// A single reply list whose contents are swapped before the pull, so the
|
||||
// same route re-serializes fresh data on the refresh request.
|
||||
final items = <Map<String, dynamic>>[
|
||||
notificationJson(id: 1, title: 'Before refresh'),
|
||||
];
|
||||
adapter.onGet(
|
||||
'/notifications',
|
||||
(server) => server.reply(200, items),
|
||||
queryParameters: {'is_new': true, 'page_offset': 0, 'page_limit': 6},
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: gruvboxDarkTheme,
|
||||
home: const Scaffold(body: NotificationsList()),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.textContaining('Before refresh'), findsOneWidget);
|
||||
|
||||
// The next fetch returns fresh data; a pull-down should pick it up.
|
||||
items
|
||||
..clear()
|
||||
..add(notificationJson(id: 2, title: 'After refresh'));
|
||||
await tester.fling(
|
||||
find.byType(CustomScrollView),
|
||||
const Offset(0, 300),
|
||||
1000,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.textContaining('After refresh'), findsOneWidget);
|
||||
expect(find.textContaining('Before refresh'), findsNothing);
|
||||
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user