Newby Coder header banner

Flutter Expandable List

An expandable list responds to user gesture by expanding and shrinking its size, typically to show and hide a sublist

expandable package can be used to implement an expandable list

Each list item and its functionality to expand to show its sublist can be implemented using a ExpandablePanel component wrapped inside a ScrollOnExpand component so that the app can get scrolled when item is pressed

The outer list containing the expandable items can be added as a child of ExpandableTheme widget

Creating new Flutter App

Check Flutter installation to setup Flutter

Use flutter create command to create a Flutter project (here expandable_list_app :

flutter create expandable_list_app

Dependency

App Code

expandable_list_app/lib/main.dart
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Expandable List App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ExpandableListPage(),
    );
  }
}

class ExpandableListPage extends StatelessWidget {
  var array = [
    {
      'expanded': false, 'category_Name': "Mobiles", 'sub_Category': [{ 'id': 1, 'name': 'Mi' }, { 'id': 2, 'name': 'RealMe' }, { 'id': 3, 'name': 'Samsung' },
      { 'id': 4, 'name': 'Infinix' }, { 'id': 5, 'name': 'Oppo' }, { 'id': 6, 'name': 'Apple' }, { 'id': 7, 'name': 'Honor' }]
    },
    {
      'expanded': false, 'category_Name': "Laptops", 'sub_Category': [{ 'id': 8, 'name': 'Dell' }, { 'id': 9, 'name': 'MAC' }, { 'id': 10, 'name': 'HP' },
      { 'id': 11, 'name': 'ASUS' }]
    },
    {
      'expanded': false, 'category_Name': "Computer Accessories", 'sub_Category': [{ 'id': 12, 'name': 'Pendrive' }, { 'id': 13, 'name': 'Bag' },
      { 'id': 14, 'name': 'Mouse' }, { 'id': 15, 'name': 'Keyboard' }]
    },
    {
      'expanded': false, 'category_Name': "Home Entertainment", 'sub_Category': [{ 'id': 16, 'name': 'Home Audio Speakers' },
      { 'id': 17, 'name': 'Home Theatres' }, { 'id': 18, 'name': 'Bluetooth Speakers' }, { 'id': 19, 'name': 'DTH Set Top Box' }]
    },
    {
      'expanded': false, 'category_Name': "TVs by brand", 'sub_Category': [{ 'id': 20, 'name': 'Mi' },
      { 'id': 21, 'name': 'Thomson' }, { 'id': 22, 'name': 'LG' }, { 'id': 23, 'name': 'SONY' }]
    },
    {
      'expanded': false, 'category_Name': "Kitchen Appliances", 'sub_Category': [{ 'id': 24, 'name': 'Microwave Ovens' },
      { 'id': 25, 'name': 'Oven Toaster Grills (OTG)' }, { 'id': 26, 'name': 'Juicer/Mixer/Grinder' }, { 'id': 27, 'name': 'Electric Kettle' }]
    }
  ];
  ListView generateItems() {
    return ListView.separated(
      shrinkWrap:true,
      itemCount: array.length,
      itemBuilder: (BuildContext context, int index) {
        return ExpandableWidget(array[index]);
      },
      separatorBuilder: (BuildContext context, int index) => const Divider(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Expandable List Example"),
      ),
      body: ExpandableTheme(
        data: ExpandableThemeData(iconColor: Colors.blue, useInkWell: false),
        child: generateItems(),
      ),
    );
  }
}

class ExpandableWidget extends StatelessWidget {
  var subcategoryList;
  var category;

  ExpandableWidget(entry){
    category = entry['category_Name'];
    subcategoryList = entry['sub_Category'];
  }

  @override
  Widget build(BuildContext context) {

    buildHeader() {
      return Builder(
        builder: (context) {
          var controller = ExpandableController.of(context);
          return Container(
            width:double.infinity,
            height: 50.0,
            alignment: Alignment.center,
            child: Stack(
              children:[
                Container(
                  width:double.infinity,
                  alignment: Alignment.centerLeft,
                  child : Text(category,
                    style: Theme.of(context).textTheme.button.copyWith(
                      color: Colors.deepPurple
                    ),
                  ),
                ),
                Container(
                  width:double.infinity,
                  height:double.infinity,
                  child: FlatButton(
                    onPressed: () {
                      controller.toggle();
                    },
                  ),
                ),
              ]
            ),
          );
        },
      );
    }

    buildExpanded() {
      return ListView.separated(
        shrinkWrap:true,
        physics: const NeverScrollableScrollPhysics(),
        itemCount: subcategoryList.length,
        itemBuilder: (BuildContext context, int index) {
          var subcategoryName = subcategoryList[index]['name'];
          return Container(
            height: 30,
            child: Padding(
              padding: const EdgeInsets.only(left: 10,),
              child: Text('${subcategoryName}')
            ),
          );
        },
        separatorBuilder: (BuildContext context, int index) => const Divider(),
      );
    }

    return ExpandableNotifier(
      child: Container(
        color: Colors.amber[600],
        padding: const EdgeInsets.only(left: 10.0, right: 0, bottom: 0),
        child: ScrollOnExpand(
            child:
              ExpandablePanel(
                header: buildHeader(),
                expanded: buildExpanded(),
              ),

        ),
      )
    );
  }
}

Run instructions

Ensure a supported device is connected or emulator/simulator is started

Go to project directory

Use flutter run command to run

flutter run

It builds and runs app on an available android/ios device


Screenshot/image

Android

cl-flutter-expandable-list

iOS

cm-flutter-expandable-list-as1cm-flutter-expandable-list-as2cm-flutter-expandable-list-as3