flutter 将事件从流添加到我的Bloc然后重定向到另一个路由时出现问题(身份验证过程)

aij0ehis  于 2023-05-19  发布在  Flutter
关注(0)|答案(1)|浏览(96)

所以我将提供我的代码,因为它是理解我的问题的最简单的方法。我从我的UserService类开始,它创建一个流,每当AuthStateChanges(来自FirebaseAuth)流触发时,该流就会连续接收数据

enum AuthEventEnum { authenticated, unauthenticated }

class UserService {
  StreamController<AuthEventEnum> controller =
      StreamController<AuthEventEnum>();
  late final StreamSubscription _subscription;
  StreamSubscription get subscription => _subscription;
  UserService() {
    final StreamSubscription _subscription =
        FirebaseAuth.instance.authStateChanges().listen(
      (User? user) {
        print(user);
        if (user == null) {
          controller.sink.add(AuthEventEnum.unauthenticated);
        } else if (user != null) {
          controller.sink.add(AuthEventEnum.authenticated);
        }
      },
    );
  }
}

现在我试着连接到我的AuthBloc(我很确定错误来自这里...但我不知道如何修复它)

class AuthBloc extends Bloc<AuthEventEnum, AuthState> {
  final UserService _userService;
  late final _addEvents;

  AuthBloc(this._userService) : super(AuthInitial()) {
    _addEvents = _userService.controller.stream.listen(
      (event) {
        if (event == AuthEventEnum.authenticated) {
          mapEventToState(AuthEventEnum.authenticated);
        } else if (event == AuthEventEnum.unauthenticated) {
          mapEventToState(AuthEventEnum.authenticated);
        }
      },
    );
  }

  @override
  Stream<AuthState> mapEventToState(AuthEventEnum event) async* {
    if (event == AuthEventEnum.authenticated) {
      yield AuthAuthenticatedState();
    } else if (event == AuthEventEnum.unauthenticated) {
      yield AuthUnauthenticatedState();
    }
  }
}

现在的最终目标是根据他们的AuthStatus将用户重定向到不同的路由…

class AppRouter {
  AuthBloc authBloc;
  late final GoRouter routes;
  AppRouter({required this.authBloc}) {
    routes = GoRouter(
      initialLocation: '/home',
      redirect: (context, state) {
        if (authBloc.state is AuthInitial) {
          return '/register';
        } else if (authBloc.state is AuthAuthenticatedState) {
          return '/app';
        } else if (authBloc.state is AuthUnauthenticatedState) {
          return '/register';
        }
      },
      routes: [
        GoRoute(
          path: '/home',
          builder: (context, state) => const HomePage(),
        ),
        GoRoute(
          path: '/login',
          builder: (context, state) => const LoginPage(),
        ),
        GoRoute(
          path: '/register',
          builder: (context, state) => const RegisterPage(),
        ),
        GoRoute(
          path: '/app',
          builder: (context, state) => const AppPage(),
        ),
      ],
    );
  }
}

在主函数相关情况下,其为:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  final authService = UserService();
  final authBloc = AuthBloc(authService);

  runApp(
    BlocProvider.value(
      value: authBloc,
      child: MaterialApp.router(
        title: 'flutter demo',
        theme: ThemeData(primarySwatch: Colors.blue),
        routerConfig: AppRouter(authBloc: authBloc).routes,
      ),
    ),
  );
}

了解有关Bloc如何处理流以及Bloc如何侦听其他流并向自身添加事件的详细信息

jhdbpxl9

jhdbpxl91#

对于初学者来说,你正在使用一个非常过时的Bloc API。@ Krish Bhanushali在评论中链接的文章是一个良好的开端。没有理由继续使用mapEventToState,您应该更新您的Bloc包。
除此之外,直接使用Bloc处理流的最佳方式是emit.forEach(...)

// The functionality you had in your UserService class can be simplified to this

class UserService {
  Stream<User?> get userSubscription =>
      FirebaseAuth.instance.authStateChanges();
}

// Create a standard Bloc event that will init a stream listener

abstract class AuthEvent {}

class AuthInitListener extends AuthEvent {}

// Use AuthEvent in Bloc instead of the enum

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final UserService _userService;

  AuthBloc(this._userService) : super(AuthInitial()) {
    // register all event handlers in the constructor
    on<AuthInitListener>(_onInitAuthListener); 
  }

  // Event handler function

  Future<void> _onInitAuthListener(
    AuthInitListener event, 
    Emitter<AuthState> emit,
    ) async {
    // emit.forEach takes a stream and returns a new state when the stream
    // is updated
    emit.forEach(
      _userService.userSubscription, // stream from UserService
    // The state returned from the onData callback is what is emitted and all listeners will be notified
     onData: (User? user) {
        return user != null
            ? AuthAuthenticatedState()
            : AuthUnauthenticatedState();
      },
    );
  }
}

然后在初始化BlocProvider时调用该事件。

runApp(
    BlocProvider.value(
      value: authBloc..add(AuthInitListener()), // calling the event here
...

所以这里的总体思路是使用Bloc提供的工具来处理流。Firebase已经提供了一个Stream<User>,所以只需要使用它。无需创建额外的流/控制器/订阅等。它更简单,更不容易出错。

相关问题