2

Streambuilder, ChangeNotifier and Consumer cannot figure out how to use correctly. Flutter

I've tried and tried and tried, I've searched a lot but I cannot figure this out: I'm using a Streambuilder this should update a ChangeNotifier that should trigger rebuild in my Consumer widget. Supposedly... but even if I call the provider with the (listen: false) option I've got this error

The following assertion was thrown while dispatching notifications for HealthCheckDataNotifier: setState() or markNeedsBuild() called during build. the widget which was currently being built when the offending call was made was: StreamBuilder<List>

Important: I cannot create the stream sooner because I need to collect other informations before reading firebase, see (userMember: userMember)

Widget build(BuildContext context) {
  return MultiProvider(
      providers: [
        /// I have other provider...
        ChangeNotifierProvider<HealthCheckDataNotifier>(create: (context) => HealthCheckDataNotifier())
       
      ],
 child: MaterialApp(...

then my Change notifier look like this

class HealthCheckDataNotifier extends ChangeNotifier {
  HealthCheckData healthCheckData = HealthCheckData(
      nonCrewMember: false,
      dateTime: DateTime.now(),
      cleared: false,
      );

  void upDate(HealthCheckData _healthCheckData) {
    healthCheckData = _healthCheckData;
    notifyListeners();
  }
}

then the Streambuilder

return StreamBuilder<List<HealthCheckData>>(
        stream: HeathCheckService(userMember: userMember).healthCheckData,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.active) {
            if (snapshot.hasData) {
              if (snapshot.data!.isNotEmpty) {
                healthCheckData = snapshot.data?.first;
              }

              if (healthCheckData != null) {
                timeDifference = healthCheckData!.dateTime.difference(DateTime.now()).inHours;
                _cleared = healthCheckData!.cleared;
                if (timeDifference < -12) {
                  healthCheckData!.cleared = false;
                  _cleared = false;
                }
///The problem is here but don't know where to put this or how should be done

                Provider.of<HealthCheckDataNotifier>(context, listen: false).upDate(healthCheckData!);
              } 
            }
            return Builder(builder: (context) {
              return Provider<HealthCheckData?>.value(
                value: healthCheckData,
                builder: (BuildContext context, _) {
                  return const HealthButton();
                },
              );
            });
          } else {
            return const Text('checking health'); //Scaffold(body: Loading(message: 'checking...'));
          }
        });

and finally the Consumer (note: the consumer is on another Route)

return Consumer<HealthCheckDataNotifier>(
      builder: (context, hN, _) {
        if (hN.healthCheckData.cleared) {
          _cleared = true;
          return Container(
            color: _cleared ? Colors.green : Colors.amber[900],

Hope is enough clear, Thank you so very much for your time!

Mirakle
  • 33
  • 4

1 Answers1

1

it is not possible to setState(or anything that trigger rerender) in the builder callback

just like you don't setState in React render

const A =()=>{
   const [state, setState] = useState([])
   return (
   <div>
     {setState([])}
     <p>will not work</p>
   </div> 
   )
}

it will not work for obvious reason, render --> setState --> render --> setState --> (infinite loop)

so the solution is similar to how we do it in React, move them to useEffect (example using firebase onAuthChange)

class _MyAppState extends Stateful<MyApp> {
  StreamSubscription<User?>? _userStream;
  var _waiting = true;
  User? _user;

  @override
  void initState() {
    super.initState();
    _userStream = FirebaseAuth.instance.authStateChanges().listen((user) async {
      setState(() {
        _waiting = false;
        _user = user;
      });
    }, onError: (error) {
      setState(() {
        _waiting = false;
      });
    });
  }
  @override
  void dispose() {
    super.dispose();
    _userStream?.cancel();
  }

  @override
  Widget build(context) {
    return Container()
  }
}
Acid Coder
  • 2,047
  • 15
  • 21