Last active
April 17, 2020 21:06
-
-
Save andantonyan/9f14c18a0693be0bae344e985b0bdd58 to your computer and use it in GitHub Desktop.
Flutter BloC Tree basic concept
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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