Skip to content

Commit

Permalink
Merge pull request #20 from K9i-0/feature/custom_refresh_ui
Browse files Browse the repository at this point in the history
add: enableRefreshIndicator and enableErrorSnackBar to PagingHelperVi…
  • Loading branch information
K9i-0 authored Jun 2, 2024
2 parents 0aacb37 + e303eac commit 8796fb3
Show file tree
Hide file tree
Showing 12 changed files with 321 additions and 51 deletions.
17 changes: 4 additions & 13 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,18 @@
"flutterMode": "release"
},
{
"name": "ui custom example",
"name": "basic ui custom example",
"cwd": "example",
"program": "lib/main2.dart",
"request": "launch",
"type": "dart"
},
{
"name": "ui custom example (profile mode)",
"name": "advanced ui custom example",
"cwd": "example",
"program": "lib/main2.dart",
"program": "lib/main3.dart",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
"type": "dart"
},
{
"name": "ui custom example (release mode)",
"cwd": "example",
"program": "lib/main2.dart",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}
84 changes: 84 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class SampleScreen extends StatelessWidget {

## UI Customization

### Basic Customization

You can easily customize the appearance of loading and error states using `ThemeExtension`.

<img src="https://raw.githubusercontent.com/K9i-0/riverpod_paging_utils/main/gifs/ui_customization.gif" alt="riverpod_paging_utils_sample" width="33%">
Expand Down Expand Up @@ -139,3 +141,85 @@ class MainApp extends StatelessWidget {
```

A complete sample implementation can be found in the [example/lib/main2.dart](https://github.com/K9i-0/riverpod_paging_utils/blob/main/example/lib/main2.dart) file.

### Advanced Customization

Customizing the appearance of error SnackBars or RefreshIndicators requires a bit more setup.

#### 1. Theme Configuration

First, adjust your `PagingHelperViewTheme` like this:

```dart
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
extensions: [
PagingHelperViewTheme(
// disable error snackbar
enableErrorSnackBar: false,
// disable pull-to-refresh
enableRefreshIndicator: false,
),
],
),
home: const SampleScreen(),
);
}
}
```

#### 2. Integrating Custom Refresh (e.g., with easy_refresh)

If you're using a package like [easy_refresh](https://pub.dev/packages/easy_refresh) to provide a RefreshIndicator, modify your screen code as follows:

```dart
class SampleScreen extends ConsumerWidget {
const SampleScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('Advanced UI Customization'),
),
body: PagingHelperView(
provider: sampleNotifierProvider,
futureRefreshable: sampleNotifierProvider.future,
notifierRefreshable: sampleNotifierProvider.notifier,
contentBuilder: (data, endItemView) {
// Use EasyRefresh alternative to RefreshIndicator
return EasyRefresh(
onRefresh: () {
ref.invalidate(sampleNotifierProvider);
return ref.read(sampleNotifierProvider.future);
},
child: ListView.builder(
itemCount: data.items.length + (endItemView != null ? 1 : 0),
itemBuilder: (context, index) {
// If the end item view is provided and the index is the last item,
// return the end item view.
if (endItemView != null && index == data.items.length) {
return endItemView;
}
// Otherwise, build a list tile for each sample item.
return ListTile(
title: Text(data.items[index].name),
subtitle: Text(data.items[index].id),
);
},
),
);
},
),
);
}
}
```

A complete sample implementation can be found in the [example/lib/main3.dart](https://github.com/K9i-0/riverpod_paging_utils/blob/main/example/lib/main3.dart) file.
2 changes: 1 addition & 1 deletion example/lib/main2.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class SampleScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample Screen'),
title: const Text('Basic UI Customization'),
),
body: PagingHelperView(
provider: sampleNotifierProvider,
Expand Down
2 changes: 1 addition & 1 deletion example/lib/main2.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

113 changes: 113 additions & 0 deletions example/lib/main3.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import 'package:easy_refresh/easy_refresh.dart';
import 'package:example/data/sample_item.dart';
import 'package:example/repository/sample_repository.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
import 'package:riverpod_paging_utils/theme_extension.dart';

part 'main3.g.dart';

void main() {
runApp(
const ProviderScope(
child: MainApp(),
),
);
}

class MainApp extends StatelessWidget {
const MainApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
extensions: [
PagingHelperViewTheme(
// disable error snackbar
enableErrorSnackBar: false,
// disable pull-to-refresh
enableRefreshIndicator: false,
),
],
),
home: const SampleScreen(),
);
}
}

/// A Riverpod provider that mixes in [CursorPagingNotifierMixin].
/// This provider handles the pagination logic for fetching [SampleItem] data using cursor-based pagination.
@riverpod
class SampleNotifier extends _$SampleNotifier
with CursorPagingNotifierMixin<SampleItem> {
/// Builds the initial state of the provider by fetching data with a null cursor.
@override
Future<CursorPagingData<SampleItem>> build() => fetch(cursor: null);

/// Fetches paginated data from the [SampleRepository] based on the provided [cursor].
/// Returns a [CursorPagingData] object containing the fetched items, a flag indicating whether more data is available,
/// and the next cursor for fetching the next page.
@override
Future<CursorPagingData<SampleItem>> fetch({
required String? cursor,
}) async {
// Simulate a delay of 2 seconds to demonstrate the loading view.
await Future<void>.delayed(const Duration(seconds: 2));
final repository = ref.read(sampleRepositoryProvider);
final (items, nextCursor) = await repository.getByCursor(cursor);
final hasMore = nextCursor != null && nextCursor.isNotEmpty;

return CursorPagingData(
items: items,
hasMore: hasMore,
nextCursor: nextCursor,
);
}
}

/// A sample page that demonstrates the usage of [PagingHelperView] with the [SampleNotifier] provider.
class SampleScreen extends ConsumerWidget {
const SampleScreen({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('Advanced UI Customization'),
),
body: PagingHelperView(
provider: sampleNotifierProvider,
futureRefreshable: sampleNotifierProvider.future,
notifierRefreshable: sampleNotifierProvider.notifier,
contentBuilder: (data, endItemView) {
// Use EasyRefresh alternative to RefreshIndicator
return EasyRefresh(
onRefresh: () {
ref.invalidate(sampleNotifierProvider);
return ref.read(sampleNotifierProvider.future);
},
child: ListView.builder(
itemCount: data.items.length + (endItemView != null ? 1 : 0),
itemBuilder: (context, index) {
// If the end item view is provided and the index is the last item,
// return the end item view.
if (endItemView != null && index == data.items.length) {
return endItemView;
}

// Otherwise, build a list tile for each sample item.
return ListTile(
title: Text(data.items[index].name),
subtitle: Text(data.items[index].id),
);
},
),
);
},
),
);
}
}
30 changes: 30 additions & 0 deletions example/lib/main3.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion example/lib/ui/first_page_error_screen.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion example/lib/ui/second_page_error_screen.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 25 additions & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.6"
easy_refresh:
dependency: "direct main"
description:
name: easy_refresh
sha256: "486e30abfcaae66c0f2c2798a10de2298eb9dc5e0bb7e1dba9328308968cae0c"
url: "https://pub.dev"
source: hosted
version: "3.4.0"
fake_async:
dependency: transitive
description:
Expand Down Expand Up @@ -451,6 +459,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.0"
path_drawing:
dependency: transitive
description:
name: path_drawing
sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.dev"
source: hosted
version: "1.0.1"
pool:
dependency: transitive
description:
Expand Down Expand Up @@ -737,4 +761,4 @@ packages:
version: "2.1.0"
sdks:
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.1.0-0"
flutter: ">=3.7.0"
1 change: 1 addition & 0 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ environment:
flutter: ">=3.0.0"

dependencies:
easy_refresh: ^3.4.0
flutter:
sdk: flutter
flutter_riverpod: ^2.5.1
Expand Down
Loading

0 comments on commit 8796fb3

Please sign in to comment.