flutterlistviewexpansion

flutter expansion panel listView Builder


a beginner in a field, I have this code that works, but there is an error in the case of clicking When I click on any card, all cards also open. I want to open a card that I just click on

import 'dart:io';
import 'package:english_club/data/im.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';


class Novals extends StatefulWidget {
  @override
  _NovalsState createState()
  {
    return _NovalsState();
  }
}

class _NovalsState extends State<Novals> {
  bool _expanded = false;
  var _test = "Full Screen";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
       resizeToAvoidBottomInset: false,
      body: ListView.builder(
        itemCount: storys.length,

        itemBuilder: (context, index){
          return SingleChildScrollView(
            child: Column(
              children: [
                Center(
                  child: Container(
                    margin: EdgeInsets.all(10),
                    color: Colors.green,
                    child: ExpansionPanelList(
                    animationDuration: Duration(milliseconds: 2000),
                    children: [
                      ExpansionPanel(
                          headerBuilder: (context, isExpanded) {
                            return ListTile(
                              title: Text(storys[index]["name"], style: TextStyle(color: Colors.black),),
                            );
                          },
                          body:ListTile(
                            title: Text(storys[index]["lines"],style: TextStyle(color: Colors.black)),
                          ),
                        isExpanded: _expanded,
                        canTapOnHeader: true,
                      ),
                    ],
                    dividerColor: Colors.grey,
                    expansionCallback: (panelIndex, isExpanded) {
                      _expanded = !_expanded;
                      setState(() {

                      });
                    },

              ),
             ),
                ),
            ]
            ),
          );
        }
      ),
    );
  }
}

I tried to solve it, but I could not aim for the code to display the name of the story, and when the user clicks on it, the whole story appears


Solution

  • Firstly, you should remove the SingleChildScrollView as the ListView child. ListView.builder is enough to provide scrolling to your Scaffold.

    For the main issue, you should split each item to a separate Widget with its own _expanded state. This will make your UI much faster, the widgets will expand separately and the whole screen won't have to reload after each item's setState().

    Example code:

    class Novals extends StatefulWidget {
      @override
      _NovalsState createState()
      {
        return _NovalsState();
      }
    }
    
    class _NovalsState extends State<Novals> {
      var _test = "Full Screen";
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          resizeToAvoidBottomInset: false,
          body: ListView.builder(
              itemCount: storys.length,
              itemBuilder: (context, index){
                return StoryWidget(story: storys[index]);
              }
          ),
        );
      }
    }
    
    class StoryWidget extends StatefulWidget {
      final Map<String, dynamic> story;
      const StoryWidget({Key? key, required this.story}) : super(key: key);
    
      @override
      _StoryWidgetState createState() => _StoryWidgetState();
    }
    
    class _StoryWidgetState extends State<StoryWidget> {  
    bool _expanded = false;
    
    @override
      Widget build(BuildContext context) {
        return Column(
            children: [
              Center(
                child: Container(
                  margin: EdgeInsets.all(10),
                  color: Colors.green,
                  child: ExpansionPanelList(
                    animationDuration: Duration(milliseconds: 2000),
                    children: [
                      ExpansionPanel(
                        headerBuilder: (context, isExpanded) {
                          return ListTile(
                            title: Text(widget.story["name"], style: TextStyle(color: Colors.black),),
                          );
                        },
                        body:ListTile(
                          title: Text(widget.story["lines"],style: TextStyle(color: Colors.black)),
                        ),
                        isExpanded: _expanded,
                        canTapOnHeader: true,
                      ),
                    ],
                    dividerColor: Colors.grey,
                    expansionCallback: (panelIndex, isExpanded) {
                      _expanded = !_expanded;
                      setState(() {});
                    },
                  ),
                ),
              ),
            ]
        );
      }
    }
    

    P/s: It's a good practice to put your story into a model class using jsonDecode() instead of using Map directly. It would help you avoid bugs and better maintenance in the long term