Dynamic ExpansionPanelList

Issue

i’m beginner with flutter and i try to create Dynamic ExpansionPanelList using API. Everything it’s ok and work but when i click in icon to expanded the children container. After 3s the container closed because he reload the api and I don’t know why he make that.

This is my full code if someone can help me please

class ExpansionList extends StatefulWidget {
  const ExpansionList({Key? key}) : super(key: key);

  @override
  _ExpansionListState createState() => _ExpansionListState();
}

class _ExpansionListState extends State<ExpansionList> {
  Widget build(BuildContext context) {
    return FutureBuilder<List<Job>>(
      future: _fetchJobs(),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {
          List<Job>? data = snapshot.data;
          return Column(
            children: [
                _jobsListView(data, context),
            ],
          );
        } else if (snapshot.hasError) {
          return Text("${snapshot.error}");
        }
        return CircularProgressIndicator(

        );
      },
    );
  }

  Widget _jobsListView(data, context) {
    return ExpansionPanelList(
      expansionCallback: (int index, bool isExpanded) {
        setState(() {
          data[index].isExpanded = !isExpanded;
        });
      },
      children: data.map<ExpansionPanel>((Job item) {
        return ExpansionPanel(
            headerBuilder: (BuildContext context, bool isExpanded) {
              return ListTile(
                title: Text(item.company,
                    style:
                        TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
              );
            },
            body: Container(
              child: Column(
                children: [
                  _tile('CLASSE', item.company, item.description, Icons.work),
                  _tile('MOTIF', item.company, item.description, Icons.work),
                  _tile('SANCTION', item.company, item.description, Icons.work),
                  GestureDetector(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => ShowArticle(item: item),
                          ),
                        );
                      },
                      child: Container(
                        alignment: Alignment.topRight,
                        child: Text('En savoir plus',
                            style: TextStyle(color: Colors.blue)),
                        padding: const EdgeInsets.all(20),
                      ))
                ],
              ),
            ),
            isExpanded: item.isExpanded,
        );
      }).toList(),
    );
  }
}

Future<List<Job>> _fetchJobs() async {
  final response = await http.get(Uri.parse(jobsListAPIUrl));

  if (response.statusCode == 200) {
    List jsonResponse = json.decode(response.body);
    return jsonResponse.map((job) => new Job.fromJson(job)).toList();
  } else {
    throw Exception('Failed to load jobs from API');
  }
}

ListTile _tile(
        String title, String subtitle, String description, IconData icon) =>
    ListTile(
      title: Text(title,
          style: const TextStyle(
            fontWeight: FontWeight.w500,
            fontSize: 20,
          )),
      subtitle: Text(subtitle),
      leading: Icon(
        icon,
        color: Colors.blue[500],
      ),
    );


Thanks for your answered

Solution

The code below should solve your problem. I created a custom state for ExpansionList. This way the API will not be reloaded.

class ExpansionList extends StatelessWidget {
  const ExpansionList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<Job>>(
      future: _fetchJobs(),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {
          List<Job>? data = snapshot.data;
          return Column(
            children: [
              ExpansionPaneListState(data: data),
            ],
          );
        } else if (snapshot.hasError) {
          return Text("${snapshot.error}");
        }
        return const CircularProgressIndicator();
      },
    );
  }

  Future<List<Job>> _fetchJobs() async {
    final response = await http.get(Uri.parse(jobsListAPIUrl));

    if (response.statusCode == 200) {
      List jsonResponse = json.decode(response.body);
      return jsonResponse.map((job) => new Job.fromJson(job)).toList();
    } else {
      throw Exception('Failed to load jobs from API');
    }
  }
}
class ExpansionPaneListState extends StatefulWidget {
  const ExpansionPaneListState({Key? key, this.data}) : super(key: key);

  final List<Job>? data;

  @override
  _ExpansionPaneListStateState createState() => _ExpansionPaneListStateState();
}
class _ExpansionPaneListStateState extends State<ExpansionPaneListState> {
  @override
  Widget build(BuildContext context) {
    return ExpansionPanelList(
      expansionCallback: (int index, bool isExpanded) {
        setState(() {
          data[index].isExpanded = !isExpanded;
        });
      },
      children: data.map<ExpansionPanel>((Job item) {
        return ExpansionPanel(
          headerBuilder: (BuildContext context, bool isExpanded) {
            return ListTile(
              title: Text(item.company, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
            );
          },
          body: Container(
            child: Column(
              children: [
                _tile('CLASSE', item.company, item.description, Icons.work),
                _tile('MOTIF', item.company, item.description, Icons.work),
                _tile('SANCTION', item.company, item.description, Icons.work),
                GestureDetector(
                    onTap: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => ShowArticle(item: item),
                        ),
                      );
                    },
                    child: Container(
                      alignment: Alignment.topRight,
                      child: Text('En savoir plus', style: TextStyle(color: Colors.blue)),
                      padding: const EdgeInsets.all(20),
                    ))
              ],
            ),
          ),
          isExpanded: item.isExpanded,
        );
      }).toList(),
    );
  }
  
  ListTile _tile(String title, String subtitle, String description, IconData icon) {
    return ListTile(
      title: Text(title,
          style: const TextStyle(
            fontWeight: FontWeight.w500,
            fontSize: 20,
          )),
      subtitle: Text(subtitle),
      leading: Icon(
        icon,
        color: Colors.blue[500],
      ),
    );
  }
}

Answered By – Salih Can

Answer Checked By – Timothy Miller (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.