ExpansionTileController typedef

  1. @Deprecated('Use ExpansibleController instead. ' 'This feature was deprecated after v3.31.0-0.1.pre.')
ExpansionTileController = ExpansibleController

Enables control over a single ExpansionTile's expanded/collapsed state.

It can be useful to expand or collapse an ExpansionTile programmatically, for example to reconfigure an existing expansion tile based on a system event. To do so, create an ExpansionTile with an ExpansionTileController that's owned by a stateful widget or look up the tile's automatically created ExpansionTileController with ExpansibleController.of.

Typical usage of the ExpansibleController.of function is to call it from within the build method of a descendant of an ExpansionTile.

When the ExpansionTile is actually created in the same build function as the callback that refers to the controller, then the context argument to the build function can't be used to find the ExpansionTileController (since it's "above" the widget being returned in the widget tree). In cases like that you can add a Builder widget, which provides a new scope with a BuildContext that is "under" the ExpansionTile:

To see it in action, copy and run this code snippet on DartPad.

import 'package:material_ui/material_ui.dart';

/// Flutter code sample for [ExpansionTile] and [ExpansibleController].

void main() {
  runApp(const ExpansionTileControllerApp());
}

class ExpansionTileControllerApp extends StatefulWidget {
  const ExpansionTileControllerApp({super.key});

  @override
  State<ExpansionTileControllerApp> createState() =>
      _ExpansionTileControllerAppState();
}

class _ExpansionTileControllerAppState
    extends State<ExpansionTileControllerApp> {
  final ExpansibleController controller = ExpansibleController();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ExpansionTileController Sample')),
        body: Column(
          children: <Widget>[
            // A controller has been provided to the ExpansionTile because it's
            // going to be accessed from a component that is not within the
            // tile's BuildContext.
            ExpansionTile(
              controller: controller,
              title: const Text('ExpansionTile with explicit controller.'),
              children: <Widget>[
                Container(
                  alignment: .center,
                  padding: const .all(24),
                  child: const Text('ExpansionTile Contents'),
                ),
              ],
            ),
            const SizedBox(height: 8),
            ElevatedButton(
              child: const Text('Expand/Collapse the Tile Above'),
              onPressed: () {
                if (controller.isExpanded) {
                  controller.collapse();
                } else {
                  controller.expand();
                }
              },
            ),
            const SizedBox(height: 48),
            // A controller has not been provided to the ExpansionTile because
            // the automatically created one can be retrieved via the tile's BuildContext.
            ExpansionTile(
              title: const Text('ExpansionTile with implicit controller.'),
              children: <Widget>[
                Builder(
                  builder: (BuildContext context) {
                    return Container(
                      padding: const .all(24),
                      alignment: .center,
                      child: ElevatedButton(
                        child: const Text('Collapse This Tile'),
                        onPressed: () {
                          return ExpansibleController.of(context).collapse();
                        },
                      ),
                    );
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the ExpansionTileController. With this approach you would have an outer widget that creates the ExpansionTile populated by instances of your new inner widgets, and then in these inner widgets you would use ExpansionTileController.of.

The ExpansibleController.expand and ExpansibleController.collapse methods cause the ExpansionTile to rebuild, so they may not be called from a build method.

Remember to dispose of the ExpansionTileController when it is no longer needed. This will ensure we discard any resources used by the object.

Implementation

// TODO(framework): Replace the following block with a @dartpad directive
// when it's supported. https://github.com/dart-lang/dartdoc/issues/4123
/// {@macro material_ui.dartpad_guide}
///
/// {@example /example/lib/expansion_tile/expansion_tile.1.dart#body}
///
/// </callout-box>
///
/// A more efficient solution is to split your build function into
/// several widgets. This introduces a new context from which you
/// can obtain the [ExpansionTileController]. With this approach you
/// would have an outer widget that creates the [ExpansionTile]
/// populated by instances of your new inner widgets, and then in
/// these inner widgets you would use `ExpansionTileController.of`.
///
/// The  [ExpansibleController.expand] and [ExpansibleController.collapse]
/// methods cause the [ExpansionTile] to rebuild, so they may not be called from
/// a build method.
///
/// Remember to dispose of the [ExpansionTileController] when it is no longer
/// needed. This will ensure we discard any resources used by the object.
@Deprecated(
  'Use ExpansibleController instead. '
  'This feature was deprecated after v3.31.0-0.1.pre.',
)
typedef ExpansionTileController = ExpansibleController;