简介
实现可同时嵌套多个BlocBuilder的MultiBlocBuilder
- flutter_bloc提供了MultiBlocProvider、MultiBlocListener
- 作为强迫症患者怎么能没有MultiBlocBuilder这种东西存在,我们来实现它
完成后的使用示例
// 1. main.dart
runApp(MultiBlocProvider(
providers: [
BlocProvider<ThemeBloc>(
builder: (context) => ThemeBloc(),
),
BlocProvider<RongCloudBloc>(
builder: (context) => RongCloudBloc(),
)
],
child: App(),)
);
// 2. app.dart
MultiBlocBuilder(
blocBuilders: <ExtendedBlocBuilder>[
ExtendedBlocBuilder(
bloc: _rongCloudBloc,
condition: (dynamic preState, dynamic state) {
return (preState as RongCloudState).status !=
(state as RongCloudState).status;
}),
ExtendedBlocBuilder(
bloc: _themeBloc,
),
],
builder: (BuildContext context) {
return MaterialApp(
theme: _themeBloc.currentState.themeData,
debugShowCheckedModeBanner: true,
home: HomePage(),
onGenerateRoute: Application.router.generator,
);
}
);
实现步骤
- 实现ExtendedBlocBuilder
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
typedef ExtendedBlocBuilderCondition = bool Function(
dynamic previous, dynamic current);
class ExtendedBlocBuilder {
final Bloc bloc;
final ExtendedBlocBuilderCondition condition;
const ExtendedBlocBuilder({
@required this.bloc,
this.condition,
}) : assert(bloc != null);
}
- 实现MultiBlocBuilder
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'extended_bloc_builder.dart';
typedef BlocContextBuilder = Widget Function(BuildContext context);
class MultiBlocBuilder<S> extends StatefulWidget {
const MultiBlocBuilder({
Key key,
@required this.blocBuilders,
@required this.builder,
}) : assert(blocBuilders != null),
assert(builder != null),
super(key: key);
final List<ExtendedBlocBuilder> blocBuilders;
final BlocContextBuilder builder;
@override
_MultiBlocBuilderState createState() => _MultiBlocBuilderState();
}
class _MultiBlocBuilderState extends State<MultiBlocBuilder> {
@override
Widget build(BuildContext context) {
dynamic tree;
List<ExtendedBlocBuilder> blocBuilders =
widget.blocBuilders.reversed.toList();
for (final ExtendedBlocBuilder blocBuilder in blocBuilders) {
tree = ExtendedBlocListener(
bloc: blocBuilder.bloc,
listener: (BuildContext context, preState, state) {
if (blocBuilder.condition?.call(preState, state) ?? true) {
setState(() {});
}
},
child: tree ?? widget.builder(context),
);
}
return tree;
}
}
typedef ExtendedBlocWidgetListener<S> = void Function(
BuildContext context, S preState, S state);
class ExtendedBlocListener<E, S> extends ExtendedBlocListenerBase<E, S> {
/// The [Bloc] whose state will be listened to.
/// Whenever the bloc's state changes, `listener` will be invoked.
final Bloc<E, S> bloc;
/// The [ExtendedBlocWidgetListener] which will be called on every state change (including the `initialState`).
/// This listener should be used for any code which needs to execute
/// in response to a state change (`Transition`).
/// The state will be the `nextState` for the most recent `Transition`.
final ExtendedBlocWidgetListener<S> listener;
/// The [Widget] which will be rendered as a descendant of the [BlocListener].
final Widget child;
const ExtendedBlocListener({
Key key,
@required this.bloc,
@required this.listener,
this.child,
}) : assert(bloc != null),
assert(listener != null),
super(key: key, bloc: bloc, listener: listener);
/// Clone the current [BlocListener] with a new child [Widget].
/// All other values, including [Key], [Bloc] and [BlocWidgetListener] are
/// preserved.
ExtendedBlocListener<E, S> copyWith(Widget child) {
return ExtendedBlocListener<E, S>(
key: key,
bloc: bloc,
listener: listener,
child: child,
);
}
@override
Widget build(BuildContext context) => child;
}
abstract class ExtendedBlocListenerBase<E, S> extends StatefulWidget {
/// The [Bloc] whose state will be listened to.
/// Whenever the bloc's state changes, `listener` will be invoked.
final Bloc<E, S> bloc;
/// The [ExtendedBlocWidgetListener] which will be called on every state change.
/// This listener should be used for any code which needs to execute
/// in response to a state change (`Transition`).
/// The state will be the `nextState` for the most recent `Transition`.
final ExtendedBlocWidgetListener<S> listener;
const ExtendedBlocListenerBase({
Key key,
@required this.bloc,
@required this.listener,
}) : super(key: key);
State<ExtendedBlocListenerBase<E, S>> createState() =>
_ExtendedBlocListenerBaseState<E, S>();
/// Returns a [Widget] based on the [BuildContext].
Widget build(BuildContext context);
}
class _ExtendedBlocListenerBaseState<E, S>
extends State<ExtendedBlocListenerBase<E, S>> {
StreamSubscription<S> _subscription;
S _previousState;
S _state;
@override
void initState() {
super.initState();
_previousState = widget.bloc.currentState;
_state = widget.bloc.currentState;
_subscribe();
}
@override
void didUpdateWidget(ExtendedBlocListenerBase<E, S> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.bloc.state != widget.bloc.state) {
if (_subscription != null) {
_unsubscribe();
_previousState = widget.bloc.currentState;
_state = widget.bloc.currentState;
}
_subscribe();
}
}
@override
Widget build(BuildContext context) => widget.build(context);
@override
void dispose() {
_unsubscribe();
super.dispose();
}
void _subscribe() {
if (widget.bloc.state != null) {
_subscription = widget.bloc.state.listen((S state) {
widget.listener.call(context, _previousState, state);
_previousState = state;
});
}
}
void _unsubscribe() {
if (_subscription != null) {
_subscription.cancel();
_subscription = null;
}
}
}