flutterflutter-layoutpanelexpansionflutter-ios

Flutter Conversion - UICollectionViewDiffableDataSource


Im trying to convert my existing iOS Application into Flutter.

In iOS I'm using UICollectionViewDiffableDataSource to view a structure like that:

struct ClassItem: Hashable{
 let year: String
 let students: [StudentItem]
}

let BiologyClass = [
 ClassItem(year: "2021", students: [
  StudentItem(name: "Michael Pearson", favColor: "blue"),
  StudentItem(name: "Pearson Michael", favColor: "green")
 ]),
 ClassItem(year: "2020", students: [
  StudentItem(name: "Steve Pearson", favColor: "blue"),
  StudentItem(name: "Steve Michael", favColor: "green"),
  StudentItem(name: "Pearson Steve", favColor: "red")
 ]),

That looks like this:

iosView

In Flutter I try to work with an ExpansionPanel:

  Widget build(BuildContext context) {
    return ExpansionPanelList(
            children: [
              ExpansionPanel(
                headerBuilder: (context, isExpanded) {
                  return ListTile(
                    title: Text(
                      '2021',
                      style: TextStyle(color: Colors.black),
                    ),
                  );
                },
                body: Column(
                  children: [
                    ListTile(
                      title: Text('Description text',
                          style: TextStyle(color: Colors.black)),
                    ),
                    ListTile(
                      title: Text('Description text2',
                          style: TextStyle(color: Colors.black)),
                    ),
                  ],
                ),
                isExpanded: _expanded,
                canTapOnHeader: true,
              ),
            ],
            dividerColor: Colors.grey,
            expansionCallback: (panelIndex, isExpanded) {
              _expanded = !_expanded;
              setState(() {});
            },
      
    ); 

So the question is, how is it possible to create a variable number of Tiles within the ExpansionPanel. Or better, how is it possible to build something like shown with using UICollectionViewDiffableDataSource in iOS env. Currently I would have to create all tiles by hand and that is just not feasible in the final version. Flutter looks like this currently:

FlutterView

Thanks.

EDIT: There is an error "Duplicate Keys Found". The following image is showing the debugging window. I don't know why the entry is doubled. Debug

EDIT2: Got It, there was a line commented out in the CustomExpansionTile!


Solution

  • Output:

    enter image description here

    Create Data Models similar to Swift structs and datasource as BiologyClass:

    class ClassItem {
      ClassItem({this.isExpanded: false, this.year, this.students});
    
      bool isExpanded;
      final String year;
      final List<StudentItem> students;
    }
    
    class StudentItem {
      StudentItem({this.name, this.favColor});
    
      final String name;
      final String favColor;
    }
    

    Now you can iterate through the items and populate the ExpansionPanelList with dynamic data:

    class _MyHomePageState extends State<MyHomePage> {
      List<ClassItem> _items = <ClassItem>[
        ClassItem(year: "2021", students: [
          StudentItem(favColor: "blue", name: "Michael Pearson"),
          StudentItem(favColor: "green", name: "Pearson Michael")
        ]),
        ClassItem(year: "2020", students: [
          StudentItem(favColor: "blue", name: "Steve Pearson"),
          StudentItem(favColor: "green", name: "Steve Michael"),
          StudentItem(favColor: "red", name: "Pearson Steve")
        ]),
      ];
    
      @override
      Widget build(BuildContext context) {
        return ListView(
          children: <Widget>[
            ExpansionPanelList(
              expansionCallback: (int index, bool isExpanded) {
                setState(() {
                  _items[index].isExpanded = !_items[index].isExpanded;
                });
              },
              children: _items.map((ClassItem item) {
                return ExpansionPanel(
                  headerBuilder: (BuildContext context, bool isExpanded) {
                    return Text(item.year);
                  },
                  isExpanded: item.isExpanded,
                  body: Column(children: [
                    ...item.students.map((StudentItem student) {
                      return GestureDetector(
                        onTap: () {
                          print(student.name);
                        },
                        child: Container(child: Padding(padding: const EdgeInsets.all(8.0), child: Text(student.name))),
                      );
                    }).toList(),
                  ]),
                );
              }).toList(),
            )
          ],
        );
      }
    }