dart 为什么Bloc触发多次相同的状态?

0ejtzxu1  于 2023-06-19  发布在  其他
关注(0)|答案(1)|浏览(152)

我是Bloc的新手,我注意到当一个状态改变时,它会为一个状态改变多次,让我解释一下,例如我有这样的设置,在我的login_page.dart上,我有一个BlocListener,当登录操作成功时,它基本上显示一个snackbar并导航到另一个页面:

BlocListener<AuthBloc, AuthState>(
  listenWhen: (previous, current) => previous != current,
  listener: (context, state) {
    dev.log('listener login page: $state');

    if (state is AuthLoadingState) {
      loadingDialog(context);
    } else if (state is AuthLoadedState) {
      Navigator.of(context).pushReplacement(
        MaterialPageRoute(
          builder: (context) => const HomePage(),
        ),
      );
      showSnackBar(
          message: 'Logged in successfully', isError: false);
    } else if (state is AuthErrorState) {
      Navigator.of(context).pop();
      showSnackBar(message: state.message, isError: true);
    }
  },
  child: AuthButtonWidget(
    formKey: _formKey,
    onPressed: () {
      if (_formKey.currentState!.validate()) {
        BlocProvider.of<AuthBloc>(context).add(
          LogInWithEmailAndPasswordEvent(
            email: _emailController.text,
            password: _passwordController.text,
          ),
        );
      }
    },
    text: 'Log In',
  ),
),

在我的home_page.dart上,我有一个BlocConsumer,它监听一个事件,显示一个snackbar,并在按下按钮(注销)时导航回登录页面:

BlocConsumer<AuthBloc, AuthState>(
  listenWhen: (previous, current) => previous != current,
  listener: (context, state) {
    dev.log('listener home page: $state');
    if (state is AuthLoadingState) {
      loadingDialog(context);
    } else if (state is AuthLoggedOutState) {
      Navigator.of(context).pushReplacement(
        MaterialPageRoute(
          builder: (context) => const LoginPage(),
        ),
      );
      showSnackBar(message: 'Logged out successfully', isError: false);
    } else if (state is AuthErrorState) {
      Navigator.of(context).pop();
      showSnackBar(message: state.message, isError: true);
    }
  },
  buildWhen: (previous, current) => previous != current,
  builder: (context, state) {
    dev.log('builder home page: $state');

    if (state is AuthLoadedState) {
      return const HomePageWidget();
    } else if (state is AuthErrorState) {
      return const Center(
        child: Text('AuthErrorState'),
      );
    } else {
      return const HomePageWidget();
    }
  },
);

HomePageWidget()只是一个带有浮动操作按钮的列,用于断开用户连接。更多信息,请看我的博客:

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final SignUpWithEmailAndPasswordUsecase signUpWithEmailAndPassword;
  final LogInWithEmailAndPasswordUsecase logInWithEmailAndPassword;
  final LogOutUsecase logOut;
  AuthBloc(
    this.signUpWithEmailAndPassword,
    this.logInWithEmailAndPassword,
    this.logOut,
  ) : super(AuthInitial()) {
    on<AuthEvent>((event, emit) async {
      if (event is SignUpWithEmailAndPasswordEvent) {
        emit(AuthLoadingState());

        final failureOrUnit = await signUpWithEmailAndPassword(
          email: event.email,
          password: event.password,
          name: event.name,
        );

        emit(_mapFailureOrPostsState(either: failureOrUnit));
      } else if (event is LogInWithEmailAndPasswordEvent) {
        emit(AuthLoadingState());

        final failureOrUnit = await logInWithEmailAndPassword(
          email: event.email,
          password: event.password,
        );

        emit(_mapFailureOrPostsState(either: failureOrUnit));
      } else if (event is LogOutEvent) {
        emit(AuthLoadingState());

        final failureOrUnit = await logOut();

        emit(_mapFailureOrPostsState(
          either: failureOrUnit,
          isLogOut: true,
        ));
      }
    });
  }
}

