Mikael Wills
Mikael Wills

Reputation: 195

Adding up values stored in Firebase and displaying total in a FutureBuilder

Im trying to retrieve a list of monthly expenses from Firebase, add up the amount of each monthly expense and show it in a FutureBuilder. In the Text widget i'm simply getting null. Been trying to google an answer for the past half hour but i don't even know where to begin as in what to search for.

Tried throwing in some print statement to see whats going on but any print statement I put in after the provider call doesn't show.

EDIT: Added Future casts to the commented lines. still no luck. but ignored the variable and called the future function directly from the FutureBuilder. Now all print statements are working and it is returning an instance of 'MonthlyExpense'. It is null still.

class SummaryView extends StatefulWidget {
  @override
  _SummaryViewState createState() => _SummaryViewState();
}

class _SummaryViewState extends State<SummaryView> {
  

  /*  Removed future variable and initState()
  Future<double> _totalExpensesAmount; //Future<double> added

  @override
  void initState() {
    super.initState();
    print("init");
    _totalExpensesAmount = _getTotalExpensesAmount();
  }
  */

  Future<double> _getTotalExpensesAmount() async { //Future<double> added
    print("started");
    final user = Provider.of<BPUser>(context);
    print("user: $user");
    double expensesTotal = 0.0;

    var snapshot = await FirebaseFirestore.instance
        .collection("BPUsers/${user.uid}/monthlyexpenses")
        .get();
    print(snapshot);
    List<MonthlyExpense> searchedProducts =
        DatabaseService().monthlyExpenseListFromSnapshot(snapshot);
    print(searchedProducts);
    for (var i = searchedProducts.length; i >= 1; i--) {
      expensesTotal += double.parse(searchedProducts[i].amount);
    }
    return Future<double>expensesTotal; //Future<double> added
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: mainBackgroundColor,
      body: Column(
        children: [
          Container(
            child: FutureBuilder(
                future: _getTotalExpensesAmount(), // called the function directly
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.done) {
                    return Text(
                      "${snapshot.data}",
                      style: nameStyle,
                    );
                  } else {
                    return Text("Loading...");
                  }
                }),
          )
        ],
      ),
    );
  }
}

The monthlyExpenseListFromSnapshot function (which works perfectly in another widget I use it in):

List<MonthlyExpense> monthlyExpenseListFromSnapshot(QuerySnapshot snapshot) {
    return snapshot.docs.map((doc) {
      return MonthlyExpense(
          name: doc.data()['name'] ?? '',
          amount: doc.data()['amount'] ?? '',
          isActive: doc.data()['isActive']);
    }).toList();
  }

The firebase database: enter image description here

Upvotes: 0

Views: 121

Answers (3)

Mikael Wills
Mikael Wills

Reputation: 195

First issue was to skip the variable and the init call and call the _getTotalExpensesAmount() future function (adding the Future signature to it).

Secondly my for loop was wrong. needed to change the conditions to:

for (var i = searchedProducts.length - 1; i >= 0; i--) {

Everything is working fine now!

Upvotes: 1

Juancki
Juancki

Reputation: 1872

You need to define the signature of the function to represent what it is expected, in this case Future <double>.

// ....
Future<double> _getTotalExpensesAmount() async {

Upvotes: 1

Scott Godfrey
Scott Godfrey

Reputation: 671

You're getExpensesTotal doesn't return a future. Because of this, the widgetvwill never rebuild since the data is already calculated. FutureBuilder and StreamBuilder only cause a rebuild AFTER data been loaded after the initial build. Try surrounding your expensesTotal with Future.value.

Upvotes: 1

Related Questions