Closed huang12zheng closed 4 years ago
Hi @huang12zheng 👋 Thanks for opening an issue!
With the Pages API you can use a BlocBuilder to handle navigation in response to state changes. The following is an example just to demonstrate the concept but I generally don't recommend having events like push
/pop
and tightly coupling your bloc to navigation.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocProvider(
create: (_) => FlowBloc(),
child: Flow(),
),
);
}
}
class Flow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<FlowBloc, FlowState>(
builder: (context, state) {
return Navigator(
pages: [
if (state.index >= 0) MaterialPage<void>(child: PageA()),
if (state.index >= 1) MaterialPage<void>(child: PageB()),
],
onPopPage: (route, dynamic result) {
context.bloc<FlowBloc>().add(FlowEvent.pop);
return false;
},
);
},
);
}
}
class PageA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('A')),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.arrow_forward),
onPressed: () => context.bloc<FlowBloc>().add(FlowEvent.push),
),
);
}
}
class PageB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('B')),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.arrow_back),
onPressed: () => context.bloc<FlowBloc>().add(FlowEvent.pop),
),
);
}
}
enum FlowEvent { push, pop }
enum FlowState { a, b }
class FlowBloc extends Bloc<FlowEvent, FlowState> {
FlowBloc() : super(FlowState.a);
@override
Stream<FlowState> mapEventToState(FlowEvent event) async* {
switch (event) {
case FlowEvent.push:
yield state.index + 1 < FlowState.values.length
? FlowState.values[state.index + 1]
: state;
break;
case FlowEvent.pop:
yield state.index > 0 ? FlowState.values[state.index - 1] : state;
break;
}
}
}
Hope that helps 👍
Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation 😄
abstract class RouteState with EquatableMixin {
const RouteState():super();
String get id;
Widget buildWidget();
@override
List<Object> get props => [runtimeType, id];
}
abstract class SimpleRouteState extends RouteState {
const SimpleRouteState():super();
@override
String get id => '';
}
class RouteTreeState with EquatableMixin{
final List<RouteState> values;
RouteTreeState([this.values= const [] ]);
@override
List<RouteState> get props => values;
List<Widget> buildWidget() {
return <Widget>[
for (RouteState state in values) state.buildWidget()
];
}
List<Page> buildRoute() {
return <Page>[
for (RouteState state in values) toPage(state.buildWidget())
];
}
}
class NavigatorCubit extends Cubit<RouteTreeState> {
NavigatorCubit(RouteTreeState initState) : super(initState);
void push(RouteState newState) =>
emit(RouteTreeState([
...state.values..remove(newState),
newState,
]));
void pop()=> emit(RouteTreeState(
[...state.values]..removeLast(),
));
}
VoidCallback cubitNav<T extends NavigatorCubit>(BuildContext context,RouteState state) {
return ()=>context.bloc<T>().push(state);
}
class RouteTreeBuilder<T extends NavigatorCubit> extends StatelessWidget {
const RouteTreeBuilder({
Key key,
@required this.create,
// @required this.homeScreen,
this.onPopPage,
}) : super(key: key);
final CreateBloc<T> create;
// final Widget homeScreen;
final PopPageCallback onPopPage;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: create,
child: BlocBuilder<T, RouteTreeState>(
builder: (builderContext, state) => Navigator(
pages: state.buildRoute(),
onPopPage: onPopPage ?? (route, result) {
if (!route.didPop(result)) return false;
// Update the list of pages by setting _selectedBook to null
builderContext.bloc<T>().pop();
return true;
}
)
)
);
}
}
my code:
Can I stop using BlocListener ?