easy_expandable 0.1.0 copy "easy_expandable: ^0.1.0" to clipboard
easy_expandable: ^0.1.0 copied to clipboard

Wrap any widget so it smoothly expands and collapses when its header is tapped. Zero-boilerplate, controlled or uncontrolled, with lazy body building.

easy_expandable #

pub package license: MIT

Wrap any widget so it smoothly expands and collapses when its header is tapped. No controllers to wire up, no boilerplate — but full control when you want it.

  • 🎯 Wrap anything — the header and the body are your widgets.
  • 🪗 Smooth reveal — the body slides out from under a clip (no fade flicker).
  • 🎛️ Controlled or uncontrolled — let the widget own its state, or drive it yourself (great for accordions).
  • 💤 Lazy by default — a collapsed body is not built at all, unless you ask to keep it with maintainState.
  • Accessible — the header is exposed as an expandable button to screen readers.

Getting started #

dependencies:
  easy_expandable: ^0.1.0
import 'package:easy_expandable/easy_expandable.dart';

Usage #

Uncontrolled — the simplest case #

The widget remembers whether it is open. Just give it a starting value.

Expandable(
  initiallyExpanded: false,
  header: (context, isExpanded) => ListTile(
    title: const Text('Tap me'),
    trailing: Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
  ),
  child: const Padding(
    padding: EdgeInsets.all(16),
    child: Text('Anything can go here.'),
  ),
)

A standard header, without the boilerplate #

The header is always your widget — but for the common "title + chevron" case you can drop in ExpandableHeader instead of wiring the rotating icon yourself. Full custom headers stay exactly as above; this is purely opt-in.

Expandable(
  header: (context, isExpanded) => ExpandableHeader(
    leading: const Icon(Icons.folder_outlined),
    title: const Text('Section'),
    isExpanded: isExpanded, // drives the auto-rotating chevron
  ),
  child: const Text('Body'),
)

Controlled — you own the state #

Pass isExpanded and update it in onExpansionChanged. This is what lets you coordinate several tiles, persist state, or open one from a button elsewhere.

bool _open = false;

Expandable(
  isExpanded: _open,
  onExpansionChanged: (value) => setState(() => _open = value),
  header: (context, isExpanded) => const Text('Controlled header'),
  child: const Text('Body'),
)

In controlled mode the body only moves when the parent updates isExpanded. If you forget to call setState, the tap is reported but nothing opens.

With a controller — drive it programmatically #

Skip the bool and setState entirely: give it an ExpandableController and call expand(), collapse(), or toggle() from anywhere.

final controller = ExpandableController(); // dispose it in State.dispose()

Expandable(
  controller: controller,
  header: (context, isExpanded) => const Text('Controller-driven'),
  child: const Text('Body'),
)

// elsewhere:
controller.toggle();

Accordion — a group of tiles #

ExpandableGroup coordinates several entries. By default it is an accordion (one open at a time); pass allowMultiple: true to let several stay open.

ExpandableGroup(
  // controller: myGroupController,   // optional; for collapseAll()/expandAll()
  separatorBuilder: (context, i) => const Divider(height: 1),
  children: [
    for (final section in sections)
      ExpandableGroupChild(
        header: (context, isExpanded) => Text(section.title),
        body: Text(section.body),
      ),
  ],
)

Parameters #

Parameter Type Default Description
header Widget Function(context, bool isExpanded) required Always-visible, tappable header.
child Widget required Content revealed when expanded.
initiallyExpanded bool false Starting value (uncontrolled mode).
isExpanded bool? null Non-null switches to controlled mode.
onExpansionChanged ValueChanged<bool>? null Called with the requested state on tap.
duration Duration 200ms Animation duration.
curve Curve Curves.easeOut Animation curve.
maintainState bool false Keep the body built while collapsed.
crossAxisAlignment CrossAxisAlignment .start Column alignment.
revealAlignment Alignment .topCenter Where the body slides out from.

Additional information #

Issues and contributions are welcome on the issue tracker.

2
likes
160
points
52
downloads

Documentation

API reference

Publisher

verified publisherhotshotslabs.com

Weekly Downloads

Wrap any widget so it smoothly expands and collapses when its header is tapped. Zero-boilerplate, controlled or uncontrolled, with lazy body building.

Repository (GitHub)
View/report issues

Topics

#expandable #expansion #accordion #collapse #animation

License

MIT (license)

Dependencies

flutter

More

Packages that depend on easy_expandable