Skip to content

Instantly share code, notes, and snippets.

@ewilliams-zoot
Forked from ricbermo/controller.dart
Created August 21, 2021 22:22
Show Gist options
  • Save ewilliams-zoot/99b550b4173e4f2207ac8670cf8c9ac3 to your computer and use it in GitHub Desktop.
Save ewilliams-zoot/99b550b4173e4f2207ac8670cf8c9ac3 to your computer and use it in GitHub Desktop.
Repository-Provider-Controller Patter
// custom provider to improve lists performance
final currentModel = ScopedProvider<Category>(
(_) => throw UnimplementedError(),
);
// used to display snackbars
final exceptionProvider = StateProvider<CustomException>(
(_) => null,
);
final modelListController = StateNotifierProvider<ListController>((ref) => ListController(ref.read)..retrieveData());
class ListController extends StateNotifier<AsyncValue<List<Model>>> {
final Reader _read;
ListController(this._read) : super(const AsyncValue.loading());
Future<void> retrieveData() async {
try {
state = const AsyncValue.loading();
final items = await _read(
repositoryProvider,
).retrieveData();
if (mounted) {
state = AsyncValue.data(items);
}
} on CustomException catch (e) {
_read(exceptionProvider).state = e;
}
}
}
class CustomException implements Exception {
final String message;
const CustomException({this.message = 'Something went wrong!'});
@override
String toString() => 'CustomException { message: $message }';
}
import 'package:dio/dio.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
final dioProvider = Provider<Dio>((ref) {
final dio = Dio();
dio.options.baseUrl = "path/to/server";
return dio;
});
// to understand this, please refer to
// https://www.youtube.com/watch?v=J2iFYZUabVM&t=1041s
final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
throw UnimplementedError();
});
class GridView extends HookWidget {
@override
Widget build(BuildContext context) {
final controller = useProvider(modelListController.state);
return controller.when(
data: (items) => Wrap(
runSpacing: 16,
alignment: WrapAlignment.spaceBetween,
children: List.generate(items.length, (index) {
final item = items[index];
return ProviderScope(
overrides: [currentModel.overrideWithValue(item)],
child: const ModelTile(),
);
}),
),
loading: () => const SkeletonLoader(),
error: (error, _) {
return Text(
error is CustomException ? error.message : 'Something went wrong!');
},
);
}
}
final repositoryProvider = Provider<ModelBaseRepository>(
(ref) => ModelRepository(ref.read),
);
class ModelRepository implements ModelBaseRepository {
final Reader _read;
const ModelRepository(this._read);
@override
Future<List<Model>> retrieveData() async {
try {
final response = await _read(dioProvider).get(
'/path',
);
final data = Map<String, dynamic>.from(
response.data,
);
final jsonAPI = Japx.decode(data);
final results = List<Map<String, dynamic>>.from(jsonAPI['data']);
final List<Model> listOfItems = results
.map(
(data) => Model.fromMap(data),
)
.toList(growable: false);
return listOfItems;
} on DioError catch (err) {
final message = err.response?.statusMessage ?? 'Something went wrong!';
throw CustomException(message: message);
} on SocketException catch (_) {
const message = 'Please check your connection.';
throw const CustomException(message: message);
}
}
}
import 'package:mind_peace/models/a_random_model.dart';
abstract class ModelBaseRepository {
Future<List<Model>> retrieveData();
}
//use ProviderListener to display the snackbar
body: ProviderListener(
provider: exceptionProvider,
onChange: (
BuildContext ctx,
StateController<CustomException> exception,
) {
Scaffold.of(ctx).showSnackBar(
SnackBar(
content: Text(exception.state?.message),
),
);
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(),
),
),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment