I'fetching data from my API and want to display the data in a ExpansionPanelList. Now I have the problem, that every time when i want to fold out a Panel it closes immediately. Thanks for helping, here my Code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:http/http.dart' as http;
class Job {
final String vorname;
final String nachname;
bool isExpanded;
Job({
required this.vorname,
required this.nachname,
required this.isExpanded,
});
factory Job.fromJson(Map<String, dynamic> json) {
return Job(
vorname: json['vorname'],
nachname: json['nachname'],
isExpanded: false,
);
}
}
Future<List<Job>> fetchJobs() async {
final response = await http.get(Uri.parse('http://10.0.2.2:8000/api/jobs'));
if(response.statusCode == 200){
List jsonResponse = json.decode(response.body);
return jsonResponse.map((data) => Job.fromJson(data)).toList();
} else {
throw Exception('failed to load job');
}
}
class AuftraegePage extends StatefulWidget {
@override
_AuftraegePage createState() => _AuftraegePage();
}
class _AuftraegePage extends State<AuftraegePage>{
String selectedDay = DateFormat('dd.MM.yyyy').format(DateTime.now());
final List<Map<String, dynamic>> _items = List.generate(
10,
(index) => {
'id': index,
'title': 'Max Mustermann',
'description':
'This is the description of the item $index. Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
'isExpanded': false,
});
void _showDatePicker(){
showDatePicker(
locale: const Locale('de', 'DE'),
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1970),
lastDate: DateTime(2050),
builder: (context, child) {
return Theme(
data: ThemeData.light().copyWith(
colorScheme: ColorScheme.light(
primary: Color(0xff1f6526),
onPrimary: Colors.white,
onSurface: Colors.black,
),
dialogBackgroundColor:Colors.white,
),
child: child!,
);
},
).then((value) {
setState(() {
String _dateTime = DateFormat('dd.MM.yyy', 'de_DE').format(value!);
selectedDay = _dateTime;
});
} );
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey[300],
child: SingleChildScrollView(
padding: EdgeInsets.only(left: 16.0, right: 16.0, top: 4.0),
physics: AlwaysScrollableScrollPhysics(),
child: Column(
children: <Widget> [
const SizedBox(height: 20),
const Text('Aufträge',
style: TextStyle(
fontSize: 35,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
buildDatePicker(),
const SizedBox(height: 20),
buildCreateButton(),
const SizedBox(height: 20),
buildListOfJobs(),
],
),
),
);
}
Widget buildListOfJobs(){
return FutureBuilder<List<Job>>(
future: fetchJobs(),
builder: (context, snapshot) {
if(snapshot.hasData){
return ExpansionPanelList(
dividerColor: Colors.grey[700],
elevation: 3,
// Controlling the expansion behavior
expansionCallback: (index, isExpanded) {
setState(() {
snapshot.data![index].isExpanded = !isExpanded;
print(snapshot.data![index].isExpanded);
});
},
animationDuration: Duration(milliseconds: 600),
children: snapshot.data!.map(
(item) => ExpansionPanel(
canTapOnHeader: true,
backgroundColor:
item.isExpanded == true ? Colors.grey[300] : Colors.grey[300],
headerBuilder: (context, isExpanded){
return Container(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.vorname,
style: TextStyle(
fontSize: 18.0,
),
),
SizedBox(height: 5),
Wrap(
children: <Widget>[
Icon(
Icons.access_time_filled,
size: 22.0,
),
SizedBox(
width:5,
),
Text('07:00 - 07:13',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15
),)
],
),
],
)
);
},
body: Container(
color: Colors.grey[300],
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 30),
child: Column(
children: [
Row(
children: [
Expanded(
flex: 5,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.user,
size: 19,),
SizedBox(
width:5,
),
Text('Mitglied: OGV Imst ',
style: TextStyle(
fontSize: 15,
),),
],
),
),
Expanded(
flex: 4,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.locationDot,
size: 19),
SizedBox(
width:5,
),
Text('Ort: Imst',
style: TextStyle(
fontSize: 15,
)),
],
),
),
],
),
SizedBox(height: 10),
Row(
children: [
Expanded(
flex: 5,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.weightHanging,
size: 19,),
SizedBox(
width:5,
),
Text('Menge: 300 ',
style: TextStyle(
fontSize: 15,
),),
],
),
),
Expanded(
flex: 4,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.wineBottle,
size: 19,),
SizedBox(
width:5,
),
Text('Most: 0',
style: TextStyle(
fontSize: 15,
)),
],
),
),
],
),
SizedBox(height: 10),
Row(
children: [
Expanded(
flex: 5,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.bottleDroplet,
size: 19,),
SizedBox(
width:5,
),
Text('Flaschen: 0',
style: TextStyle(
fontSize: 15,
),),
],
),
),
Expanded(
flex: 4,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.bagShopping,
size: 19,),
SizedBox(
width:5,
),
Text('Bag: 0',
style: TextStyle(
fontSize: 15,
)),
],
),
),
],
),
SizedBox(height: 10),
Row(
children: [
Expanded(
flex: 5,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.box,
size: 19,),
SizedBox(
width:5,
),
Text('Karton: 0',
style: TextStyle(
fontSize: 15,
),),
],
),
),
],
),
SizedBox(height: 10),
Divider(color: Colors.black,),
SizedBox(height: 10),
Row(
children: [
Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.message,
size: 19,),
SizedBox(
width:5,
),
Text('Anmerkungen: ',
style: TextStyle(
fontSize: 15,
),),
],
),
],
),
SizedBox(height: 10),
Divider(color: Colors.black,),
SizedBox(height: 10),
Row(
children: [
Expanded(
flex: 5,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.tasks,
size: 19,),
SizedBox(
width:5,
),
Text('Bestätigt: Nein',
style: TextStyle(
fontSize: 15,
),),
],
),
),
Expanded(
flex: 4,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.xmark,
size: 19,),
SizedBox(
width:5,
),
Text('Stoniert: Nein',
style: TextStyle(
fontSize: 15,
)),
],
),
),
],
),
SizedBox(height: 10),
Row(
children: [
Expanded(
flex: 5,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.eye,
size: 19,),
SizedBox(
width:5,
),
Text('Noshow: Nein',
style: TextStyle(
fontSize: 15,
),),
],
),
),
Expanded(
flex: 4,
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.check,
size: 19,),
SizedBox(
width:5,
),
Text('Erledigt: Nein',
style: TextStyle(
fontSize: 15,
)),
],
),
),
],
),
SizedBox(height: 20),
Row(
children: [
Expanded(
flex: 5,
child: ElevatedButton(
onPressed:_showDatePicker,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
elevation: 5,
padding: EdgeInsets.all(13),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
),
child: Wrap(
children: const <Widget>[
Icon(FontAwesomeIcons.edit,
color: Colors.white,
size: 20.0,
),
SizedBox(
width:5,
),
Text(
'Bearbeiten',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
],
),
),
),
SizedBox(width: 10),
Expanded(
flex: 5,
child: ElevatedButton(
onPressed:_showDatePicker,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.yellow[600],
elevation: 5,
padding: EdgeInsets.all(13),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
),
child: Wrap(
children: <Widget>[
Icon(FontAwesomeIcons.file,
color: Colors.black,
size: 20.0,
),
SizedBox(
width:5,
),
Text(
'Rechnung',
style: TextStyle(
color: Colors.black,
fontSize: 16.0,
),
),
],
),
),
),
],
),
],
),
),
isExpanded: item.isExpanded,
),
).toList(),
);
} else if(snapshot.hasError) {
return Text('${snapshot.error}');
//return Text('keine Daten gefunden');
}
return const CircularProgressIndicator();
},
);
}
Widget buildDatePicker(){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Datum wählen: ',
style: TextStyle(
fontSize: 20,
),
),
ElevatedButton(
onPressed:_showDatePicker,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[700],
elevation: 5,
padding: EdgeInsets.all(13),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
),
child: Wrap(
children: <Widget>[
Icon(
Icons.date_range,
color: Colors.white,
size: 20.0,
),
SizedBox(
width:5,
),
Text(
selectedDay,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
],
),
),
],
);
}
Widget buildCreateButton(){
return ElevatedButton(
onPressed:_showDatePicker,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[700],
minimumSize: const Size.fromHeight(50),
elevation: 5,
padding: EdgeInsets.only(left: 35, right: 20, top: 13, bottom: 13),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
),
child: Wrap(
children: <Widget>[
Text(
'Auftrag erstellen',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
SizedBox(
width:5,
),
Icon(
Icons.add_card ,
color: Colors.white,
size: 20.0,
),
],
),
);
}
}
I think there could be an issue during fetching the data, because I think with every click my data get reloaded and "isExpanded" is set to *false * again.
You can replace future: fetchJobs()
with by creating a state future variable. It will prevent unwanted api call.
late final jobListFuture = fetchJobs();
@override
Widget build(BuildContext context) {
Widget buildListOfJobs(){
return FutureBuilder<List<Job>>(
future: jobListFuture,