Home > Software engineering >  loop generated ListView with ListTile inside ExpansionTile
loop generated ListView with ListTile inside ExpansionTile

Time:01-24

May i ask, how may I achieve this: Intended

I tried many a times, but failed,

List<String> list = [
  "Topic 1",
  " Sub Topic 1",
  " Sub Topic 2",
  " Sub Topic 3",
  " Sub Topic 4",
  "Topic 2",
  " Sub Topic 1",
  " Sub Topic 2",
  "Topic 3",
  " Sub Topic 1",
  " Sub Topic 2",
  " Sub Topic 3",
];

Thanking you...

CodePudding user response:

You need to represent your data in a nested way. Currently your list does not represent that and it'll not be easy to create the widget in mind with the given list.

For instance, you can create a data object such as:

class Topic {
  final String text;
  final List<Topic> subTopics = [];
  Topic({required this.text, List<Topic> subTopics = const []}) {
    this.subTopics.addAll(subTopics);
  }
}

Then create a list of Topics such as:


final topics = <Topic>[
  // topic 1
  Topic(
    text: 'Topic 1',
    subTopics: [
      Topic(text: 'Sub Topic 1'),
      Topic(
        text: 'Sub Topic 2',
        subTopics: [
          Topic(
            text: 'sub sub topic 1',
          ),
        ],
      ),
      Topic(text: 'Sub Topic 3'),
    ],
  ),
  // topic 2
  Topic(
    text: 'Topic 2',
    subTopics: [
      Topic(
        text: 'Sub Topic 1',
      ),
      Topic(
        text: 'Sub Topic 2',
      ),
    ],
  ),
  // topic 3
  Topic(
    text: 'Topic 3',
  )
];

Finally in your widget, you can build it like this:

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

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: topics.length,
      itemBuilder: (context, index) {
        return TopicWidget(topic: topics[index]);
      },
    );
  }
}

class TopicWidget extends StatelessWidget {
  final Topic topic;
  const TopicWidget({Key? key, required this.topic}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
      title: Text(topic.text),
      children: List.generate(topic.subTopics.length, (index) => TopicWidget(topic: topic.subTopics[index])),
    );
  }
}

The result is:

enter image description here

Here is a fully runnable example that you can view on DartPad: https://dartpad.dev/?id=6802f0f362f0c408b149715ce3e8590b

CodePudding user response:

Update

For simple case you do loop, as you commented to use ListView.

 ListView(
  children: [
    ...List.generate(
      list.length,
      (index) => Column(
        children: [
          if (index != 0 &&
              index != list.length - 1 &&
              !list[index].contains("Sub"))
            Divider(),
          ListTile(
            title: Text(list[index]),
            trailing: list[index].contains("Sub")
                ? null
                : Icon(Icons.arrow_drop_down),
          ),
        ],
      ),
    )
  ],
)

For this case I like to modify the data structure using Map.

  final Map<String, List<String?>> data = {
    "Topic 1": [
      " Sub Topic 1",
    ],
    " Sub Topic 2": [
      "  SubSub Topic 1",
    ],
    "Topic 2": [
      " Sub Topic 1",
      " Sub Topic 2",
    ],
    "Topic 3": []
  };

For the UI, I am using ExpansionTile

...data.keys.map(
  (k) => ExpansionTile(
    expandedAlignment: Alignment.centerLeft,
    title: Text(k),
    children: data[k] == null
        ? [
            const SizedBox(),
          ]
        : data[k]!
            .map(
              (e) => Text(e ?? ""),
            )
            .toList(),
  ),
)
  •  Tags:  
  • Related