Search code examples
flutterblocnavigator

using two diffrenet bloc inside the app does not work


i am using bloc with freezed, i am using an AuthBloc on MyApp then i need to use another bloc inside the Product Page .

the the user click on the button inside Product Page, Details Page will be opened. but when the user click the button there is no action happened, or error.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<AuthBloc>(
      create: (context) => AuthBloc(),
      child: MaterialApp(
        home: Router(
          routerDelegate: AppRouterDelegate(),
          backButtonDispatcher: RootBackButtonDispatcher(),
        ),
      ),
    );
  }
}

Products Page:

class ProductPage extends StatelessWidget {
  const ProductPage();

  @override
  Widget build(BuildContext context) {
    return BlocProvider<ProductsBloc>(
      create: (context) => ProductsBloc(),
      child: Scaffold(
          appBar: AppBar(
            title: Text('Products'),
          ),
          body: Center(
              child: FittedBox(child: BlocBuilder<ProductsBloc, ProductsState>(
            builder: (context, state) {
              return TextButton(
                child: Text("More Details"),
                onPressed: () => BlocProvider.of<ProductsBloc>(context)
                    .add(const ProductsEvent.goToDetailsEvent()),
              );
            },
          )))),
    );
  }
}

ProductBloc:

class ProductsBloc extends Bloc<ProductsEvent, ProductsState> {
  ProductsBloc() : super(_Initial());

  @override
  Stream<ProductsState> mapEventToState(
    ProductsEvent event,
  ) async* {
    if(event is GoToDetailsEvent){
      yield GoToDetailsState();
    }
  }
}

Product Event:

@freezed
class ProductsEvent with _$ProductsEvent {
  const factory ProductsEvent.started() = _Started;
  const factory ProductsEvent.goToDetailsEvent() = GoToDetailsEvent;
}

Product State:

@freezed
class ProductsState with _$ProductsState {
  const factory ProductsState.initial() = _Initial;
  const factory ProductsState.goToDetailsState()=GoToDetailsState;
}

Router Delegate Code:

class AppRouterDelegate extends RouterDelegate
    with ChangeNotifier, PopNavigatorRouterDelegateMixin {
  AppRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
  final GlobalKey<NavigatorState> navigatorKey;

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<AuthBloc, AuthState>(
      builder: (context, state) {
        return Navigator(
          key: navigatorKey,
          pages: [
            MaterialPage(
              key: ValueKey("Home"),
              child: HomePage(),
            ),
            if (state is SignInState)
              MaterialPage(
                 key: ValueKey("Auth"),
                child: AuthPage(),
              ),
            if (state is ProductState)
              MaterialPage(
                 key: ValueKey("Product"),
                child: ProductPage(),
              ),
            if (state is GoToDetailsState)
              MaterialPage(
                 key: ValueKey("Details"),
                child: DetailsPage(),
              ),
          ],
          onPopPage: (route, result) {
            if (!route.didPop(result)) return false;
            return true;
          },
        );
      },
    );
  }

  @override
  Future<void> setNewRoutePath(configuration) {
    throw UnimplementedError();
  }
}

Solution

  • You sent a 'ProductsEvent.goToDetailsEvent' at ProductPage but your 'BlocBuilder' in AppRouterDelegate's build method is for AuthBloc.

    If you want to use 2 BlocBuilder, just refer to below code.
    You need to change each state's name to distinguish each bloc state.
    (But I am not sure that it is best way.)

    ...
    BlocBuilder<AuthBloc, AuthState>(
        builder: (context, authState) {
            return BlocBuilder<ProductBloc, ProductState>(
                builder: (context, productState) {
                    return Navigator(
                        key: navigatorKey,
    ...