skip to Main Content

i have a method that changes a static boolean variable that i want to use to conditionally display a button widget, the value is set to true if a condition is met else it is set to false, when i print the value of the variable inside the method it displays the expected boolean value, but the problem is when i call that boolean variable from another class it doesnt print out the expected value even if the condition is met, sometimes it will still print out true even when the condition is not met, and vice versa

I created the method here


class GetBool {
//THIS IS THE VARIABLE I WANT TO BE CHANGING ITS VALUE
  static bool? isAdded;
}

void getBool(String model) async {
    print("function ran");
    User? user = FirebaseAuth.instance.currentUser;
    DocumentSnapshot<Map<String, dynamic>> document = await FirebaseFirestore
        .instance
        .collection("Cart")
        .doc(user!.uid)
        .get();

    DocumentReference docRef =
        FirebaseFirestore.instance.collection("Cart").doc(user.uid);
    List list = [];
    for (var doc in document.data()!['cart'] as Iterable) {
      list.add(doc['model']);
      print("model says before condition $model");
      print("list is $list");

      if (list.contains(model)) {
        GetBool.isAdded = true;
        print("this item exist");
        print(GetBool.isAdded);
      } else {
        GetBool.isAdded = false;
        print("this item doesnt exist");
        print(GetBool.isAdded);
      }
    }
  }

this is where i call the method

class WatchTile extends StatefulWidget {
  final Watch? watch;
  const WatchTile({this.watch, super.key});

  @override
  State<WatchTile> createState() => _WatchTileState();
}

class _WatchTileState extends State<WatchTile> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.pushNamed(
          context,
          "/watch_details",
          arguments: widget.watch,
        );
//I CALL THE METHOD HERE WHEN I NAVIGATE TO watch_details.dart
        DatabaseService().getBool(widget.watch!.model!);
      },
      child: Container(
        decoration: BoxDecoration(
          border: Border.all(color: Colors.black),
        ),
        child: Column(
          children: <Widget>[
            Container(
              height: 200,
              decoration: BoxDecoration(
                  image: DecorationImage(
                      image: NetworkImage(widget.watch!.image!),
                      fit: BoxFit.cover)),
            ),
            Container(
              padding: const EdgeInsets.only(left: 4.0),
              width: double.maxFinite,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    widget.watch!.brand!,
                    style: const TextStyle(color: Colors.grey),
                  ),
                  Text(widget.watch!.model!),
                  Text('$${widget.watch!.price!}'),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

watch_details.dart



final FirebaseAuth auth = FirebaseAuth.instance;
User? user = auth.currentUser;

class WatchDetail extends StatefulWidget {
  const WatchDetail({super.key});

  @override
  State<WatchDetail> createState() => _WatchDetailState();
}

class _WatchDetailState extends State<WatchDetail> {
  final DatabaseService _database = DatabaseService();

  int itemQuantity = 1;
  @override
  Widget build(BuildContext context) {
    print("getbool says ${GetBool.isAdded}");

    final watch = ModalRoute.of(context)?.settings.arguments as Watch;
   

    print("route args is ${watch.brand}");
    return Scaffold(
      appBar: AppBar(
        title: Text(watch.brand!),
      ),
      body: Column(
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
//THIS IS WHERE I WANT TO CONDITIONALLY DISPLAY A WIDGET
                child: GetBool.isAdded == false
                    ? ElevatedButton(
                        style: ButtonStyle(),
                        onPressed: () async {
                          await _database.addToCart(watch.model!, watch.brand!,
                              itemQuantity, int.parse(watch.price!));
                        },
                        child: Text("Add To Cart"),
                      )
                    : ElevatedButton(
                        onPressed: () {}, child: Text("Added To Cart")),
              ),
              
            ],
          )
        ],
      ),
    );
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    i ended up changing the return type of the getBool method to a Future like so

     Future<bool> getBool(String model) async {
        print("function ran");
        User? user = FirebaseAuth.instance.currentUser;
        DocumentSnapshot<Map<String, dynamic>> document = await FirebaseFirestore
            .instance
            .collection("Cart")
            .doc(user!.uid)
            .get();
    
        DocumentReference docRef =
            FirebaseFirestore.instance.collection("Cart").doc(user.uid);
        List list = [];
        for (var doc in document.data()!['cart'] as Iterable) {
          list.add(doc['model']);
          print("model says before condition $model");
          print("list is $list");
    
          if (list.contains(model)) {
            // GetBool.isAdded = true;
            print("this item  exist");
    
            return true;
          } else {
            GetBool.isAdded!.then((item) => item = false);
    
            print("this item doesnt exist");
            return false;
          }
        }
        return false;
      }
    

    then in my watch_details.dart i wrapped my "add to cart" button with a future builder, so snapshot.data will return the value of the getBool function(either true or false) and the add to cart button will change depending on the value

    FutureBuilder(
                        future: DatabaseService().getBool(watch.model!),
                        builder: (context, snapshot) {
                          print("snapshot data bool is ${snapshot.data}");
                          return Container(
                              child: snapshot.data == true
                                  ? ElevatedButton(
                                      style: ElevatedButton.styleFrom(
                                          backgroundColor: Colors.black,
                                          shape: const RoundedRectangleBorder(
                                              borderRadius: BorderRadius.zero)),
                                      onPressed: () {},
                                      child: const Row(
                                        children: [
                                          Text(
                                            "Added To Cart",
                                            style: TextStyle(color: Colors.white),
                                          ),
                                          SizedBox(
                                            width: 10,
                                          ),
                                          Icon(
                                            Icons.shopping_cart,
                                            color: Colors.white,
                                          ),
                                        ],
                                      ))
                                  : ElevatedButton(
                                      onPressed: () async {
                                        await _database.addToCart(
                                          watch.model!,
                                          watch.brand!,
                                          itemQuantity,
                                          int.parse(watch.price!),
                                          watch.images![0],
                                        );
                                        showDialog(
                                            context: context,
                                            builder: (context) => AlertDialog(
                                                  title: const Text("Watch Hub"),
                                                  content:
                                                      const Text("Added To Cart"),
                                                  contentPadding:
                                                      EdgeInsets.all(20.0),
                                                  actions: [
                                                    TextButton(
                                                        onPressed: () {
                                                          Navigator.of(context)
                                                              .pop();
                                                        },
                                                        child:
                                                            (const Text("Close")))
                                                  ],
                                                ));
                                      },
                                      child: const Text("Add To Cart"),
                                    ));
                        }),
    

  2. The problem is not where you call the isAdded variable, but when you call it.

    Since the data is loaded from Firestore asynchronously, any code that wants to use the value from the database has to wait until that data is available.

    In your getBool function you do that by using await here:

    DocumentSnapshot<Map<String, dynamic>> document = await FirebaseFirestore
        .instance
        .collection("Cart")
        .doc(user!.uid)
        .get();
    

    The await here ensures that the document is loaded before the rest of the code in getBool executes.

    But the other that that accesses the isAdded value is not using await to wait for the data to load, so it sees whatever value is available whenever that code executes.

    If you want to ensure the data is loaded from the database before the code uses it, you’ll have to use a Future<bool> instead of the bool? that you have for isAdded now.


    This is an incredibly common problem when dealing with modern web/cloud APIs, so I recommend reading up on how to handle such APIs in your own code on Asynchronous programming: futures, async, await, Async Widgets and Async/Await – Flutter in Focus

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search