flutter - 将 BlocListener 与 Navigator PushNamed 一起使用会导致歧义

标签 flutter state bloc flutter-bloc cubit

我在使用 block 监听器进行导航时遇到问题

我有五个导航屏幕,并且在每个文件中都使用 BlocConsumer。我正在 flutter 检查器中寻找粒度 View 。我使用 blocListener 进行导航。当我 pushNamed 第一个屏幕时,一切正常,我导航到第二个屏幕(第一个屏幕已添加到导航堆栈)。现在我在第二个屏幕,当我按下移动到第三个屏幕时,两个第二个屏幕,然后添加第三个屏幕,堆栈应该像这样 (第一个屏幕,第二个屏幕,第三个屏幕) 但不幸的是就像(第一个屏幕,第二个屏幕,第二个屏幕,第三个屏幕)。 现在,当我在第三个屏幕上并想要添加第四个屏幕时,它是堆栈,但添加了第二个屏幕,然后添加了第三个屏幕两个次,然后添加第四个屏幕。堆栈应该像这样(第一个屏幕,第二个屏幕,第三个屏幕,第四个屏幕),但不幸的是它就像(第一个屏幕,第二个屏幕,第二个屏幕,第三个屏幕,第二个屏幕,第三屏幕,第三屏幕,第四屏幕)。因此,导航堆栈中不是 4 个屏幕,而是 8 个屏幕。

这是我在所有文件中使用的模式。

这是我创建 bloc 实例并关闭它的地方。

class MyAppRoutes {
  FieldsBloc _fieldsBloc = FieldsBloc();

  Route onGenerateRoute(RouteSettings routeSettings) {
    try {
      switch (routeSettings.name) {

        case LandingPage.routeName:
          return MaterialPageRoute(builder: (_) => LandingPage());

        case CategoryPage.routeName:
          return MaterialPageRoute(
              builder: (context) => BlocProvider.value(
                    value: _fieldsBloc,
                    child: CategoryPage(),
                  ));

        case ExpertisePage.routeName:
          return MaterialPageRoute(
              builder: (context) => BlocProvider.value(
                    value: _fieldsBloc,
                    child: ExpertisePage(),
                  ));

        case ExpertiseLevelPage.routeName:
          return MaterialPageRoute(
              builder: (context) => BlocProvider.value(
                    value: _fieldsBloc,
                    child: ExpertiseLevelPage(),
                  ));

        case EducationPage.routeName:
          return MaterialPageRoute(
              builder: (context) => BlocProvider.value(
                    value: _fieldsBloc,
                    child: EducationPage(),
                  ));

        default:
          return null;
      }
    } catch (e) {
      print(e);
    }
  }

  void dispose() async {
    _fieldsBloc.close();
  }
}

这是我在每个文件中使用的小部件。

BlocConsumer<FieldsBloc, FieldsState>(builder: (context, state) {
              if (state is FieldsInitial) {
                return Container();
              } else if (state is FieldLoadingState) {
                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: getCircularProgress(context),
                );
              } else if (state is FieldSuccessfulState) {
                return Container();
              } else if (state is FieldUnsuccessfulState) {
                return Padding(
                    padding: const EdgeInsets.all(15.0),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Icon(
                          Icons.error,
                          color: Colors.red,
                        ),
                        SizedBox(
                          width: 5.0,
                        ),
                        Expanded(
                            child: TextStyleRes.textStyleFont1(
                                textColor: Colors.red,
                                text: state.message,
                                fontSize: 12,
                                fontWeight: FontWeight.w700)),
                      ],
                    ));
              }
              return Container();
            }, listener: (context, state) {
              if (state is FieldSuccessfulState)
                return SchedulerBinding.instance.addPostFrameCallback((_) {
                  Navigator.of(context).pushNamed(ExpertisePage.routeName);
                });
            }),

这是正在触发的 Bloc 事件。

abstract class FieldsEvent {}

class NextButtonEventScreen3 extends FieldsEvent {
  List<String> categories;

  NextButtonEventScreen3(this.categories);
}

class NextButtonEventScreen4 extends FieldsEvent {
  List skills;

  NextButtonEventScreen4(this.skills);
}

class NextButtonEventScreen5 extends FieldsEvent {
  String expert;

  NextButtonEventScreen5(this.expert);
}

这就是 Bloc 。

if (event is NextButtonEventScreen3) {
       if (event.categories.isNotEmpty) {
         yield FieldLoadingState();
         categories = event.categories;

         yield FieldSuccessfulState();
       } else
         throw ('Please choose at least 1 category');
     }
     //=====================SignUpScreen4===========================
     else if (event is NextButtonEventScreen4) {
       if (event.skills.isNotEmpty) {
         yield FieldLoadingState();
         skills = event.skills;

         yield FieldSuccessfulState(_updateModel());
       } else
         throw ('Please provide at least 1 skill');
     }
     //=====================SignUpScreen5===========================
     else if (event is NextButtonEventScreen5) {
       expert = event.expert;

       yield FieldSuccessfulState();
     } 

这些是 Bloc 内的州

abstract class FieldsState {
 String message;
}

class FieldsInitial extends FieldsState {}
class FieldLoadingState extends FieldsState {}


class FieldSuccessfulState extends FieldsState {
 var data;

 FieldSuccessfulState([this.data]);
}


class FieldUnsuccessfulState extends FieldsState {
 String message;

 FieldUnsuccessfulState({this.message});
}

最佳答案

导航堆栈上的先前页面仍然存在,正在监听 block 事件。当第二个屏幕上的状态变为 FieldsSuccessfulState 时,两个监听器都会看到此情况并尝试导航到下一个屏幕。

为了确保只有当前屏幕会对 FieldsSuccessfulState 使用react,我可以想到两个选项:

  • FieldsSuccessfulState 拆分为多个结果(不同的类或添加的字段),并使每个屏幕仅对自己的成功状态使用react。

  • 在导航到下一个屏幕之前检查 ModalRoute.of(context).isCurrent。这可以在监听器本身或 listenWhen 参数中完成。

关于flutter - 将 BlocListener 与 Navigator PushNamed 一起使用会导致歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68137100/

相关文章:

Dart或Flutter如何读取一个float32 little endian编码的二进制文件并转换成List<double>?

flutter - 在 Flutter 中按下/离开时,TextField 会重新加载 FutureBuilder

flutter - 从 firebase flutter 中的已知文档获取字段值

intellij-idea - 如何签署 Flutter 的应用程序

stream - 使用 Kafka Streams 进行简单分类

javascript - 如何在 ionic 中使用 state.go?

javascript - 用户界面路由器 : Reset $state without using $state. go()

flutter - 如何限制 Bloc 内的事件?

flutter - BlocProvider 中的惰性标志有什么影响?

flutter - 从 BlocBuilder 导航到新屏幕