I’m trying to get the totalValue in the title of the appBar. The value of totalValue comes from the Streambuilder and it changes everytime I press the FilterChips.
Code:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:calc_tag/services/get_items_for_month.dart';
import 'package:flutter/scheduler.dart';
class FilterChipExample extends StatefulWidget {
final int year;
final int month;
FilterChipExample({required this.year, required this.month});
@override
_FilterChipExampleState createState() => _FilterChipExampleState();
}
class _FilterChipExampleState extends State<FilterChipExample> {
final List<String> categories = [
'Costanera Norte',
'Vespucio Norte',
'Vespucio Sur',
'Autopista Central',
];
num totalValue = 0;
String titulo = '';
List<String> selectedCategories = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$totalValue'),
),
body: Column(
children: [
Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
child: Wrap(
children: categories.map((category) {
return FilterChip(
selectedColor: Colors.grey,
showCheckmark: false,
visualDensity:
const VisualDensity(vertical: -4, horizontal: -4),
selected: selectedCategories.contains(category),
label: Container(
margin: const EdgeInsets.all(0.0),
child: Text(category),
),
onSelected: (selected) {
setState(() {
if (selected) {
selectedCategories.add(category);
} else {
selectedCategories.remove(category);
}
});
});
}).toList(),
),
),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: getItemsForMonth(widget.year, widget.month),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final documents = snapshot.data!.docs;
final filteredDocuments = documents.where((element) {
return selectedCategories.contains(element['contract']);
}).toList();
// setState(() {
totalValue = filteredDocuments.fold(
0.0, (num sum, doc) => sum + (doc['cost'] ?? 0.0));
// });
return ListView.builder(
itemCount: filteredDocuments.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
'${filteredDocuments[index]['contract']} ${(filteredDocuments[index]['created'] as Timestamp).toDate()}'),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'$ ${filteredDocuments[index]['cost'].toString()}'),
const SizedBox(
width: 30,
),
Text(
' ${filteredDocuments[index]['category'].toString()} ',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 10),
),
],
),
);
},
);
},
),
),
],
),
);
}
}
I tried using setState(){} inside the StreamBuilder but I get this error:
FlutterError (setState() or markNeedsBuild() called during build.
This FilterChipExample widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
FilterChipExample
The widget which was currently being built when the offending call was made was:
StreamBuilder<QuerySnapshot<Object?>>)
2
Answers
The easiest solution for this case is to up the StreamBuilder so that the appBar is inside it.
when you encountering errors like
try to use
WidgetsBinding.instance.addPostFrameCallback
to delay updating your widgets