skip to Main Content

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


  1. The easiest solution for this case is to up the StreamBuilder so that the appBar is inside it.

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatefulWidget {
      const MyApp({super.key});
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      var i = 1;
      StreamController<int> s = StreamController<int>();
      var title = "Title";
    
      @override
      void initState() {
        super.initState();
        () async {
          while (true) {
            await Future.delayed(const Duration(seconds: 2), () {
              s.add(i++);
            });
          }
        }();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Material App',
          home: StreamBuilder<int>(
              stream: s.stream,
              builder: (context, snapshot) {
                return Scaffold(
                  appBar: AppBar(
                    title: Text('Stream value: ${snapshot.data}'),
                  ),
                  body: Center(
                    child: Text('Stream value: ${snapshot.data}'),
                  ),
                );
              }),
        );
      }
    }
    
    Login or Signup to reply.
  2. when you encountering errors like

    setState() or markNeedsBuild() called during build

    try to use WidgetsBinding.instance.addPostFrameCallback to delay updating your widgets

        WidgetsBinding.instance.addPostFrameCallback((_) {
          setState(() {
          totalValue = filteredDocuments.fold(
              0.0, (num sum, doc) => sum + (doc['cost'] ?? 0.0));
          });
        });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search