Home > Mobile >  setState inside FutureBuilder
setState inside FutureBuilder

Time:01-14

I am designing a screen with a list of documents and I decided to use ToggleButtons on the header to filter the documents depending on the year they were published.

The information about the documents is loaded from an URL and I use a FutureBuilder to build the screen. And when I click on a ToggleButton, the setState doesn't work, it doesn't updates the screen. And I don't know why.

That way is impossible to filter the documents.

I share the code:

class ListOfDocuments extends StatefulWidget {
  final SingleItem singleItem;
  final String sectionRowName;
  const ListOfDocuments(
      {Key? key, required this.singleItem, required this.sectionRowName})
      : super(key: key);

  @override
  State<ListOfDocuments> createState() => _ListOfDocumentsState();
}

class _ListOfDocumentsState extends State<ListOfDocuments> {
  // Iniciamos el servicio para pedir a la web la información.
  final HttpService httpService = HttpService();
  // Iniciamos la variable donde almacenaremos los documentos.
  Future<List<Document>>? listadoDocumentos;
  // Obtenemos el código del idioma utilizado.
  String myLocale = Intl.getCurrentLocale();

  // Cargamos documentos.
  void cargarDocumentos() async {
    // Pedimos el listado de documentos.
    listadoDocumentos = httpService.getDocList(
      myLocale.toString(),
      widget.sectionRowName,
      widget.singleItem,
    );
  }

  @override
  void initState() {
    super.initState();
    // Cargamos los documentos al iniciar.
    cargarDocumentos();
  }

