Home > Software engineering >  Flutter app global state frorm streamsubscription of firebase document
Flutter app global state frorm streamsubscription of firebase document

Time:09-11

I try to make indicator which can show actual status open or close of some place. To do this I use boolean value from firebase documents field, true for open and false for close. I made cubit (from bloc) to set a global state for entire application.

Technicaly its should work like: Stream form documents filed - converted to my own datamodel - emit stream subscritpion of this like global state end change look of widget depends on state value (switch widget1 or widget2).

My code works, but not what I want. The widget does not realtime change but after reloading the page. I'm not sure that my stream code is wrong or the delivery state is wrong. Please help, I'm new to flutter.

My codes below:

GlobalDataSource:

class GlobalRemoteDataSource {
//  one time read
  // Future<GlobalDataModel> getGlobalState() async {
  //   final globalState = await FirebaseFirestore.instance
  //       .collection('global')
  //       .doc('states')
  //       .get();
  //   return GlobalDataModel(admin: null, open: globalState['indicator']);
  // }

  Stream<GlobalDataModel> getGlobalStateStream() {
    return FirebaseFirestore.instance
        .collection('global')
        .doc('states')
        .snapshots()
        .map((documentSnapshot) {
      return GlobalDataModel(
          open: documentSnapshot.get('indicator'), admin: null);
    });
  }
}

Cubit:

class RootCubit extends Cubit<RootState> {
  RootCubit(this._globalRemoteDataSource)
      : super(RootState(open: null, admin: false));

  final GlobalRemoteDataSource _globalRemoteDataSource;
  StreamSubscription? _streamSubscription;

  Future<void> initGlobalSteram() async {
    _streamSubscription =
        _globalRemoteDataSource.getGlobalStateStream().listen((state) {
      emit(RootState(open: state.open, admin: state.admin));
    });
  }

  @override
  Future<void> close() {
    _streamSubscription?.cancel();
    return super.close();
  }
}

State implementation for the entire application:

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) =>
          RootCubit(GlobalRemoteDataSource())..initGlobalSteram(),
      child: BlocBuilder<RootCubit, RootState>(
        builder: (context, state) {
          return MaterialApp(
            title: 'Carmel Bakery',
            theme: GlobalTheme(),
            home: AuthGate(),
          );
        },
      ),
    );
  }
}

Place where I use state value:

class OfferPageCakes extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final bool? indicator = context.read<RootCubit>().state.open;
    return Scaffold(
      appBar: AppBar(
        leading: indicator == null
            ? Container(
                height: 50, width: 50, child: CircularProgressIndicator())
            : PlaceStatusIndicator(indicator: indicator),
        title: const Center(
          child: Text('CIASTA'),
        ),
        actions: [
          Column(
            children: [
              AnimatedArrowRight(),
              const Text("NAPOJE"),
            ],
          )
        ],
      ),
      body: ListView(
        children: [
          Text('dane z Firebase'),
          indicator == null
              ? Container(
                  height: 50, width: 50, child: CircularProgressIndicator())
              : PlaceStatusIndicator(indicator: indicator)
        ],
      ),
    );
  }
}

CodePudding user response:

Maybe try to wrap your Scaffold in BlocProvider and then declare indicator variable inside.

  Widget build(BuildContext context) {
    return BlocBuilder<RootCubit, RootState>(
    builder: (context, rootState) {
      final bool? indicator = rootState.open;
      return Scaffold(
  • Related