riverpod: Pattern for dealing with loading AsyncValue inside an Provider or FutureProvider? - riverpod

The question is what to return in case an inner provider is loading.
final myProvider = Provider<int>((ref) {
final myAsyncValue = ref.watch(someFutureProvider);
return myAsyncValue.when(data: compute_something,
loading: what_to_do_here,
error: (_, __) => throw Exception());
}
And what about if myProvider is a FutureProvider?
Apologies if I am fundamentally misunderstanding riverpod.
thx

Related

Is there an idiomatic way to parse JSON into objects with a shared base class but differing data shapes?

I'm brand new to Dart so I've just been getting my feet wet implementing an SDK for an existing API,
but I'm running into some issues figuring out how to structure things to cut down on boilerplate when
I'm parsing the responses from the server.
Basically, all responses from the API look like the following:
{
"data": {
...
},
"error": {
...
}
}
where either data or error is present, but never both. Fairly standard stuff. The shape of the
error isn't all that interesting since it's the same for every response, but the data object is
different for every response. So far I've been able to get something "working" by declaring a base class
that handles the error and passing that up through super (I should note I'm using the
json_serializer package):
class Response {
Response({this.error});
Error? error;
}
#JsonSerializable(createToJson: false)
class ExampleResponse extends Response {
ExampleResponse({
this.data,
Error? error,
}) : super(error: error);
Map<String, dynamic>? data;
factory ExampleResponse.fromJson(Map<String, dynamic> json) =>
_$ExampleResponseFromJson(json);
}
This works but I lose all of the type information for the data field. I'm not sure how to approach this
from here that doesn't involve yet another class for the data shape:
#JsonSerializable()
class Thing {
Thing(this.name);
String name;
factory Thing.fromJson(Map<String, dynamic> json) => _$ThingFromJson(json);
String toString() => 'Thing{name: $name}';
}
#JsonSerializable()
class ExampleResponseData {
ExampleResponseData(this.thing);
Thing thing;
factory ExampleResponseData.fromJson(Map<String, dynamic> json) =>
_$ExampleResponseDataFromJson(json);
String toString() => 'ExampleResponseData{thing: $thing}';
}
#JsonSerializable(createToJson: false)
class ExampleResponse extends Response {
ExampleResponse({
this.data,
Error? error,
}) : super(error: error);
ExampleResponseData? data;
factory ExampleResponse.fromJson(Map<String, dynamic> json) =>
_$ExampleResponseFromJson(json);
String toString() => 'ExampleResponse{error: $error, data: $data}';
}
In Go I would just define an inner struct for occassions where the type has no value outside of JSON
parsing like:
type ExampleResponse struct {
Error *Error `json:"error"`
Data *struct {
Thing Thing `json:"thing"`
} `json:"thing"`
}
but I'm a lot rustier with OOP in general and on even more unfamiliar ground with Dart. Is there a
better way to handle this that doesn't end up with twice as many types as there are responses?
You can find solution based on built_value. Idea is to use generic for data type. Built value can handle such cases, though you will have a little bit of boilerplate. Below you can find example:
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
part 'built_value_example.g.dart';
void main(List<String> arguments) {
final resultAJson = endpointA();
final resultA = serializers.deserialize(resultAJson, specifiedType: FullType(Response, [FullType(DataA)]));
print(resultA);
final resultBJson = endpointB();
final resultB = serializers.deserialize(resultBJson, specifiedType: FullType(Response, [FullType(DataB)]));
print(resultB);
}
Map<String, dynamic> endpointA() {
final data = DataA((b) => b..dataAField = 7);
final response = Response<DataA>((b) => b..data = data);
return serializers.serialize(response, specifiedType: FullType(Response, [FullType(DataA)])) as Map<String, dynamic>;
}
Map<String, dynamic> endpointB() {
final data = DataB((b) => b..dataBField = 'data b value');
final response = Response<DataB>((b) => b..data = data..error = 'data b error');
return serializers.serialize(response, specifiedType: FullType(Response, [FullType(DataB)])) as Map<String, dynamic>;
}
abstract class Response<DATA_TYPE> implements Built<Response<DATA_TYPE>, ResponseBuilder<DATA_TYPE>> {
Response._();
factory Response([Function(ResponseBuilder<DATA_TYPE> b) updates]) = _$Response<DATA_TYPE>;
static Serializer<Response> get serializer => _$responseSerializer;
DATA_TYPE get data;
String? get error;
}
abstract class DataA implements Built<DataA, DataABuilder> {
DataA._();
factory DataA([Function(DataABuilder b) updates]) = _$DataA;
static Serializer<DataA> get serializer => _$dataASerializer;
int get dataAField;
}
abstract class DataB implements Built<DataB, DataBBuilder> {
DataB._();
factory DataB([Function(DataBBuilder b) updates]) = _$DataB;
static Serializer<DataB> get serializer => _$dataBSerializer;
String get dataBField;
}
#SerializersFor([
DataA,
DataB,
Response,
])
final Serializers serializers = (_$serializers.toBuilder()
..addPlugin(StandardJsonPlugin())
..addBuilderFactory(FullType(Response, [FullType(DataA)]), () => ResponseBuilder<DataA>())
..addBuilderFactory(FullType(Response, [FullType(DataB)]), () => ResponseBuilder<DataB>()))
.build();

Avoid repetition in BLoCs and RxDart

hopefully I can make myself clear.
After video and tutorials, I found this way to have some widgets to input data to the bloc (valueSetting) and some others to get this data (value).
What I am asking is if there is a better way (there has to be..). I want to avoid the need to have 4 variables for just 1 real value shared between widgets.
import 'dart:async';
import 'package:rxdart/subjects.dart';
class BlocExample {
final _valueSettingController = StreamController<bool>();
// object use by widget to push data
Sink<bool> get valueSetting => _valueSettingController.sink;
final _value = BehaviorSubject<bool>(seedValue: false);
// object used by widget to get data
Stream<bool> get value => _value.stream;
BlocExample() {
_valueSettingController.stream.listen(_value.add);
}
void dispose() {
_value.close();
_valueSettingController.close();
}
}
First of, let me say that you can remove the private variables by using a custom factory constructor. Here's an example:
class MyBloc {
final Sink<bool> input;
final Stream<bool> output;
final VoidCallback _dispose;
MyBloc._({this.input, this.output, VoidCallback dispose}) : _dispose = dispose;
factory MyBloc() {
final mainController = BehaviorSubject(seedValue: false);
return MyBloc._(
input: mainController.sink,
output: mainController.stream,
dispose: () {
mainController.close();
},
);
}
void dispose() {
_dispose();
}
}
Secondly, the problem you're trying to solve is actually not a problem. While it seems at first that there's a lot of duplicates; in reality they serve different purposes.
In many situations, your Stream will be more than just _controller.stream. For example, for whatever reason you may want to transform the value before exposing it:
final mainController = BehaviorSubject(seedValue: false);
final Stream<bool> output = mainController.map((foo) => !foo);
This code makes that the output stream reverses the value of everything passed to mainController.sink
But in my situation this is not the case. So why 3 variables that point to the same thing?
The fact that in your situation, your controller is both the sink and stream without transformation is an implementation detail and may be subject to changes.
By exposing Sink/Stream as done before, you actually abstract this implementation detail. So that in the future if your stream needs custom operations; no change will be required by your UI.
This is not necessary. But recommended.
You can do something like this :)
enum STREAM_GROUP {
TYPE1,TYPE2,TYPE3
}
class BlocExample {
Map<STREAM_GROUP, StreamController<bool>> groups = new Map();
Stream<bool> getValue(STREAM_GROUP type){
return groups[type].stream;
}
Sink<bool> getValueSetting(STREAM_GROUP type){
return groups[type].sink;
}
BlocExample() {
groups[STREAM_GROUP.TYPE1] = StreamController<bool>();
groups[STREAM_GROUP.TYPE2] = StreamController<bool>();
groups[STREAM_GROUP.TYPE3] = StreamController<bool>();
groups.forEach((groupType, streamController){
final currentValue = BehaviorSubject<bool>(seedValue: false);
streamController.stream.listen(currentValue.add);
});
}
void dispose() {
groups.forEach((groupType, streamController){
streamController.close();
});
}
}

