Search code examples
flutterfirebasestreambuilderflutter-bloc

How to listen to stream variable using bloc | Could not find the correct Provider<StateStreamable<.>> above this BlocBuilder<StateStreamable<.>, <.>>


I am looking for the right way of implementing streams using bloc. I have a appUser of type AppUser which changes based on the stream . I want to listen to this appUser throughout the application using bloc.

My present implementation .

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
      
@override
Widget build(BuildContext context) {
  return MultiBlocProvider(
    providers: [
      BlocProvider(lazy: false, create: (context) => UserCubit())
    ],
    child: MaterialApp(
      theme: customTheme(context),
      home: const MyWidget(),
    ));
}

UserCubit.dart

class UserCubit extends Cubit<UserState> {
  final _firestore = FirebaseFirestore.instance;
  final User? _currentUser = FirebaseAuth.instance.currentUser;
  AppUser? appUser;

  UserCubit() : super(UserInitialState()) {
    emit(UserMainLoadingState());
    _firestore
        .collection("users")
        .doc(_currentUser?.uid) // or whatever your collection name is
        .snapshots()
        .listen((event) {
      event.exists
          ? {
              print(" The user is ${event.data()}"),
              appUser = AppUser.fromjson(event.data()!, event.id), // here it changes
              emit(UserExists(user: appUser!))
            }
          : {print("Oops no user"), emit(UserNotExists())};
    });
  }

MyWidget.dart

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Center(
        child: BlocBuilder(builder: (context, state) {
      return Text(BlocProvider.of<UserCubit>(context).appUser.toString()); // I want to get the changes of appUser
    })));
  }
}

Solution

  • You should move your AppUser to the UserState like this:

    abstract class UserState extends Equatable {
      final AppUser? appUser;
      const UserState({this.appUser});
    
      @override
      List<Object?> get props => [appUser];
    }
    
    class UserExists extends UserState {
      const UserExists({required AppUser user}) : super(appUser: user);
    }
    

    And then you can read it from state of your BlocBuilder like so:

    BlocBuilder<UserCubit, UserState>(
      builder: (context, state) {
        return Text(state.appUser.toString());
      },
    );