Skip to content

Commit d5e3bc4

Browse files
authored
New async child (#567)
This PR is meant to extend the current AsyncChild to handle multiple AsyncValues simultaneously to avoid having to use AsyncChild in AsyncChild. For example with Phonebook we need to fetch associations and associationKinds and both are mixed up, thus we can't show any of them separately and we need to wait for both to be available. With this one widget would be sufficient.
1 parent 1de22f5 commit d5e3bc4

File tree

1 file changed

+218
-3
lines changed

1 file changed

+218
-3
lines changed

lib/tools/ui/builders/async_child.dart

Lines changed: 218 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_riverpod/flutter_riverpod.dart';
33
import 'package:titan/tools/constants.dart';
44
import 'package:titan/tools/ui/widgets/loader.dart';
5+
import 'package:tuple/tuple.dart';
56

6-
class AsyncChild<T> extends StatelessWidget {
7-
final AsyncValue<T> value;
8-
final Widget Function(BuildContext context, T value) builder;
7+
class AsyncChild<P> extends StatelessWidget {
8+
final AsyncValue<P> value;
9+
final Widget Function(BuildContext context, P value) builder;
910
final Widget Function(Object? error, StackTrace? stack)? errorBuilder;
1011
final Widget Function(BuildContext context)? loadingBuilder;
1112
final Widget Function(BuildContext context, Widget child)? orElseBuilder;
@@ -42,3 +43,217 @@ class AsyncChild<T> extends StatelessWidget {
4243
);
4344
}
4445
}
46+
47+
Widget handleLoadingAndError(
48+
List<AsyncValue> values,
49+
BuildContext context, {
50+
Widget Function(BuildContext context)? loadingBuilder,
51+
Widget Function(Object? error, StackTrace? stack)? errorBuilder,
52+
Widget Function(BuildContext context, Widget child)? orElseBuilder,
53+
Color? loaderColor,
54+
}) {
55+
final nonNullOrElseBuilder = orElseBuilder ?? (context, child) => child;
56+
if (values.any((value) => value.hasError)) {
57+
final nonNullErrorBuilder =
58+
errorBuilder ??
59+
(error, stack) => Center(
60+
child: Text(
61+
"${TextConstants.error}:$error",
62+
style: TextStyle(color: loaderColor),
63+
),
64+
);
65+
final firstError = values.firstWhere((value) => value.hasError);
66+
final error = firstError.error;
67+
final stackTrace = firstError.stackTrace;
68+
return nonNullOrElseBuilder(
69+
context,
70+
nonNullErrorBuilder(error, stackTrace),
71+
);
72+
}
73+
if (values.any((value) => value.isLoading)) {
74+
final nonNullLoadingBuilder =
75+
loadingBuilder ?? (context) => Loader(color: loaderColor);
76+
return nonNullOrElseBuilder(context, nonNullLoadingBuilder(context));
77+
}
78+
return nonNullOrElseBuilder(context, const SizedBox.shrink());
79+
}
80+
81+
class Async2Children<P, Q> extends StatelessWidget {
82+
final Tuple2<AsyncValue<P>, AsyncValue<Q>> values;
83+
final Widget Function(BuildContext context, P value1, Q value2) builder;
84+
final Widget Function(Object? error, StackTrace? stack)? errorBuilder;
85+
final Widget Function(BuildContext context)? loadingBuilder;
86+
final Widget Function(BuildContext context, Widget child)? orElseBuilder;
87+
final Color? loaderColor;
88+
const Async2Children({
89+
super.key,
90+
required this.values,
91+
required this.builder,
92+
this.errorBuilder,
93+
this.loaderColor,
94+
this.orElseBuilder,
95+
this.loadingBuilder,
96+
});
97+
@override
98+
Widget build(BuildContext context) {
99+
List<AsyncValue> listValues = [values.item1, values.item2];
100+
if (listValues.any((value) => value.hasError || value.isLoading)) {
101+
return handleLoadingAndError(
102+
listValues,
103+
context,
104+
loadingBuilder: loadingBuilder,
105+
errorBuilder: errorBuilder,
106+
orElseBuilder: orElseBuilder,
107+
loaderColor: loaderColor,
108+
);
109+
}
110+
return builder(context, listValues[0].value as P, listValues[1].value as Q);
111+
}
112+
}
113+
114+
class Async3Children<P, Q, R> extends StatelessWidget {
115+
final Tuple3<AsyncValue<P>, AsyncValue<Q>, AsyncValue<R>> values;
116+
final Widget Function(BuildContext context, P value1, Q value2, R value3)
117+
builder;
118+
final Widget Function(Object? error, StackTrace? stack)? errorBuilder;
119+
final Widget Function(BuildContext context)? loadingBuilder;
120+
final Widget Function(BuildContext context, Widget child)? orElseBuilder;
121+
final Color? loaderColor;
122+
const Async3Children({
123+
super.key,
124+
required this.values,
125+
required this.builder,
126+
this.errorBuilder,
127+
this.loaderColor,
128+
this.orElseBuilder,
129+
this.loadingBuilder,
130+
});
131+
@override
132+
Widget build(BuildContext context) {
133+
List<AsyncValue> listValues = [values.item1, values.item2, values.item3];
134+
if (listValues.any((value) => value.hasError || value.isLoading)) {
135+
return handleLoadingAndError(
136+
listValues,
137+
context,
138+
loadingBuilder: loadingBuilder,
139+
errorBuilder: errorBuilder,
140+
orElseBuilder: orElseBuilder,
141+
loaderColor: loaderColor,
142+
);
143+
}
144+
return builder(
145+
context,
146+
listValues[0].value as P,
147+
listValues[1].value as Q,
148+
listValues[2].value as R,
149+
);
150+
}
151+
}
152+
153+
class Async4Children<P, Q, R, S> extends StatelessWidget {
154+
final Tuple4<AsyncValue<P>, AsyncValue<Q>, AsyncValue<R>, AsyncValue<S>>
155+
values;
156+
final Widget Function(
157+
BuildContext context,
158+
P value1,
159+
Q value2,
160+
R value3,
161+
S value4,
162+
)
163+
builder;
164+
final Widget Function(Object? error, StackTrace? stack)? errorBuilder;
165+
final Widget Function(BuildContext context)? loadingBuilder;
166+
final Widget Function(BuildContext context, Widget child)? orElseBuilder;
167+
final Color? loaderColor;
168+
const Async4Children({
169+
super.key,
170+
required this.values,
171+
required this.builder,
172+
this.errorBuilder,
173+
this.loaderColor,
174+
this.orElseBuilder,
175+
this.loadingBuilder,
176+
});
177+
@override
178+
Widget build(BuildContext context) {
179+
List<AsyncValue> listValues = [values.item1, values.item2, values.item3];
180+
if (listValues.any((value) => value.hasError || value.isLoading)) {
181+
return handleLoadingAndError(
182+
listValues,
183+
context,
184+
loadingBuilder: loadingBuilder,
185+
errorBuilder: errorBuilder,
186+
orElseBuilder: orElseBuilder,
187+
loaderColor: loaderColor,
188+
);
189+
}
190+
return builder(
191+
context,
192+
listValues[0].value as P,
193+
listValues[1].value as Q,
194+
listValues[2].value as R,
195+
listValues[3].value as S,
196+
);
197+
}
198+
}
199+
200+
class Async5Children<P, Q, R, S, T> extends StatelessWidget {
201+
final Tuple5<
202+
AsyncValue<P>,
203+
AsyncValue<Q>,
204+
AsyncValue<R>,
205+
AsyncValue<S>,
206+
AsyncValue<T>
207+
>
208+
values;
209+
final Widget Function(
210+
BuildContext context,
211+
P value1,
212+
Q value2,
213+
R value3,
214+
S value4,
215+
T value5,
216+
)
217+
builder;
218+
final Widget Function(Object? error, StackTrace? stack)? errorBuilder;
219+
final Widget Function(BuildContext context)? loadingBuilder;
220+
final Widget Function(BuildContext context, Widget child)? orElseBuilder;
221+
final Color? loaderColor;
222+
const Async5Children({
223+
super.key,
224+
required this.values,
225+
required this.builder,
226+
this.errorBuilder,
227+
this.loaderColor,
228+
this.orElseBuilder,
229+
this.loadingBuilder,
230+
});
231+
@override
232+
Widget build(BuildContext context) {
233+
List<AsyncValue> listValues = [
234+
values.item1,
235+
values.item2,
236+
values.item3,
237+
values.item4,
238+
values.item5,
239+
];
240+
if (listValues.any((value) => value.hasError || value.isLoading)) {
241+
return handleLoadingAndError(
242+
listValues,
243+
context,
244+
loadingBuilder: loadingBuilder,
245+
errorBuilder: errorBuilder,
246+
orElseBuilder: orElseBuilder,
247+
loaderColor: loaderColor,
248+
);
249+
}
250+
return builder(
251+
context,
252+
listValues[0].value as P,
253+
listValues[1].value as Q,
254+
listValues[2].value as R,
255+
listValues[3].value as S,
256+
listValues[4].value as T,
257+
);
258+
}
259+
}

0 commit comments

Comments
 (0)