-
-
Notifications
You must be signed in to change notification settings - Fork 956
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provider a way to merge AsyncValue together #67
Comments
Once thing you can do is use AsyncValue<int> first;
AsyncValue<int> second;
if (first is AsyncError || second is AsyncError) {
return Text('error');
} else if (first is AsyncLoading || second is AsyncLoading) {
return Text('loading');
}
return Text('${first.data.value} ${seconc.data.value}'); |
Thank you for the example code! This might be my own laziness for not separating them based on their immediate location in the UI but in one place in my code I call down three separate asynchronous resources. This is just what I had previously from my What would be the reason against something like |
It wouldn't to what you want. You're looking at combining multiple What you're probably looking for is something like: final AsyncValue<First> user = useProvider(firstProvider);
final AsyncValue<Second> profile = useProvider(secondProvider);
return AsyncValue.merge(
data: (read) {
return Text('${read(user).name} ${read(profile).name}');
},
loading: () => CircularProgressIndicator(),
error: (err, stack) => Text('error'),
); |
Sorry for the misunderstanding about useProvider. But yes, having that merge ability on the AsyncValue would be very nice to have |
@rrousselGit i have same case to merge multiple AsyncValue to one , if i implement your advice i don't have ability to get error information.
Another solution is using AsyncValue.merge , but i not see merge method. Version Package
|
AsyncValue.merge does not exist yet. |
So for now i can only get error information from multiple AsyncValue using nested
|
AsyncValue<int> value;
if (value is AsyncError<int>) {
print(value.error);
print(value.stack);
} |
@rrousselGit i don't why error and stack method not showing . When i try your example , i can see those method. But when i implement it to my I mistake somewhere ? |
Because your Remove the || or change them into && |
Eh If you need to, you can make multiple ifs or you can use |
i see , thank's for your clarification. For now i think i can't get error and stack information without nested |
About this issue: I'm a bit mixed about #67 (comment), as this wouldn't be very performant and it could cause some confusion with hooks. We could also have an A solution I am considering is to instead continue my previous experiment: https://github.com/rrousselGit/boundary We would then write: class Example extends ConsumerWidget {
@override
Widget build(context, watch) {
AsyncValue<A> first;
AsyncValue<B> second;
A a = unwrap(first);
B b = unwrap(second);
return Text('$a $b');
}
} typically used this way: Widget build(context) {
return Scaffold(
body: Boundary(
loading: (context) => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Center(child: Text('Error $err'),
child: Example(),
),
);
} |
Considering |
What are you referring to? My main concern with AsyncValue<Tuple3<int, String, double>> value;
value.when(
data: (tuple) {
return Text('${tuple.item1} ${tuple.item2} ${tuple.item3}');
});
) That |
I don't think waiting for the Dart team to introduce native touples and destructuting would be the best option considering this proposal is a year old. The package In any case, I'm all in favour of any solution you decide on |
I'm having the same problem now - what about a |
I've just been using this for the meanwhile. Seems to be cover most use cases: AsyncValue<Tuple2<T, R>> combineAsync2<T, R>(
AsyncValue<T> asyncOne,
AsyncValue<R> asyncTwo,
) {
if (asyncOne is AsyncError) {
final error = asyncOne as AsyncError<T>;
return AsyncError(error.error, error.stackTrace);
} else if (asyncTwo is AsyncError) {
final error = asyncTwo as AsyncError<R>;
return AsyncError(error.error, error.stackTrace);
} else if (asyncOne is AsyncLoading || asyncTwo is AsyncLoading) {
return AsyncLoading();
} else if (asyncOne is AsyncData && asyncTwo is AsyncData) {
return AsyncData(Tuple2<T, R>(asyncOne.data.value, asyncTwo.data.value));
} else {
throw 'Unsupported case';
}
} I've only needed to go up to 4 async values at one time so that's what I left it at. Here's the gist. Note this requires |
That only covers the case of two AsyncValue's, I was looking for a generic case of an array of values, but it should not be too hard to make. |
Would it make sense to make a common base class for |
The trouble with a list is that you lose out on type safety. You have to know the position of each value to properly type it once you get the values out. Sorry too, I only showed the |
I was thinking something like rxdart's |
I made a repositiory as a package combined_provider if you want to check out. |
@erf I see where you're coming from now and I disagree with the need for the combineProviders you suggest. If you want a value from a If you want a value from a class Example extends ConsumerWidget {
@override
Widget build(BuildContext context, reader) {
final myChangeNotifier = reader(myChangeNotifierProvider);
final myState = reader(myStateProvider);
//
}
} If you need to filter rebuilds, then you can move all of your class Example extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, watch, child) {
final myChangeNotifier = watch(myChangeNotifierProvider);
final myState = watch(myStateProvider);
//
}
);
}
} The reason async providers are special cases is the different states their values can be from loading, error and data but you can't easily bunch them up without (possibly deeply) nesting them to account for all the states. class Example extends ConsumerWidget {
@override
Widget build(BuildContext context, reader) {
return reader(myStream).when(
data: (data1) {
return reader(mySecondStream).when(
data: (data2) => Text('$data1, $data2'),
loading: loading,
error: error,
);
},
loading: loading,
error: error,
);
}
} |
You can keep using |
This issue will likely not be fixed before metaprogramming. I really don't want to promote tuples because of their bad variable name, therefore we need structures/records. |
Hi @rrousselGit any update on merging the AsyncValue ? This would reduce lot of code in UI |
I don't think the loading/errorBuilder approach mentioned before is a good idea. I think we'll need code generation. I'm currently considering: final userProvider = FutureProvider<User>();
final familyExampleProvider = FutureProvider.family<Foo, Param>();
@AsyncValue.all({userProvider, familyExampleProvider})
final groupProvider = _$group;
Widget build(context, ref) {
final group = ref.watch(
groupProvider(
// for families, we need to pass in the provider with its arguments
familyExampleProvider: familyExampleProvider(Param()),
),
);
return group.when(
loading: () => Loading(),
error: (err, stack) => Text('...'),
data: (Group group) {
return Text('a: ${group.user}, b: ${group.familyExample}');
},
);
} |
I'll implement this code-generation proposal shortly after the 2.0 |
I personally implemented an extension like this : typedef MultiAsync<T> = Iterable<AsyncValue<T>>;
extension MultiAsyncExtension<T> on MultiAsync<T> {
/// Performs an action based on the state of the [AsyncValue].
///
/// All cases are required, which allows returning a non-nullable value.
R when<R>({
required R Function(Iterable<T> data) data,
required R Function(
Iterable<Object> error,
Iterable<StackTrace?> stackTrace,
)
error,
required R Function() loading,
}) {
if (any((element) => element is AsyncError)) {
final err = where(
(element) => element is AsyncError,
).map((e) => e as AsyncError);
return error(
err.map((e) => e.error),
err.map((e) => e.stackTrace),
);
} else if (any((element) => element is AsyncLoading)) {
return loading();
} else if (every((element) => element is AsyncData)) {
return data(map((e) => e.value as T));
} else {
return error([], []);
}
}
} it's not perfect but it does the job quite well IMO to use it you can use it as a regular when but decomposing the data iterable : like [async1, async2, async3].when(
data: (data) {
data1 = data.elementAt(0) as Type1?;
data2 = data.elementAt(1) as Type2?;
} and then check for null values and if needed return a AsyncError. |
another way to "merging" AsyncValue together, is to create a new provider. final isLoggedIn = FutureProvider<bool>((ref) async {
final user = await ref.watch(userProvider);
final profile = await ref.watch(profileProvider);
return user != null && profile != null;
});
|
That's what I do for now |
I think this final isLoggedIn = Provider<AsyncValue<bool>>((ref) async {
final user = ref.watch(userProvider);
final profile = ref.watch(profileProvider);
return user.when(
data: (user) => profile.when(
data: (profile) => AsyncData(true),
loading: () => AsyncData(false),
error: (e, st) => AsyncData(false),
),
loading: () => AsyncData(false),
error: (e, st) => AsyncData(false),
);
}); |
so what's the conclusion? ❓ |
Well for starters, "when" is now discouraged in favor of pattern matching. And pattern matching doesn't quite suffer from this issue as much. Because pattern-matching can have the same "switch" handle loading/error of multiple AsyncValues |
How would you handle this with pattern matching in one switch? I haven't played with Dart 3 much yet, so maybe it's an obvious question. I think we could use a record, like |
It would also be great to somehow manage an async value that is dependent on another asyncvalue resolving |
Could you show an example of having using a switch expression for multiple AsyncValues? |
I use a function that takes a tuple as its input. final serviceAsync = ref.watch(serviceProvider);
final otherServiceAsync = ref.watch(otherServiceProvider);
final widget = switch((serviceAsync, otherServiceAsync)) {
( AsyncValue(value: final service, hasValue: true),
AsyncValue(value: final otherService, hasValue: true))
=> _bodyBuilder(service, otherService),
(AsyncError(:final error), _) || (_, AsyncError(:final error))
=> Text("Error: $error"),
_ => const LoadingAnimation(),
}; But I'm not satisfied with the error handling. 🤔 |
It seems to me that this new patterns are hard to read and look creepy I'm trying this one now: final profileAsync = ref.watch(profileProvider);
final productAsync = ref.watch(productProvider);
return AsyncBuilder(
values: [profileAsync, productAsync],
builder: (context) {
final profile = profileAsync.requireValue;
final product = productAsync.requireValue;
return ChildWidget(...);
},
// optional, if null default builder used
loadingBuilder: (context) {
return LoadingIndicator();
},
// optional, if null default builder used
errorBuilder: (context, error, stackTrace) {
return ErrorWidget(error, stackTrace);
},
); |
For my part, I simplify with the code below final service = ref.watch(serviceProvider).value;
final other = ref.watch(otherServiceProvider).value;
return switch((service, other)) {
(var s?, var o?) => _bodyBuilder(s, o),
_ => const LoadingAnimation(),
}; In this case, the variables 's' and 'o' are non-nullable, and obviously, you should catch errors in a |
This is my approach class Async2ValueWidget<T, U> extends StatelessWidget {
final ({AsyncValue<T> value1, AsyncValue<U> value2}) values;
final bool showLoadingIndicator;
final Widget? loadingWidget;
final Widget Function(T data1, U data2) builder;
const Async2ValueWidget({
super.key,
required this.values,
this.showLoadingIndicator = false,
this.loadingWidget,
required this.builder,
});
@override
Widget build(BuildContext context) {
// When both values are data
if (values
case (
value1: AsyncData<T>(value: final data1),
value2: AsyncData<U>(value: final data2)
)) {
return builder(data1, data2);
}
// When one of the values is loading
if (values
case (
value1: AsyncValue<T>(isLoading: final isLoading1),
value2: AsyncValue<U>(isLoading: final isLoading2)
)) {
if (isLoading1 || isLoading2) {
return loadingWidget ??
_LoadingIndicator(hidden: !showLoadingIndicator);
}
}
// When one of the values is an error
if (values
case (
value1: AsyncValue<T>(error: final error1, stackTrace: final _),
value2: AsyncValue<U>(error: final error2, stackTrace: final _)
)) {
if (error1 != null) {
return Center(child: ErrorMessageWidget(error1.toString()));
}
if (error2 != null) {
return Center(child: ErrorMessageWidget(error2.toString()));
}
}
// This should never happen
throw UnimplementedError();
}
} Very easy to read, the only problem with this is you need to specify T and U manually because for some reason 'builder' function can't infer the correct type Example: Async2ValueWidget<int?, bool>(
values: (
value1: selectedAnswerIndex,
value2: isExamMode,
),
// selectedAnswerIndexValue is int?, isExamModeValue is bool
builder: (selectedAnswerIndexValue, isExamModeValue) =>
ListView.separated(
... |
Hello, everyone. My approach with async_value_group. Example: class TweetsPage extends HookConsumerWidget {
const TweetsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return AsyncValueGroup.group2(
ref.watch(tweetsProvider),
ref.watch(userProvider),
).when(
data: (t) => t.$1.isEmpty ? const TweetsEmpty() : TweetsBody(tweets: t.$1, user: t.$2),
error: (error, st) => ErrorPage(error: error),
loading: () => const Loading(),
);
}
} While reading through this issue, I also came across examples of pattern matching available from Dart 3, which looks fantastic. For those who still want to use .when, my async_value_group could also be a useful reference. |
I merge AsyncValue together by Seems everything is ok as below code: typedef DependencyServices = ({
GenerateAppTitle onGenerateAppTitle,
Iterable<LocalizationsDelegate<dynamic>> delegates,
Iterable<Locale> supportedLocales,
LocaleListResolutionCallback localeListResolutionCallback,
Locale? localeUserPreferred,
});
@Riverpod()
Future<DependencyServices> someServices(SomeServicesRef ref) async {
return (
onGenerateAppTitle:
await ref.watch(onGenerateAppTitleControllerProvider.future),
delegates: await ref.watch(delegatesControllerProvider.future),
supportedLocales:
await ref.watch(supportedLocalesControllerProvider.future),
localeListResolutionCallback:
await ref.watch(localeListResolutionCallbackControllerProvider.future),
localeUserPreferred:
await ref.watch(localeUserPreferredControllerProvider.future),
);
} class MaterialLocalizationApp extends HookConsumerWidget {
final ThemeData? theme;
final Widget? home;
const MaterialLocalizationApp({
super.key,
this.theme,
this.home,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<DependencyServices> someServicesAsync =
ref.watch(someServicesProvider);
return someServicesAsync.when(
data: (DependencyServices s) {
final (
:onGenerateAppTitle,
:delegates,
:supportedLocales,
:localeListResolutionCallback,
:localeUserPreferred,
) = s;
/*final AsyncValue<Locale?> localeUserPreferredAsync =
ref.watch(localeUserPreferredControllerProvider);
return localeUserPreferredAsync.when(
data: (Locale? localeUserPreferred) { */
debugPrint(
'MaterialLocalizationApp # build localeUserPreferred = $localeUserPreferred s(${s.hashCode}) = $s');
return MaterialApp(
onGenerateTitle: onGenerateAppTitle,
localizationsDelegates: delegates,
supportedLocales: supportedLocales,
localeListResolutionCallback: localeListResolutionCallback,
locale: localeUserPreferred,
theme: theme,
home: home,
);
/*},
loading: () =>
const Center(child: CircularProgressIndicator(color: Colors.yellow)),
error: (err, _) => Center(
child: Text(
err.toString(),
style: const TextStyle(
color: Colors.yellow,
),
),
),
);*/
},
loading: () =>
const Center(child: CircularProgressIndicator(color: Colors.red)),
error: (err, _) => Center(
child: Text(
err.toString(),
style: const TextStyle(
color: Colors.red,
),
)),
);
}
} But has a bug, it will cause the Could you give me some help? Thanks.
|
is there a plan to solve this limitation/boilerplateness in riverpod v3 ? any alternative best plan for now ? |
For combining streams, rxdart has this This looks kinda neat: // Combine the AsyncValue into a single callback
// Each AsyncValue has its own Consumer widget, so rebuilts no trigger all the `watch`s again.
return authProvider.merge(launchStatsProvider).build(
(context, ref, auth, launchStats) {
return Text(
'LoggedIn: ${auth.isAuthenticated} - Total Launches: ${launchStats.totalLaunches}');
},
(context, ref, error, stackTrace, erroredProvider) {
return const Text('Oops, something unexpected happened');
},
(context, ref, provider) {
return const CircularProgressIndicator();
},
); @rrousselGit Would you be interested in a PR that add something like this? Implementation
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
extension AsyncValueProviderCombinerExt<T> on ProviderBase<AsyncValue<T>> {
AsyncValueProviderCombiner2<T, P> merge<P>(ProviderBase<AsyncValue<P>> b) {
return AsyncValueProviderCombiner2(this, b);
}
}
typedef OnErrorCombine = Widget Function(
BuildContext context,
WidgetRef ref,
Object error,
StackTrace stackTrace,
ProviderBase<AsyncValue<dynamic>> provider);
typedef OnLoadingCombine = Widget Function(BuildContext context, WidgetRef ref,
ProviderBase<AsyncValue<dynamic>> provider);
class BaseAsyncValueProviderCombiner {
const BaseAsyncValueProviderCombiner();
Consumer _makeConsumer(
Iterable<ProviderBase<AsyncValue<dynamic>>> providers,
List results,
Function data,
OnErrorCombine error,
OnLoadingCombine loading,
int providerCount) {
return Consumer(
builder: (context, ref, child) {
if (providers.isEmpty) {
return data(
context, ref, results[0], results[1], results[2], results[3]);
} else {
return ref.watch(providers.first).when(
data: (data) => _makeConsumer(providers.skip(1),
results..add(data), data, error, loading, providerCount),
error: (err, st) => error(context, ref, err, st, providers.first),
loading: () => loading(context, ref, providers.first));
}
},
);
}
}
class AsyncValueProviderCombiner2<A, B> extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
const AsyncValueProviderCombiner2(this.a, this.b);
AsyncValueProviderCombiner3<A, B, C> merge<C>(ProviderBase<AsyncValue<C>> c) {
return AsyncValueProviderCombiner3(a, b, c);
}
Widget build(Widget Function(BuildContext context, WidgetRef ref, A, B) data,
OnErrorCombine error, OnLoadingCombine loading) =>
_makeConsumer([a, b], [], data, error, loading, 2);
}
class AsyncValueProviderCombiner3<A, B, C>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
const AsyncValueProviderCombiner3(this.a, this.b, this.c);
AsyncValueProviderCombiner4<A, B, C, D> merge<D>(
ProviderBase<AsyncValue<D>> d) {
return AsyncValueProviderCombiner4(a, b, c, d);
}
Widget build(
Widget Function(BuildContext context, WidgetRef ref, A, B, C) data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer([a, b, c], [], data, error, loading, 3);
}
class AsyncValueProviderCombiner4<A, B, C, D>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
final ProviderBase<AsyncValue<D>> d;
const AsyncValueProviderCombiner4(this.a, this.b, this.c, this.d);
AsyncValueProviderCombiner5<A, B, C, D, E> merge<E>(
ProviderBase<AsyncValue<E>> e) {
return AsyncValueProviderCombiner5(a, b, c, d, e);
}
Widget build(
Widget Function(BuildContext context, WidgetRef ref, A, B, C, D) data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer([a, b, c, d], [], data, error, loading, 4);
}
class AsyncValueProviderCombiner5<A, B, C, D, E>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
final ProviderBase<AsyncValue<D>> d;
final ProviderBase<AsyncValue<E>> e;
const AsyncValueProviderCombiner5(this.a, this.b, this.c, this.d, this.e);
AsyncValueProviderCombiner6<A, B, C, D, E, F> merge<F>(
ProviderBase<AsyncValue<F>> f) {
return AsyncValueProviderCombiner6(a, b, c, d, e, f);
}
Widget build(
Widget Function(BuildContext context, WidgetRef ref, A, B, C, D, E)
data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer([a, b, c, d, e], [], data, error, loading, 5);
}
class AsyncValueProviderCombiner6<A, B, C, D, E, F>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
final ProviderBase<AsyncValue<D>> d;
final ProviderBase<AsyncValue<E>> e;
final ProviderBase<AsyncValue<F>> f;
const AsyncValueProviderCombiner6(
this.a, this.b, this.c, this.d, this.e, this.f);
AsyncValueProviderCombiner7<A, B, C, D, E, F, G> merge<G>(
ProviderBase<AsyncValue<G>> g) {
return AsyncValueProviderCombiner7(a, b, c, d, e, f, g);
}
Widget build(
Widget Function(BuildContext context, WidgetRef ref, A, B, C, D, E, F)
data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer([a, b, c, d, e, f], [], data, error, loading, 6);
}
class AsyncValueProviderCombiner7<A, B, C, D, E, F, G>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
final ProviderBase<AsyncValue<D>> d;
final ProviderBase<AsyncValue<E>> e;
final ProviderBase<AsyncValue<F>> f;
final ProviderBase<AsyncValue<G>> g;
const AsyncValueProviderCombiner7(
this.a, this.b, this.c, this.d, this.e, this.f, this.g);
AsyncValueProviderCombiner8<A, B, C, D, E, F, G, H> merge<H>(
ProviderBase<AsyncValue<H>> h) {
return AsyncValueProviderCombiner8(a, b, c, d, e, f, g, h);
}
Widget build(
Widget Function(
BuildContext context, WidgetRef ref, A, B, C, D, E, F, G)
data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer([a, b, c, d, e, f, g], [], data, error, loading, 7);
}
class AsyncValueProviderCombiner8<A, B, C, D, E, F, G, H>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
final ProviderBase<AsyncValue<D>> d;
final ProviderBase<AsyncValue<E>> e;
final ProviderBase<AsyncValue<F>> f;
final ProviderBase<AsyncValue<G>> g;
final ProviderBase<AsyncValue<H>> h;
const AsyncValueProviderCombiner8(
this.a, this.b, this.c, this.d, this.e, this.f, this.g, this.h);
AsyncValueProviderCombiner9<A, B, C, D, E, F, G, H, I> merge<I>(
ProviderBase<AsyncValue<I>> i) {
return AsyncValueProviderCombiner9(a, b, c, d, e, f, g, h, i);
}
Widget build(
Widget Function(
BuildContext context, WidgetRef ref, A, B, C, D, E, F, G, H)
data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer([a, b, c, d, e, f, g, h], [], data, error, loading, 8);
}
class AsyncValueProviderCombiner9<A, B, C, D, E, F, G, H, I>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
final ProviderBase<AsyncValue<D>> d;
final ProviderBase<AsyncValue<E>> e;
final ProviderBase<AsyncValue<F>> f;
final ProviderBase<AsyncValue<G>> g;
final ProviderBase<AsyncValue<H>> h;
final ProviderBase<AsyncValue<I>> i;
const AsyncValueProviderCombiner9(
this.a, this.b, this.c, this.d, this.e, this.f, this.g, this.h, this.i);
AsyncValueProviderCombiner10<A, B, C, D, E, F, G, H, I, J> merge<J>(
ProviderBase<AsyncValue<J>> j) {
return AsyncValueProviderCombiner10(a, b, c, d, e, f, g, h, i, j);
}
Widget build(
Widget Function(BuildContext context, WidgetRef ref, A, B, C, D, E, F,
G, H, I)
data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer([a, b, c, d, e, f, g, h, i], [], data, error, loading, 9);
}
class AsyncValueProviderCombiner10<A, B, C, D, E, F, G, H, I, J>
extends BaseAsyncValueProviderCombiner {
final ProviderBase<AsyncValue<A>> a;
final ProviderBase<AsyncValue<B>> b;
final ProviderBase<AsyncValue<C>> c;
final ProviderBase<AsyncValue<D>> d;
final ProviderBase<AsyncValue<E>> e;
final ProviderBase<AsyncValue<F>> f;
final ProviderBase<AsyncValue<G>> g;
final ProviderBase<AsyncValue<H>> h;
final ProviderBase<AsyncValue<I>> i;
final ProviderBase<AsyncValue<J>> j;
const AsyncValueProviderCombiner10(this.a, this.b, this.c, this.d, this.e,
this.f, this.g, this.h, this.i, this.j);
Widget build(
Widget Function(BuildContext context, WidgetRef ref, A, B, C, D, E, F,
G, H, I, J)
data,
OnErrorCombine error,
OnLoadingCombine loading) =>
_makeConsumer(
[a, b, c, d, e, f, g, h, i, j], [], data, error, loading, 10);
}
|
Is your feature request related to a problem? Please describe.
I'm refactoring a small app I have to use river_pods but one thing I keep wanting is a way to combine providers in a
when
ormaybeWhen
so they both get loaded at the same time in the case they're asynchronous.Describe the solution you'd like
Or, another example, all possible badges (pulled from a json file) and the user's current badges.
Describe alternatives you've considered
I can do what I want by checking both the
.data
values on the providers to check if they're null but then I lose out on the error case ofwhen
:Or for the other example, I currently do this:
Additional context
hooks_riverpod: 0.6.0-dev
The text was updated successfully, but these errors were encountered: