import 'package:flutter/material.dart';

/// A custom [ListTile] that has a built-in animated custom leading action
/// after the [leading] widget built in as a part of [title] that
/// reveals the [subtitleReveal] when clicked.
///
/// This is useful when a more compact look is desired where more information
/// is provided as an optional user based reveal action. The purpose is to make
/// UI less talkative but provide easy access to additional usage explanation.
///
/// This is a Flutter "Universal" Widget that only depends on the SDK and
/// can be dropped into any application.
class ListTileReveal extends StatefulWidget {
  const ListTileReveal({
    super.key,
    this.title,
    this.leading,
    this.subtitle,
    this.subtitleReveal,
    this.trailing,
    this.contentPadding,
    this.tileColor,
    this.onTap,
    this.dense,
    this.revealDense,
    this.enabled = true,
    this.isOpen,
    this.duration = const Duration(milliseconds: 200),
  });

  /// A widget to display before the title.
  ///
  /// Typically an [Icon] or a [CircleAvatar] widget.
  final Widget? leading;

  /// The primary content of the list tile.
  ///
  /// Typically a [Text] widget.
  ///
  /// This should not wrap. To enforce the single line limit, use
  /// [Text.maxLines].
  final Widget? title;

  /// Additional content displayed below the title.
  ///
  /// Typically a [Text] widget.
  final Widget? subtitle;

  /// Additional content displayed below the subtitle in a reveal animation.
  ///
  /// Typically a [Text] widget.
  final Widget? subtitleReveal;

  /// A widget to display after the title.
  ///
  /// Typically an [Icon] widget.
  ///
  /// To show right-aligned metadata (assuming left-to-right reading order;
  /// left-aligned for right-to-left reading order), consider using a [Row] with
  /// [CrossAxisAlignment.baseline] alignment whose first item is [Expanded] and
  /// whose second child is the metadata text, instead of using the [trailing]
  /// property.
  final Widget? trailing;

  /// The [ListTileReveal]'s internal padding.
  ///
  /// Insets a [ListTileReveal]'s contents: its [leading], [title],
  /// [subtitleReveal],
  /// and [trailing] widgets.
  ///
  /// If null, `EdgeInsets.symmetric(horizontal: 16.0)` is used in M2
  /// and `EdgeInsetsDirectional.only(start: 16.0, end: 24.0)` in M3.
  final EdgeInsetsGeometry? contentPadding;

  /// Defines the background color of `ListTileReveal` when `selected` is false.
  ///
  /// If this property is null and `selected` is false then
  /// [ListTileThemeData.tileColor] is used. If that is also null and
  /// `selected` is true, `selectedTileColor` is used.
  ///
  /// When that is also null, the [ListTileTheme.selectedTileColor] is
  /// used, otherwise [Colors.transparent] is used.
  final Color? tileColor;

  /// Called when the user taps this list tile.
  ///
  /// Inoperative if [enabled] is false.
  final GestureTapCallback? onTap;

  /// Whether this list tile and card operation is interactive.
  final bool enabled;

  /// Whether this list tile is part of a vertically dense list.
  ///
  /// If this property is null then its value is based on [ListTileTheme.dense].
  ///
  /// Dense list tiles default to a smaller height.
  final bool? dense;

  /// Whether the used reveal part of the ListTile is dense.
  ///
  /// If not defined, defaults to true.
  final bool? revealDense;

  /// Set to true to open the info section of the ListTile, to false to close
  /// it.
  ///
  /// If not defined, defaults to false.
  final bool? isOpen;

  /// The duration of the show and hide animation of child.
  final Duration duration;

  @override
  State<ListTileReveal> createState() => _ListTileRevealState();
}

class _ListTileRevealState extends State<ListTileReveal> {
  late bool _isOpen;

  @override
  void initState() {
    super.initState();
    _isOpen = widget.isOpen ?? false;
  }

  @override
  void didUpdateWidget(covariant ListTileReveal oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.isOpen != oldWidget.isOpen) _isOpen = widget.isOpen ?? false;
  }

  void _handleTap() {
    setState(() {
      _isOpen = !_isOpen;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        ListTile(
          tileColor: widget.tileColor,
          enabled: widget.enabled,
          contentPadding: widget.contentPadding,
          leading: widget.leading,
          dense: widget.dense,
          title: Wrap(
            crossAxisAlignment: WrapCrossAlignment.center,
            children: <Widget>[
              if (widget.title != null) widget.title!,
              if (widget.subtitleReveal != null && widget.enabled)
                InkWell(
                  onTap: widget.enabled ? _handleTap : null,
                  borderRadius: BorderRadius.circular(28),
                  child: SizedBox(
                    width: 28,
                    height: 28,
                    child: Icon(
                      _isOpen ? Icons.info : Icons.info_outlined,
                      size: 18,
                    ),
                  ),
                ),
            ],
          ),
          subtitle: widget.subtitle,
          trailing: widget.trailing,
          onTap: widget.enabled ? widget.onTap : null,
        ),
        AnimatedSwitcher(
          duration: widget.duration,
          transitionBuilder: (Widget child, Animation<double> animation) {
            return SizeTransition(
              sizeFactor: animation,
              axisAlignment: _isOpen ? 1 : -1,
              child: child,
            );
          },
          child: (_isOpen && widget.subtitleReveal != null && widget.enabled)
              ? ListTile(
                  dense: widget.revealDense ?? true,
                  subtitle: widget.subtitleReveal,
                  onTap: widget.enabled ? _handleTap : null,
                )
              : const SizedBox.shrink(),
        ),
      ],
    );
  }
}
