This example project demonstrates how to use the riverpod_architecture package to build Flutter applications with clean architecture patterns and Riverpod 3.0 state management.
This example app showcases two main features:
Demonstrates fetching a single user with complete state management:
- States: Initial, Loading, Error, and Data states
- Error Handling: Functional error handling with
Either<Failure, T> - Global Loading: Shows how to use global loading indicators
- Refresh: Demonstrates state refresh functionality
- Try it: Enter user ID
999to see error handling in action!
Key files:
- lib/notifiers/user_detail_notifier.dart - BaseNotifier implementation
- lib/screens/user_detail_screen.dart - UI with pattern matching
Demonstrates paginated data with infinite scroll:
- States: Loading, LoadingMore, Loaded, and Error states
- Pagination: Automatic first page load and manual load more
- Error Resilience: Errors preserve already-loaded data
- Last Page Detection: Knows when all data is loaded
- Refresh: Pull to refresh functionality
Key files:
- lib/notifiers/users_notifier.dart - PaginatedNotifier implementation
- lib/screens/users_list_screen.dart - Paginated list UI
example/
├── lib/
│ ├── main.dart # App entry point with BaseWidget
│ ├── models/
│ │ └── user.dart # User domain model
│ ├── data/
│ │ └── user_repository.dart # Mock repository with Either
│ ├── notifiers/
│ │ ├── user_detail_notifier.dart # BaseNotifier example
│ │ └── users_notifier.dart # PaginatedNotifier example
│ └── screens/
│ ├── home_screen.dart # Home navigation screen
│ ├── user_detail_screen.dart # BaseNotifier UI
│ └── users_list_screen.dart # PaginatedNotifier UI
└── pubspec.yaml
class UserDetailNotifier extends AutoDisposeBaseNotifier<User> {
late final UserRepository _repository;
@override
void prepareForBuild() {
_repository = UserRepository();
}
Future<void> fetchUser(int userId) async {
await execute(_repository.getUser(userId));
}
}class UsersNotifier extends AutoDisposePaginatedNotifier<User, void> {
late final UserRepository _repository;
@override
({PaginatedState<User> initialState, bool useGlobalFailure})
prepareForBuild() {
_repository = UserRepository();
getInitialList(); // Auto-load first page
return (
initialState: const PaginatedState.loading(),
useGlobalFailure: true,
);
}
@override
Future<Either<Failure, PaginatedList<User>>> getListOrFailure(
int page,
[void parameter]
) {
return _repository.getUsers(page: page);
}
}EitherFailureOr<User> getUser(int id) async {
try {
// ... fetch user
return Right(user);
} catch (error, stackTrace) {
return Left(
Failure(
title: error.toString(),
error: error,
stackTrace: stackTrace,
),
);
}
}return switch (state) {
BaseInitial() => InitialWidget(),
BaseLoading() => LoadingWidget(),
BaseError(:final failure) => ErrorWidget(failure),
BaseData(:final data) => DataWidget(data),
};BaseWidget(
loadingIndicator: const CircularProgressIndicator(),
onGlobalFailure: (failure) {
// Show snackbar with error
},
onGlobalInfo: (info) {
// Show snackbar with info
},
child: MaterialApp(...),
)-
Navigate to the example directory:
cd example -
Install dependencies:
flutter pub get
-
Run the app:
flutter run
- Enter different user IDs (1-100) to fetch users
- Enter
999to trigger an error - Use the refresh button to reload data
- Observe loading states during fetch
- Scroll through the automatically loaded first page
- Tap "Load More" to fetch additional pages
- Use the refresh button to reload from scratch
- Scroll to page 10 to see "last page" detection
- Try loading page 5 to see error handling with partial data
The example uses a UserRepository with mock data:
- Users: Generated from IDs 1-200 (20 per page, 10 pages total)
- Avatars: Uses pravatar.cc for placeholder images
- Delays: Simulated network delays (800ms - 1s)
- Errors: ID
999triggers an error, page 5 triggers pagination error
- Package Documentation: See CLAUDE.md in the root directory
- Riverpod 3.0 Docs: riverpod.dev
- Either Pattern: Uses either_dart
- ✅
AutoDisposeBaseNotifier- Auto-disposing single-value state - ✅
AutoDisposePaginatedNotifier- Auto-disposing paginated state - ✅
BaseState<T>- Sealed class with initial/loading/error/data - ✅
PaginatedState<T>- Sealed class for pagination states - ✅
BaseWidget- Global loading/failure/info handling - ✅
Either<Failure, T>- Functional error handling - ✅
PaginatedList<T>- Pagination metadata wrapper
To use this package in your own project:
- Add the dependency to your
pubspec.yaml - Wrap your app with
BaseWidgetfor global providers - Create notifiers extending
BaseNotifierorPaginatedNotifier - Implement repositories returning
EitherFailureOr<T> - Use pattern matching to handle different states in your UI
- This example uses Riverpod 3.0 syntax (not legacy StateNotifier)
- The package requires
flutter_riverpod: ^3.0.3 - All notifiers use the
prepareForBuild()pattern (don't overridebuild()) - States are sealed classes - use pattern matching instead of
.when()methods