Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use setState for just one widget rather than whole page?

I have a Future which takes a value from my Cloud Firestore database and displays an icon in the body of my app. If the value in the database changes, the icon updates. I have been using setState to rebuild my widget tree and this works fine - the app reacts almost instantly to changes in my Cloud Firestore database. This is great! Code:

Future<void> takenSurvey2() async {
 final sn = await Firestore.instance
 .collection('Controller')
      .document('Current Survey')
      .get();
 surveyName2 = sn['cs'];
 final snapShot = await Firestore.instance
 .collection('$surveyName2' + '_entrants')
      .document(userid)
      .get();
 if (snapShot.exists) {
 takenSurvey = true;
  } else {
 takenSurvey = false;
  }
  setState(() {});
}

and the place where the icon is shown is coded like this (within a stack):

Positioned(
    right: 30,
    top: 20,
    child: FutureBuilder(
        future: takenSurvey2(),
        builder: (context, snapshot) {
 if (takenSurvey == false) {
 return Icon(
              Foundation.burst_new,
              size: 48,
              color: Color(0xff303841),
            );
          } else if (takenSurvey == true) {
 return Icon(
              Foundation.check,
              size: 48,
              color: Color(0xff303841),
            );
          } else
            return Container();
        })),

However, I've added in my AdMob adverts using the 'admob_flutter' package, and because setState seems to be constantly running, the advert cannot load - I just see a flashing box. If I remove setState from the code above, the adverts load, but the icons in my app do not update when the value in Cloud Firestore changes.

How do I either exclude my admob_flutter widget from setState, or just use setState (or something else) to only update my FutureBuilder widget? I would like to do so in a way which limits the number of calls on Cloud Firestore, but this is not essential.

I have tried using nested conditional logic but it doesn't seem to work!

Thanks!

like image 817
David Avatar asked Nov 30 '25 02:11

David


1 Answers

What I can suggest you is to use a ValueNotifier, If you want to manipulate simple data like in this case a boolean value, this widget can be perfect. Also you can use a void function instead of a future as follows:

  // Add a ValueNotifier for a single value
  final ValueNotifier<bool> _takenSurvey = ValueNotifier(null);

  void takenSurvey2() async {
    final sn = await Firestore.instance
        .collection('Controller')
        .document('Current Survey')
        .get();
    surveyName2 = sn['cs'];
    final snapShot = await Firestore.instance
        .collection('$surveyName2' + '_entrants')
        .document(userid)
        .get();
    if (snapShot.exists) {
      // Update the value of the ValueNotifier 
      _takenSurvey.value = true;
    } else {
      _takenSurvey.value = false;
    }
  }

And to listen the changes in that value use a ValueListenableBuilder:

Positioned(
  right: 30,
  top: 20,
  child: ValueListenableBuilder(
    valueListenable: _takenSurvey,
    builder: (context, takenSurvey, child) {
      if(takenSurvey == null){
        return Container();
      }
      else{
        if (takenSurvey == false) {
          return Icon(
            Foundation.burst_new,
            size: 48,
            color: Color(0xff303841),
          );
        } else {
          return Icon(
            Foundation.check,
            size: 48,
            color: Color(0xff303841),
          );
        } 
      }
    },
  ),
)

Hope it helps.

like image 115
Luis Miguel Mantilla Avatar answered Dec 01 '25 15:12

Luis Miguel Mantilla