Flutter - использует MultiBlocProvider, но не может отобразить BlocListener на подэкране - PullRequest
0 голосов
/ 12 апреля 2020

Я собираюсь описать мою проблему и ошибку, с которой я столкнулся. А потом я скопирую свой код, чтобы сделать его более понятным. Проблема как:

  • Я использую MultiBlocProvider в root виджет (StartupScreen) с объявленными 2 блоками AuthenticationBlo c, ApplicationBlo c.
  • Использование BlocListener<AuthenticationBloc, AuthenticationState> at root widget (StartupScreen).
  • Если состояние AuthenticationBlo c изменяется на AuthAuthenticatedState, то выполняется маршрутизация на MainScreen, в противном случае - на LoginScreen.
  • Если пользователь подключился, то выполняется маршрутизация на MainScreen:
    1. Я собираюсь получить currentUser из хранилища (асинхронно), а затем обернуть BlocListener внутренним FutureBuilder. в итоге он не может отобразить экран и выдает ошибку следующим образом:
BlocProvider.of() called with a context that does not contain a Bloc of type ApplicationBloc.

No ancestor could be found starting from the context that was passed to BlocProvider.of<ApplicationBloc>().

This can happen if the context you used comes from a widget above the BlocProvider.

The context used was: BlocListener<ApplicationBloc, ApplicationState>(dirty, state: _BlocListenerBaseState<ApplicationBloc, ApplicationState>#1abf7(lifecycle state: created))

The relevant error-causing widget was
    FutureBuilder<UserCredentials>                       package:my_app/…/ui/main_screen.dart:43

When the exception was thrown, this was the stack
#0      BlocProvider.of                                  package:flutter_bloc/src/bloc_provider.dart:106
#1      _BlocListenerBaseState.initState                 package:flutter_bloc/src/bloc_listener.dart:160
#2      StatefulElement._firstBuild                      package:flutter/…/widgets/framework.dart:4355
#3      ComponentElement.mount                           package:flutter/…/widgets/framework.dart:4201
#4      SingleChildWidgetElementMixin.mount              package:nested/nested.dart:223
...

StartupScreen.dart

class StartupScreen extends StatelessWidget {
  final ApplicationBloc appBloc;
  final AuthenticationBloc authBloc;

  StartupScreen(this.appBloc, this.authBloc) : super();

  @override
  Widget build(BuildContext context) {
    ScreenSizeConfig().init(context);
    authBloc.add(AuthStartedEvent());

    return MultiBlocProvider(
      providers: [
        BlocProvider<ApplicationBloc>(
          create: (context) => appBloc,
        ),
        BlocProvider<AuthenticationBloc>(
          create: (context) => authBloc,
        ),
      ],
      child: BlocListener<AuthenticationBloc, AuthenticationState>(
        listener: (context, state) {
          if (state is AuthUnauthenticatedState) {
            Navigator.of(context).pushReplacementNamed(RouteConstants.LOGIN_SCREEN);
          } else if (state is AuthAuthenticatedState) {
            Navigator.of(context).pushReplacementNamed(RouteConstants.MAIN_SCREEN);
          }
        },
        child: BlocBuilder<AuthenticationBloc, AuthenticationState>(
          builder: (context, state) {
            return Center(
              child: Container(
                child: Text('Startup Screen'),
              ),
            );
          },
        ),
      ),
    );
  }
}

MainScreen.dart

class MainScreen extends StatelessWidget {
  final ApplicationBloc appBloc;
  final AuthenticationBloc authBloc;

  MainScreen(this.appBloc, this.authBloc) : super();

  @override
  Widget build(BuildContext context) {
    return _MainPageWidget(appBloc, authBloc);
  }
}

class _MainPageWidget extends StatefulWidget {
  final ApplicationBlocappBloc;
  final AuthenticationBloc authBloc;

  _MainPageWidget(this.appBloc, this.authBloc) : super();

  @override
  State<StatefulWidget> createState() => _MainPageState();
}

class _MainPageState extends State<_MainPageWidget> {
  Future<UserCredentials> getUserCredentials() async {
    return await widget.appBloc.authService.getUser();
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<UserCredentials>(
        future: getUserCredentials(),
        builder: (context, snapshot) {
          if (!snapshot.hasData) {
            return Center(
              child: CircularProgressIndicator(),
            );
          } else {
            return _buildBlocListener(snapshot.data);
          }
        });
  }

  Widget _buildBlocListener(UserCredentials userCredentials) {
    return BlocListener<ApplicationBloc, ApplicationState>(
      listener: (context, state) {
        if (userCredentials.isNewUser) {
          widget.appBloc.add(AppNewUserEvent());
        } else {
          widget.appBloc
              .add(AppAlreadyCompletedNewUserProcessEvent());
        }
      },
      child: _buildBlocBuilder(context, widget.appBloc),
    );
  }

  Widget _buildBlocBuilder(BuildContext context, ApplicationBloc appBloc) {
    return BlocBuilder<ApplicationBloc, ApplicationState>(
      builder: (context, state) {
        print('main_screen.dart: go to mainscreen BlocBuilder builder: state: $state');
        return Container(
          child: Text('Main Screen'),
        );
      },
    );
  }
}

1 Ответ

1 голос
/ 12 апреля 2020

Из документации библиотеки blo c:

Вы не можете получить доступ к blo c из того же контекста, в котором он был предоставлен, поэтому вы должны убедиться, что BlocProvider.of () вызывается внутри дочернего объекта BuildContext

https://bloclibrary.dev/# / faqs? id = blocproviderof-fail-to-find-blo c

Вы должны будете принять из вашего BlocListener и поместите его в отдельный виджет, или оберните ваш BlocListener виджетом построителя.

...