AuthState _mapFailureOrPostsState({
  required Either<Failure, Unit> either,
  bool isLogOut = false,
}) {
  return either.fold(
    (failure) => AuthErrorState(message: _mapFailureToMessage(failure)),
    (_) {
      if (isLogOut) {
        return AuthLoggedOutState();
      } else {
        return AuthLoadedState();
      }
    },
  );
}

String _mapFailureToMessage(Failure failure) {
  if (failure is SignUpWithEmailAndPasswordFailure) {
    switch (failure.code) {
      case 'email-already-in-use':
        return EMAIL_ALREADY_IN_USE_FAILURE_MESSAGE;
      case 'invalid-email':
        return INVALID_EMAIL_FAILURE_MESSAGE;
      case 'operation-not-allowed':
        return OPERATION_NOT_ALLOWED_FAILURE_MESSAGE;
      case 'weak-password':
        return WEAK_PASSWORD_FAILURE_MESSAGE;
      default:
        return SIGN_UP_WITH_EMAIL_AND_PASSWORD_FAILURE_MESSAGE;
    }
  } else if (failure is LogInWithEmailAndPasswordFailure) {
    switch (failure.code) {
      case 'invalid-email':
        return INVALID_EMAIL_FAILURE_MESSAGE;
      case 'user-disabled':
        return USER_IS_DISABLED_FAILURE_MESSAGE;
      case 'user-not-found':
        return USER_NOT_FOUND_FAILURE_MESSAGE;
      case 'wrong-password':
        return WRONG_PASSWORD_FAILURE_MESSAGE;
      default:
        return LOG_IN_WITH_EMAIL_AND_PASSWORD_FAILURE_MESSAGE;
    }
  } else if (failure is LogOutFailure) {
    return LOG_OUT_FAILURE_MESSAGE;
  } else if (failure is OfflineFailure) {
    return OFFLINE_FAILURE_MESSAGE;
  } else if (failure is EmptyCacheFailure) {
    return EMPTY_CACHE_FAILURE_MESSAGE;
  } else {
    return UNEXPECTED_FAILURE_MESSAGE;
  }
}

当我按下按钮时,它工作得很好,但它显示了多个小吃条(例如,当我注销时,它可以显示5个小吃条告诉我“注销成功”一个接一个),我有两个问题,为什么这些小吃条显示多倍,我可以改变我的代码来删除bug?

l7wslrjt

l7wslrjt1#

我认为这是因为你在你的主注册你的Bloc(它可以是你的主。 dart 或你的主应用程序的类)纠正我,如果我错了。
如果我们把它登记在主要的地方,这个集团就会变成一个全球性的集团。因此,如果您在1页使用Bloc listener,然后在另一页再次调用该块,它将调用您在listener中设置的结果(在本例中显示snackBar)。
因此,只在需要使用该侦听器的页面上注册块。
示例:

class ScreenLogin extends StatelessWidget {
  const ScreenLogin({super.key});

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(providers: [
      BlocProvider(create: (context) => sl<LoginBloc>()), // Register Your Bloc Here
    ], child: const Content());
  }
}

class Content extends StatelessWidget {
  const Content({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocListener<LoginBloc, LoginState>(
        listener: (context, state) {
          if(state is LoginSuccess){
            //Do Something
          }
        },
        child: Container(),
      ),
    );
  }
}

从这里移除您的寄存器组

void main() {
   runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  final String initialRoute;
  const MyApp({Key? key, required this.initialRoute}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (_) => sl<LoginBloc>(), //Remove the Bloc From Here
        )
      ],
      child: MaterialApp(
        title: 'Bla bla bla bla ',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          fontFamily: "Roboto",
          primaryColor: const Color(0xFFFC8F7B),
        ),
        initialRoute: initialRoute,
        routes: AppNavigation.routes,
      ),
    );
  }
}

相关问题