Created
August 18, 2021 23:04
-
-
Save ricbermo/046b38d1c70927be9ca78654a0c237ba to your computer and use it in GitHub Desktop.
Repository-Provider-Controller Patter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CustomException implements Exception { | |
final String message; | |
const CustomException({this.message = 'Something went wrong!'}); | |
@override | |
String toString() => 'CustomException { message: $message }'; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!'); | |
}, | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:mind_peace/models/a_random_model.dart'; | |
abstract class ModelBaseRepository { | |
Future<List<Model>> retrieveData(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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
@ewilliams-zoot
AsyncValue.data([ ...state.data?.value, newItem ])
looks good but I'm using Freezed to facilitate things, like this