  @override
  Widget build(BuildContext context) {
    // Screen size.
    double screenWidth = MediaQuery.of(context).size.width;
    double screenHeight = MediaQuery.of(context).size.height;
    // For ToggleButtons.
    List<Widget> years = <Widget>[
      Padding(
        padding: const EdgeInsets.all(4.0),
        child: Text(S.of(context).all),
      ),
      const Text("2005"),
      const Text("2006"),
      const Text("2007"),
      const Text("2008"),
      const Text("2009"),
      const Text("2010"),
      const Text("2011"),
      const Text("2012"),
      const Text("2013"),
    ];
    List<bool> selectedYear = <bool>[
      true,
      false,
      false,
      false,
      false,
      false,
      false,
      false,
      false,
      false
    ];

    return Scaffold(
      backgroundColor: kPrimaryColor,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0.0,
        centerTitle: true,
        title: getTitle(widget.sectionRowName, widget.singleItem.name, context),
      ),
      body: FutureBuilder(
        future: listadoDocumentos,
        builder: (BuildContext context, AsyncSnapshot snapshotFromList) {
          if (snapshotFromList.hasData) {
            // Hacemos una lista de titulos con fechas.
            List<Document> docList = List.generate(
              snapshotFromList.data.length,
              (index) => Document(
                id: snapshotFromList.data[index].id,
                fecha: snapshotFromList.data[index].fecha,
                titulo: snapshotFromList.data[index].titulo,
              ),
            );

            return Column(
              children: [
                SingleChildScrollView(
                  scrollDirection: Axis.horizontal,
                  child: Padding(
                    padding: const EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 0.0),
                    child: ToggleButtons(
                      direction: Axis.horizontal,
                      textStyle: kAppBarStyle.copyWith(fontSize: 18.0),
                      borderColor: Colors.yellow,
                      borderWidth: 1.0,
                      selectedBorderColor: Colors.yellowAccent,
                      fillColor: Colors.yellowAccent.withOpacity(0.2),
                      color: Colors.white,
                      selectedColor: Colors.white,
                      isSelected: selectedYear,
                      borderRadius:
                          const BorderRadius.all(Radius.circular(10.0)),
                      onPressed: (int index) {
                        setState(() {
                          // The button that is tapped is set to true, and the others to false.
                          for (int i = 0; i < selectedYear.length; i  ) {
                            selectedYear[i] = i == index;
                          }
                        });
                      },
                      children: years,
                    ),
                  ),
                ),
                Expanded(
                  child: Padding(
                    padding:
                        EdgeInsets.fromLTRB(0.0, 0.0, screenWidth * 0.05, 0.0),
                    child: SingleChildScrollView(
                      scrollDirection: Axis.vertical,
                      child: ListView.separated(
                        itemCount: docList.length,
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        separatorBuilder: (BuildContext context, int index) {
                          return const Divider(
                            thickness: 2.0,
                            color: Colors.white10,
                            indent: 15.0,
                          );
                        },
                        itemBuilder: (context, index) {
                          DateTime dateFormated = DateFormat('dd/MM/yyyy')
                              .parse(docList[index].fecha);
                          return ListTile(
                            leading: Column(
                              crossAxisAlignment: CrossAxisAlignment.center,
                              mainAxisAlignment: MainAxisAlignment.center,
                              mainAxisSize: MainAxisSize.max,
                              children: [
                                Text(
                                  DateFormat("dd").format(dateFormated),
                                  style: kDocTitleInList.copyWith(
                                    color: Colors.white70,
                                  ),
                                ),
                                Text(
                                  DateFormat("MMM")
                                      .format(dateFormated)
                                      .toUpperCase(),
                                  style: kDocTitleInList.copyWith(
                                    fontSize: 10.0,
                                    color: Colors.white70,
                                  ),
                                ),
                                Text(
                                  DateFormat("yyyy").format(dateFormated),
                                  style: kDocTitleInList.copyWith(
                                    fontSize: 14.0,
                                    color: Colors.white70,
                                  ),
                                ),
                              ],
                            ),
                            title: Text(
                              docList[index].titulo,
                              style: kDocTitleInList,
                            ),
                            trailing: const Icon(
                              Icons.arrow_forward_ios_rounded,
                              color: Colors.white,
                            ),
                            onTap: () {
                              showDialog(
                                context: context,
                                builder: (BuildContext context) {
                                  return FutureBuilder(
                                      // Pedimos a la web el documento por el "id".
                                      future: httpService.getDocument(
                                          snapshotFromList.data[index].id
                                              .toString()),
                                      builder: (BuildContext context,
                                          AsyncSnapshot snapshotFromDocument) {
                                        if (snapshotFromDocument.hasData) {
                                          return Dialog(
                                            backgroundColor: Colors.transparent,
                                            elevation: 0.0,
                                            child: Container(
                                              decoration: BoxDecoration(
                                                color: Colors.white,
                                                borderRadius:
                                                    BorderRadius.circular(30.0),
                                              ),
                                              child: Column(
                                                mainAxisAlignment:
                                                    MainAxisAlignment.center,
                                                mainAxisSize: MainAxisSize.min,
                                                children: <Widget>[
                                                  Padding(
                                                    padding:
                                                        const EdgeInsets.all(
                                                            15.0),
                                                    child: Text(
                                                      snapshotFromList
                                                          .data[index].titulo,
                                                      style: kSubTitleStyle
                                                          .copyWith(
                                                              color:
                                                                  Colors.black),
                                                    ),
                                                  ),
                                                  Padding(
                                                    padding:
                                                        const EdgeInsets.all(
                                                            15.0),
                                                    child: FlagChips(
                                                        documento:
                                                            snapshotFromDocument
                                                                .data),
                                                  ),
                                                  GestureDetector(
                                                    child: Text(
                                                      '- ${S.of(context).cerrarVentana} -',
                                                      style: kSubTitleStyle
                                                          .copyWith(
                                                              color:
                                                                  Colors.black),
                                                    ),
                                                    onTap: () {
                                                      Navigator.of(context)
                                                          .pop();
                                                    },
                                                  ),
                                                  const SizedBox(
                                                    height: 20.0,
                                                  ),
                                                ],
                                              ),
                                            ),
                                          );
                                        } else {
                                          return const Center(
                                            child: CircularProgressIndicator(
                                              color: Colors.white54,
                                            ),
                                          );
                                        }
                                      });
                                },
                              );
                            },
                          );
                        },
                      ),
                    ),
                  ),
                ),
              ],
            );
          } else {
            return const Center(
              child: CircularProgressIndicator(
                color: Colors.white54,
              ),
            );
          }
        },
      ),
    );
  }
}

CodePudding user response:

The issue is that your selectedYear variable is declared within the scope of the build function. Since this is your "state" of your stateful widget, you should be declaring this within the body of your _ListOfDocumentsState. Just move this variable outside of build to be with your httpService, listadoDocumentos, and myLocale variables.

class _ListOfDocumentsState extends State<ListOfDocuments> {
  ...(other function and variable declarations)

  List<bool> selectedYear = <bool>[
    true,
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false,
    false
  ];

  @override
  Widget build(BuildContext context) {
    // Screen size.
    double screenWidth = MediaQuery.of(context).size.width;
    double screenHeight = MediaQuery.of(context).size.height;
    // For ToggleButtons.
    List<Widget> years = <Widget>[
      Padding(
        padding: const EdgeInsets.all(4.0),
        child: Text(S.of(context).all),
      ),
      const Text("2005"),
      const Text("2006"),
      const Text("2007"),
      const Text("2008"),
      const Text("2009"),
      const Text("2010"),
      const Text("2011"),
      const Text("2012"),
      const Text("2013"),
    ];
   ...(and the rest of your build function)

With your current method, your loop updates the selectedYear list as you intend, but calling setState calls build again, which resets the selectedYear to the original state with the first element being true.

CodePudding user response:

You can define it on a Future method and call your Future method on you button.

Future<String> _reloadState() {
    setState(() {});
}

And also you can fetch your data from an API if you have then call setState function to reload your state.

  • Related