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,
),
);
}
},
),
);
}
}
2
Answers
You can define it on a
Future
method and call yourFuture
method on youbutton
.And also you can fetch your data from an
API
if you have then callsetState
function to reload your state.The issue is that your
selectedYear
variable is declared within the scope of thebuild
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 ofbuild
to be with yourhttpService
,listadoDocumentos
, andmyLocale
variables.With your current method, your loop updates the
selectedYear
list as you intend, but callingsetState
callsbuild
again, which resets theselectedYear
to the original state with the first element beingtrue
.