Repository: flutter/flutter
File: packages/flutter/lib/src/material/expansion_tile.dart (Dart)

Code before:
  /// from the [ExpansionTileThemeData.expansionAnimationStyle] will be used.
  /// Otherwise, defaults to [Curves.easeIn].
  ///
  /// If [AnimationStyle.reverseCurve] is provided, it will be used to override
  /// the collapse animation curve. If it is null, then [AnimationStyle.reverseCurve]
  /// from the [ExpansionTileThemeData.expansionAnimationStyle] will be used.
  /// Otherwise, the same curve will be used as for expansion.
  ///
  /// To disable the theme animation, use [AnimationStyle.noAnimation].
  ///
  /// {@tool dartpad}
  /// This sample showcases how to override the [ExpansionTile] expansion
  /// animation curve and duration using [AnimationStyle].
  ///
  /// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.2.dart **
  /// {@end-tool}
  final AnimationStyle? expansionAnimationStyle;

  /// Whether to add button:true to the semantics if onTap is provided.
  /// This is a temporary flag to help changing the behavior of ListTile onTap semantics.
  ///
  // TODO(hangyujin): Remove this flag after fixing related g3 tests and flipping
  // the default value to true.
  final bool internalAddSemanticForOnTap;

  /// {@tool snippet}
  /// This example listens for hover and press states on an [ExpansionTile].
  ///
  /// ```dart
  /// class ExpansionTileStatesExample extends StatelessWidget {
  ///   ExpansionTileStatesExample({super.key});
  ///
  ///   final WidgetStatesController controller = WidgetStatesController();
  ///
  ///   @override
  ///   Widget build(BuildContext context) {
  ///     controller.addListener(() {
  ///       if (controller.value.contains(WidgetState.hovered)) {
  ///         debugPrint('Tile is hovered');
  ///       }
  ///     });
  ///
  ///     return MaterialApp(
  ///       home: Scaffold(
  ///         body: ExpansionTile(
  ///           title: const Text('Settings'),
  ///           statesController: controller,
  ///         ),
  ///       ),
  ///     );
  ///   }

Review comment (suggestion):
We should add a summary sentence so that IDEs like VS Code show useful code completion information if you type `myExpansionTile.statesController`. Consider something like:

```suggestion
  /// The controller that notifies when the widget's [WidgetState]s change.
  ///
  /// {@tool snippet}
```

Code after:
  /// from the [ExpansionTileThemeData.expansionAnimationStyle] will be used.
  /// Otherwise, defaults to [Curves.easeIn].
  ///
  /// If [AnimationStyle.reverseCurve] is provided, it will be used to override
  /// the collapse animation curve. If it is null, then [AnimationStyle.reverseCurve]
  /// from the [ExpansionTileThemeData.expansionAnimationStyle] will be used.
  /// Otherwise, the same curve will be used as for expansion.
  ///
  /// To disable the theme animation, use [AnimationStyle.noAnimation].
  ///
  /// {@tool dartpad}
  /// This sample showcases how to override the [ExpansionTile] expansion
  /// animation curve and duration using [AnimationStyle].
  ///
  /// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.2.dart **
  /// {@end-tool}
  final AnimationStyle? expansionAnimationStyle;

  /// Whether to add button:true to the semantics if onTap is provided.
  /// This is a temporary flag to help changing the behavior of ListTile onTap semantics.
  ///
  // TODO(hangyujin): Remove this flag after fixing related g3 tests and flipping
  // the default value to true.
  final bool internalAddSemanticForOnTap;

  /// The controller that notifies when the widget's [WidgetState]s change.
  ///
  /// This allows listening to and controlling states such as
  /// [WidgetState.hovered], [WidgetState.focused], [WidgetState.pressed],
  /// and [WidgetState.disabled] for the tile's header.
  ///
  /// If null, the backing [ListTile] will create and manage its own
  /// [WidgetStatesController].
  final WidgetStatesController? statesController;

  @override
  State<ExpansionTile> createState() => _ExpansionTileState();
}

class _ExpansionTileState extends State<ExpansionTile> {
  static final Animatable<double> _easeInTween = CurveTween(curve: Curves.easeIn);
  static final Animatable<double> _easeOutTween = CurveTween(curve: Curves.easeOut);
  static final Animatable<double> _halfTween = Tween<double>(begin: 0.0, end: 0.5);

  final ShapeBorderTween _borderTween = ShapeBorderTween();
  final ColorTween _headerColorTween = ColorTween();
  final ColorTween _iconColorTween = ColorTween();
  final ColorTween _backgroundColorTween = ColorTween();

  late Animation<double> _iconTurns;
  late Animation<ShapeBorder?> _border;