Skip to content

Instantly share code, notes, and snippets.

@andantonyan
Last active April 17, 2020 21:06
Show Gist options
  • Save andantonyan/9f14c18a0693be0bae344e985b0bdd58 to your computer and use it in GitHub Desktop.
Save andantonyan/9f14c18a0693be0bae344e985b0bdd58 to your computer and use it in GitHub Desktop.
Flutter BloC Tree basic concept
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rxdart/rxdart.dart';
abstract class BlocNodeEvent extends Equatable {
@override
List<Object> get props => [];
@override
bool get stringify => true;
}
abstract class BlocNodeState extends Equatable {
@override
List<Object> get props => [];
@override
bool get stringify => true;
}
abstract class BlocNode<E extends BlocNodeEvent, S extends BlocNodeState> extends Bloc<E, S> {
final List<BlocNode> children;
final bool root;
final Subject _unsubscribe = BehaviorSubject();
Iterable<BlocNode> get preOrder sync* {
yield this;
for (var child in children) {
yield* child.preOrder;
}
}
BlocNode({this.children = const [], this.root = false}) : assert(children != null), assert(root != null) {
if (root) {
final blocs = children.toList();
MergeStream(blocs)
.takeUntil(_unsubscribe)
.listen((state) => blocs.forEach((bloc) => bloc.onTreeStateChange(state)));
}
}
void onTreeStateChange(BlocNodeState state) {}
T getChild<T extends BlocNode>() => preOrder.firstWhere((child) => child is T, orElse: () => null);
List<T> getChildren<T extends BlocNode>() => preOrder.whereType<T>();
@override
Future<void> close() {
_unsubscribe.close();
return super.close();
}
}
/// Example
class User {
final int id;
final String name;
const User(this.id, this.name);
@override
String toString() {
return 'User{id: $id, name: $name}';
}
}
// Search BloC
abstract class UserSearchEvent extends BlocNodeEvent {}
class UserSearchFilterChanged extends UserSearchEvent {}
class UserSearchItemUpdated extends UserSearchEvent {
final User user;
@override
List<Object> get props => [user];
UserSearchItemUpdated(this.user);
}
abstract class UserSearchState extends BlocNodeState {}
class UserSearchInitial extends UserSearchState {}
class UserSearchLoaded extends UserSearchState {
final List<User> users;
@override
List<Object> get props => [users];
UserSearchLoaded(this.users);
}
class UserSearchBloc extends BlocNode<UserSearchEvent, UserSearchState> {
@override
UserSearchState get initialState => UserSearchInitial();
@override
Stream<UserSearchState> mapEventToState(final UserSearchEvent event) async* {
if (event is UserSearchFilterChanged) {
final users = List.generate(3, (i) => User(i, 'User:$i'));
yield UserSearchLoaded(users);
} else if (event is UserSearchItemUpdated && state is UserSearchLoaded) {
final users = (state as UserSearchLoaded).users;
final updatedUsers = users.map((u) => u.id == event.user.id ? event.user : u).toList();
yield UserSearchLoaded(updatedUsers);
}
}
@override
void onTreeStateChange(final BlocNodeState state) {
if (state is UserUpdateSuccess) {
add(UserSearchItemUpdated(User(state?.user?.id, state?.user?.name)));
}
}
}
// Update BloC
abstract class UserUpdateEvent extends BlocNodeEvent {}
class UserUpdated extends UserUpdateEvent {
final int id;
final String name;
@override
List<Object> get props => [id, name];
UserUpdated(this.id, this.name);
}
abstract class UserUpdateState extends BlocNodeState {}
class UserUpdateInitial extends UserUpdateState {}
class UserUpdateSuccess extends UserUpdateState {
final User user;
UserUpdateSuccess(this.user);
@override
List<Object> get props => [user];
}
class UserUpdateBloc extends BlocNode<UserUpdateEvent, UserUpdateState> {
@override
UserUpdateState get initialState => UserUpdateInitial();
@override
Stream<UserUpdateState> mapEventToState(final UserUpdateEvent event) async* {
if (event is UserUpdated) {
yield UserUpdateSuccess(User(event.id, event.name));
}
}
}
// Combined
class UserCombinedState extends BlocNodeState {}
class UserCombinedBloc extends BlocNode<BlocNodeEvent, UserCombinedState> {
@override
UserCombinedState get initialState => UserCombinedState();
UserCombinedBloc({List<BlocNode> children}) : super(children: children, root: true);
@override
Stream<UserCombinedState> mapEventToState(BlocNodeEvent event) {}
}
void main() {
final combinedBloc = UserCombinedBloc(children: [UserSearchBloc(), UserUpdateBloc()]);
combinedBloc.getChild<UserSearchBloc>().listen(print);
combinedBloc.getChild<UserUpdateBloc>().listen(print);
combinedBloc.getChild<UserSearchBloc>().add(UserSearchFilterChanged());
Future.delayed(Duration(seconds: 2)).then((_) {
combinedBloc.getChild<UserUpdateBloc>().add(UserUpdated(1, 'Updated:1'));
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment