Skip to content

Instantly share code, notes, and snippets.

@zs-dima
Last active August 19, 2021 09:34
Show Gist options
  • Save zs-dima/009139aa960612e036287d1147aed19c to your computer and use it in GitHub Desktop.
Save zs-dima/009139aa960612e036287d1147aed19c to your computer and use it in GitHub Desktop.
StateBuilder to discuss
typedef StateBuilderCondition<State extends Object> = bool Function(State state);
typedef StateBuilderConverter<State extends Object, Result extends Object?> = Result Function(State state);
typedef StateBuilderBuilder<Result extends Object?> = Widget Function(BuildContext context, Result result);
typedef StateBuilderOnWaiting = Widget Function(BuildContext context);
typedef StateBuilderOnError = Widget Function(BuildContext context, Object? error);
class StateBuilder {
@factory
static _StateBuilder<State> where<State extends Object>({
Key? key,
State? initialData,
required IStateObservable bloc,
StateBuilderCondition<State>? buildWhen,
required StateBuilderBuilder<State> builder,
StateBuilderOnWaiting? onWaiting,
StateBuilderOnError? onError,
bool animate = true,
}) =>
_StateBuilder<State>(
key: key,
initialData: initialData,
bloc: bloc.whereState<State>(),
buildWhen: buildWhen,
builder: builder,
onError: onError,
onWaiting: onWaiting,
animate: animate,
);
@factory
static _StateBuilderConvert<State, Result> whereConvert<State extends Object, Result extends Object?>({
Key? key,
Result? initialData,
required IStateObservable bloc,
StateBuilderCondition<State>? buildWhen,
required StateBuilderConverter<State, Result> convert,
required StateBuilderBuilder<Result> builder,
StateBuilderOnWaiting? onWaiting,
StateBuilderOnError? onError,
bool animate = true,
}) =>
_StateBuilderConvert<State, Result>(
key: key,
initialData: initialData,
bloc: bloc.whereState<State>(),
buildWhen: buildWhen,
convert: convert,
builder: builder,
onError: onError,
onWaiting: onWaiting,
animate: animate,
);
}
class _StateBuilderConvert<State extends Object, Result extends Object?> extends StatelessWidget {
final bool animate;
final Result? initialData;
final StateObservable<State> bloc;
final StateBuilderCondition<State>? buildWhen;
final StateBuilderConverter<State, Result> convert;
final StateBuilderBuilder<Result> builder;
final StateBuilderOnWaiting? onWaiting;
final StateBuilderOnError? onError;
const _StateBuilderConvert({
Key? key,
this.initialData,
required this.bloc,
required this.builder,
this.buildWhen,
required this.convert,
this.onWaiting,
this.onError,
this.animate = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final Stream<State> stream = buildWhen == null //
? bloc.stream
: bloc.stream.where((state) => buildWhen!(state));
return StreamBuilder(
initialData: bloc.state is State ? convert(bloc.state) : initialData,
stream: stream.map(convert).distinct(),
builder: (context, AsyncSnapshot<Result> snapshot) {
Widget result;
if (snapshot.hasError) {
result = onError != null //
? onError!(context, snapshot.error)
: (context.tryRead<DefaultBlocErrorWidget>() ?? const SizedBox.shrink());
} else if (snapshot.hasData) {
final Result data = snapshot.data as Result;
result = builder(context, data);
} else {
result = onWaiting != null //
? onWaiting!(context)
: (context.tryRead<DefaultBlocWaitingWidget>() ?? const SizedBox.shrink());
}
if (animate) return AnimatedSwitcher(duration: const Duration(milliseconds: 500), child: result);
return result;
},
);
}
}
class _StateBuilder<State extends Object> extends StatelessWidget {
final bool animate;
final State? initialData;
final StateObservable<State> bloc;
final StateBuilderCondition<State>? buildWhen;
final StateBuilderBuilder<State> builder;
final StateBuilderOnWaiting? onWaiting;
final StateBuilderOnError? onError;
const _StateBuilder({
Key? key,
this.initialData,
required this.bloc,
required this.builder,
this.buildWhen,
this.onWaiting,
this.onError,
this.animate = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final Stream<State> stream = buildWhen == null //
? bloc.stream
: bloc.stream.where((state) => buildWhen!(state));
return StreamBuilder(
initialData: bloc.state is State ? bloc.state : initialData,
stream: stream.distinct(),
builder: (context, AsyncSnapshot<State> snapshot) {
Widget result;
if (snapshot.hasError) {
result = onError != null //
? onError!(context, snapshot.error)
: (context.tryRead<DefaultBlocErrorWidget>() ?? const SizedBox.shrink());
} else if (snapshot.hasData) {
final State data = snapshot.data as State;
result = builder(context, data);
} else {
result = onWaiting != null //
? onWaiting!(context)
: (context.tryRead<DefaultBlocWaitingWidget>() ?? const SizedBox.shrink());
}
if (animate) return AnimatedSwitcher(duration: const Duration(milliseconds: 500), child: result);
return result;
},
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment