Using Provider in ExpansionPanelList

Viewed 255

I am trying to use provider with an ExpansionPanelList. The issue is that I would need a new instance of provider for each ExpansionPanel but the ExpansionPanelList needs the expansionCallBack function which controls the state of the expansion (bool isExpanded).

This is the sample code using setState and StatefulWidget, and I am trying to port this to provider.

class ExpansionPanelProvider {
  ExpansionPanelProvider({
    this.expandedValue,
    this.headerValue,
    this.isExpanded = false,
  });

  String expandedValue;
  String headerValue;
  bool isExpanded;

  /*void updateExpansionState() {
      isExpanded = !isExpanded;
      notifyListeners();
  }*/

}

List<ExpansionPanelProvider> generateItems(int numberOfItems) {
  return List.generate(numberOfItems, (int index) {
    return ExpansionPanelProvider(
      headerValue: 'Panel ${index + 1}',
      expandedValue: 'This is item number ${index + 1}',
    );
  });
}

/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({Key key}) : super(key: key);
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  List<ExpansionPanelProvider> _data = generateItems(80);
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Container(
        child: _buildPanel(),
      ),
    );
  }

  Widget _buildPanel() {
    return ExpansionPanelList(
      animationDuration: Duration(milliseconds: 800),
      expansionCallback: (int index, bool isExpanded) {
        setState(() {
          _data[index].isExpanded = !isExpanded;
        });
      },
      children: _data.map<ExpansionPanel>((ExpansionPanelProvider item) {
        return ExpansionPanel(
          canTapOnHeader: true,
          isExpanded: item.isExpanded,
          headerBuilder: (BuildContext context, bool isExpanded) {
            return ListTile(
              title: Text(item.headerValue),
            );
          },
          body: ListTile(
              title: Text(item.expandedValue),
              subtitle: Text('To delete this panel, tap the trash can icon'),
              trailing: Icon(Icons.delete),
              onTap: () {
                setState(() {
                  _data.removeWhere((currentItem) => item == currentItem);
                });
              }),
        );
      }).toList(),
    );
  }
}

This code uses set strings and data which is to be displayed. My data comes from another provider.

[EDIT]

I tried wrapping ExpansionPanel in a ChangeNotifierProvider, but that didn't work because ExpansionPanel can be a child of only ExpansionPanelList Widget.

2 Answers

I faced the same problem as you though we code the difference. I solved this by ADDING extends ChangeNotifier to a ExpansionPanelItem class. Like which did as Flutter documentation, Iterate the panel-item-class and wrap a ChangeNotifierProvider.value(...) around both headerBulider and body attributes in ExpansionPanel.

ExpansionPanel(
      canTapOnHeader: true,
      headerBuilder: (ctx, bool) => ChangeNotifierProvider.value(
        value: item,
        child: _buildPanelHeader(item, index),
      ),
      body: ChangeNotifierProvider.value(
        value: item,
        child: item.body,
      ),
      isExpanded: item.isExpanded,
    )

and then wrap Consumer around where you build your body and header like below:

// ...
Widget _buildPanelHeader(ExpansionPanelItem item, int index) {
return Consumer<ExpansionPanelItem>(
  builder: (ctx, itemRef, c) {
    return Container(
      // your header
    );
  }
);

}

You can use ChangeNotifierProvider on both the headerBuilder and body of the ExpansionPanel. That way you will still have the ExpansionPanel as the return type.

Be very careful with how much customization your widget will need. The trailing arrow from a ExpansionPanel can't be adjusted in the current version. I've swapped the ExpansionPanel to a ExpansionTile eventually.

Related