Hasan A Yousef
Hasan A Yousef

Reputation: 24908

How can I create Expanded panel

Here is a material design of Expanded panel that looks like:

enter image description here

I'd like to make a similar one with Flutter, not sure if I've to start with something like the below code or know, and how to complete it!

new ExpansionPanelList(
  children: <ExpansionPanel>[
    new ExpansionPanel(
      headerBuilder: (BuildContext context, bool isExpanded) {
            isExpanded = true;
            return new ListTile(
            // leading: item.iconpic,
            title: new Text(
            "First",
            textAlign: TextAlign.left,
            style: new TextStyle(
            fontSize: 20.0,
            fontWeight: FontWeight.w400,
            ),
            ));
          },
      body: new Text("school"),
      isExpanded: true,
    ),
    new ExpansionPanel(
      headerBuilder: (BuildContext context, bool isExpanded) {
        isExpanded = true;
        return new ListTile(
          // leading: item.iconpic,
            title: new Text(
              "Second",
              textAlign: TextAlign.left,
              style: new TextStyle(
                fontSize: 20.0,
                fontWeight: FontWeight.w400,
              ),
            ));
      },
      isExpanded: false,
      body: new Text("hospital"),
    ),
    new ExpansionPanel(
        headerBuilder: (BuildContext context, bool isExpanded) {
          isExpanded = true;
          return new ListTile(
            // leading: item.iconpic,
              title: new Text(
                "Third",
                textAlign: TextAlign.left,
                style: new TextStyle(
                  fontSize: 20.0,
                  fontWeight: FontWeight.w400,
                ),
              ));
        },
        body: new Text("va facility"),
        isExpanded: true)
    ]),

UPDATE

I just need to start and have the empty panels

enter image description here

Upvotes: 1

Views: 13882

Answers (3)

Shady Aziza
Shady Aziza

Reputation: 53307

In case if you particularly need to mimic the images you referenced from the material design. You would want to build your own custom expansion panel.

I have a simple example using AnimatedContainer to show you how to create the expanded and collapsed effects, and it is up to you to populate both the header and the body sections with what you want.

enter image description here

class AnimateExpanded extends StatefulWidget {
  @override
  _AnimateExpandedState createState() => new _AnimateExpandedState();
}

class _AnimateExpandedState extends State<AnimateExpanded> {
  double _bodyHeight = 0.0;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      backgroundColor: Colors.grey[500],
      body: new SingleChildScrollView(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Card(
              child: new Container(
                height: 50.0,
                child: new Row(
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: <Widget>[
                    new IconButton(
                      icon: new Icon(Icons.keyboard_arrow_down),
                      onPressed: () {
                        setState(() {
                          this._bodyHeight = 300.0;
                        });
                      },
                    )
                  ],
                ),
              ),
            ),
            new Card(
              child: new AnimatedContainer(
                child: new Row(
                  mainAxisAlignment: MainAxisAlignment.end,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    new IconButton(
                      icon: new Icon(Icons.keyboard_arrow_up),
                      onPressed: () {
                        setState(() {
                          this._bodyHeight = 0.0;
                        });
                      },
                    ),
                  ],
                ),
                curve: Curves.easeInOut,
                duration: const Duration(milliseconds: 500),
                height: _bodyHeight,
                // color: Colors.red,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 6

Muhammad Adil
Muhammad Adil

Reputation: 4678

You can use ExpansionTile inside ListView like this

        ListView(
              shrinkWrap: true,
              children: <Widget>[
                ExpansionTile(
                  backgroundColor: Colors.amber,
                  leading: Icon(Icons.event),
                  title: Text('Test1'),
                  children: <Widget>[
                    ListTile(title: Text('Title of the item')),
                    ListTile(
                      title: Text('Title of the item2'),
                    )
                  ],
                ),
                ExpansionTile(
                  title: Text('Test2'),
                  children: <Widget>[
                    ListTile(title: Text('Title of the item')),
                    ListTile(
                      title: Text('Title of the item2'),
                    )
                  ],
                )
              ],
            )

Upvotes: 3

rmtmckenzie
rmtmckenzie

Reputation: 40423

Here's a working example (including main etc so you can just paste into a file and run)

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class ListItem {
  final WidgetBuilder bodyBuilder;
  final String title;
  final String subtitle;
  bool isExpandedInitially;

  ListItem({
    @required this.bodyBuilder,
    @required this.title,
    this.subtitle = "",
    this.isExpandedInitially = false,
  })  : assert(title != null),
        assert(bodyBuilder != null);

  ExpansionPanelHeaderBuilder get headerBuilder =>
      (context, isExpanded) => new Row(children: [
            new SizedBox(width: 100.0, child: new Text(title)),
            new Text(subtitle)
          ]);
}

class ExpansionList extends StatefulWidget {
  /// The items that the expansion list should display; this can change
  /// over the course of the object but probably shouldn't as it won't
  /// transition nicely or anything like that.
  final List<ListItem> items;

  ExpansionList(this.items) {
    // quick check to make sure there's no duplicate titles.
    assert(new Set.from(items.map((li) => li.title)).length == items.length);
  }

  @override
  State<StatefulWidget> createState() => new ExpansionListState();
}

class ExpansionListState extends State<ExpansionList> {
  Map<String, bool> expandedByTitle = new Map();

  @override
  Widget build(BuildContext context) {
    return new ExpansionPanelList(
      children: widget.items
          .map(
            (item) => new ExpansionPanel(
                headerBuilder: item.headerBuilder,
                body: new Builder(builder: item.bodyBuilder),
                isExpanded:
                    expandedByTitle[item.title] ?? item.isExpandedInitially),
          )
          .toList(growable: false),
      expansionCallback: (int index, bool isExpanded) {
        setState(() {
          expandedByTitle[widget.items[index].title] = !isExpanded;
        });
      },
    );
  }
}

void main() => runApp(
      new MaterialApp(
        home: new SingleChildScrollView(
          child: new SafeArea(
            child: new Material(
              child: new ExpansionList(
                [
                  new ListItem(
                      title: "Title 1",
                      subtitle: "Subtitle 1",
                      bodyBuilder: (context) => new Text("Body 1")),
                  new ListItem(
                      title: "Title 2",
                      subtitle: "Subtitle 2",
                      bodyBuilder: (context) => new Text("Body 1"),
                      isExpandedInitially: true)
                ],
              ),
            ),
          ),
        ),
      ),
    );

If I had to guess, you're missing the parts where you pass in expanded into each expansion header, and the part where you keep track of whether each expansion header is expanded or not.

I've done it a particular way here that assumes each title is unique; you could do something similar but rely on different properties. Or you could build everything in the initState method of your ExpansionListState equivalent.

This is a full working example of pretty much the exact UI you have in the picture in your post. You can simply download the flutter gallery from the play store to see the result. They did it a different way than I did (building everything in the initState method), and it's more complicated than what I did, but would be worth understanding as well.

Hope that helps =)

Upvotes: 2

Related Questions