compilation Error in Task.ContinueWith when passing TaskScheduler.FromCurrentSynchronizationContext as second parameter

private void CountWithTask_Click(object sender, RoutedEventArgs e)
{
lblOutput.Content = "file is in processing...";
var taskInt = CountWords();
taskInt.ContinueWith(x =>
{
MessageBox.Show("process has been done.");
lblOutput.Content = $"count of words in file are :- {x.Result}";
},TaskScheduler.FromCurrentSynchronizationContext);
}
}
in the code snippet where I have return "x.Result",I am getting the compilation error at that point. and the error is image 1
"Task does not contain a definition for 'result' and no extension method 'Result' accepting a first argument of type 'Task' could be found (are you missing a using directive or an assembly refernce?)"
if I remove that line then it is giving error at TaskScheduler, and the error is
"Argument 2: cannot convert from 'method-group' to Cacellationtoken."
enter image description here
code for the CountWords method:
public async Task<int> CountWords()
{
Debug.WriteLine(string.Format($"Thread ID: {Thread.CurrentThread.ManagedThreadId.ToString()}"));
await Task.Delay(2000);
int count;
StreamReader reader = new StreamReader(FileName);
var str = await reader.ReadToEndAsync();
count = str.Length;
reader.Close();
return count;
}
I got the issue the issue was very small my mistake and my negligence towards the intelligence. :)
I am using only "TaskScheduler.FromCurrentSynchronizationContext", which is wrong becuase "FromCurrentSynchronizationContext" is a method and it should be like this "TaskScheduler.FromCurrentSynchronizationContext()".
Proper code would be this :
private void CountWithTask_Click(object sender, RoutedEventArgs e)
{
lblOutput.Content = "file is in processing...";
var taskInt = CountWords();
taskInt.ContinueWith(x =>
{
MessageBox.Show("process has been done.");
lblOutput.Content = $"count of words in file are :- {x.Result}";
},TaskScheduler.FromCurrentSynchronizationContext());
}
}
Thanks for the response #scott.
Thanks Stackoverflow. :)

Async/future error handling in Dart not working as expected

I've been at this for hours studying the Futures and Error Handling section on the Dart page without any luck. Can anyone explain why the code below does not print All good?
import 'dart:async';
main() async {
try {
await func1();
} catch (e) {
print('All good');
}
}
Future func1() {
var completer = new Completer();
func2().catchError(() => completer.completeError('Noo'));
return completer.future;
}
Future func2() {
var completer = new Completer();
completer.completeError('Noo');
return completer.future;
}
In func1 the function used as catchError parameter must be a subtype of type (dynamic) => dynamic regarding the error:
Unhandled exception:
type '() => dynamic' is not a subtype of type '(dynamic) => dynamic' of 'f'.
Thus you should use:
Future func1() {
var completer = new Completer();
func2().catchError((e) => completer.completeError('Noo'));
return completer.future;
}
You don't get any analyzer error because the parameter is typed with Function. You can file an issue to know why the type is not a more specific type matching (dynamic)=>dynamic

Creating an instance of a generic type in DART

