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.