I was wondering if is possible to create an instance of a generic type in Dart. In other languages like Java you could work around this using reflection, but I'm not sure if this is possible in Dart.
I have this class:
class GenericController <T extends RequestHandler> {
void processRequest() {
T t = new T(); // ERROR
}
}
I tried mezonis approach with the Activator and it works. But it is an expensive approach as it uses mirrors, which requires you to use "mirrorsUsed" if you don't want to have a 2-4MB js file.
This morning I had the idea to use a generic typedef as generator and thus get rid of reflection:
You define a method type like this: (Add params if necessary)
typedef S ItemCreator<S>();
or even better:
typedef ItemCreator<S> = S Function();
Then in the class that needs to create the new instances:
class PagedListData<T>{
...
ItemCreator<T> creator;
PagedListData(ItemCreator<T> this.creator) {
}
void performMagic() {
T item = creator();
...
}
}
Then you can instantiate the PagedList like this:
PagedListData<UserListItem> users
= new PagedListData<UserListItem>(()=> new UserListItem());
You don't lose the advantage of using generic because at declaration time you need to provide the target class anyway, so defining the creator method doesn't hurt.
You can use similar code:
import "dart:mirrors";
void main() {
var controller = new GenericController<Foo>();
controller.processRequest();
}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T();
T t = Activator.createInstance(T);
t.tellAboutHimself();
}
}
class Foo extends RequestHandler {
void tellAboutHimself() {
print("Hello, I am 'Foo'");
}
}
abstract class RequestHandler {
void tellAboutHimself();
}
class Activator {
static createInstance(Type type, [Symbol constructor, List
arguments, Map<Symbol, dynamic> namedArguments]) {
if (type == null) {
throw new ArgumentError("type: $type");
}
if (constructor == null) {
constructor = const Symbol("");
}
if (arguments == null) {
arguments = const [];
}
var typeMirror = reflectType(type);
if (typeMirror is ClassMirror) {
return typeMirror.newInstance(constructor, arguments,
namedArguments).reflectee;
} else {
throw new ArgumentError("Cannot create the instance of the type '$type'.");
}
}
}
I don't know if this is still useful to anyone. But I have found an easy workaround. In the function you want to initialize the type T, pass an extra argument of type T Function(). This function should return an instance of T. Now whenever you want to create object of T, call the function.
class foo<T> {
void foo(T Function() creator) {
final t = creator();
// use t
}
}
P.S. inspired by Patrick's answer
2022 answer
Just came across this problem and found out that although instantiating using T() is still not possible, you can get the constructor of an object easier with SomeClass.new in dart>=2.15.
So what you could do is:
class MyClass<T> {
final T Function() creator;
MyClass(this.creator);
T getGenericInstance() {
return creator();
}
}
and when using it:
final myClass = MyClass<SomeOtherClass>(SomeOtherClass.new)
Nothing different but looks cleaner imo.
Here's my work around for this sad limitation
class RequestHandler {
static final _constructors = {
RequestHandler: () => RequestHandler(),
RequestHandler2: () => RequestHandler2(),
};
static RequestHandler create(Type type) {
return _constructors[type]();
}
}
class RequestHandler2 extends RequestHandler {}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T(); // ERROR
T t = RequestHandler.create(T);
}
}
test() {
final controller = GenericController<RequestHandler2>();
controller.processRequest();
}
Sorry but as far as I know, a type parameter cannot be used to name a constructor in an instance creation expression in Dart.
Working with FLutter
typedef S ItemCreator<S>();
mixin SharedExtension<T> {
T getSPData(ItemCreator<T> creator) async {
return creator();
}
}
Abc a = sharedObj.getSPData(()=> Abc());
P.S. inspired by Patrick
simple like that.
import 'dart:mirrors';
void main(List<String> args) {
final a = A<B>();
final b1 = a.getInstance();
final b2 = a.getInstance();
print('${b1.value}|${b1.text}|${b1.hashCode}');
print('${b2.value}|${b2.text}|${b2.hashCode}');
}
class A<T extends B> {
static int count = 0;
T getInstance() {
return reflectClass(T).newInstance(
Symbol(''),
['Text ${++count}'],
{Symbol('value'): count},
).reflectee;
}
}
class B {
final int value;
final String text;
B(this.text, {required this.value});
}
Inspired by Patrick's answer, this is the factory I ended up with.
class ServiceFactory<T> {
static final Map<Type, dynamic> _cache = <String, dynamic>{};
static T getInstance<T>(T Function() creator) {
String typeName = T.toString();
return _cache.putIfAbsent(typeName, () => creator());
}
}
Then I would use it like this.
final authClient = ServiceFactory.getInstance<AuthenticationClient>(() => AuthenticationClient());
Warning: Erik made a very good point in the comment below that the same type name can exist in multiple packages and that will cause issues. As much as I dislike to force the user to pass in a string key (that way it's the consumer's responsibility to ensuring the uniqueness of the type name), that might be the only way.

Resources