buildM3ESimpleBody function

Widget buildM3ESimpleBody(
  1. BuildContext context,
  2. M3EExpandableData data,
  3. double progress,
  4. M3EExpandableStyle decoration,
)

Implementation

Widget buildM3ESimpleBody(
  BuildContext context,
  M3EExpandableData data,
  double progress,
  M3EExpandableStyle decoration,
) {
  final List<Widget> children = [];

  if (data.subtitle != null && data.subtitle!.isNotEmpty) {
    // Resolve subtitle styles based on user input
    TextStyle collapsedSubtitleStyle;
    TextStyle expandedSubtitleStyle;

    if (data.subtitleStyle != null && data.subtitleStyle!.length == 2) {
      collapsedSubtitleStyle = data.subtitleStyle![0];
      expandedSubtitleStyle = data.subtitleStyle![1];
    } else if (data.subtitleStyle != null && data.subtitleStyle!.length == 1) {
      collapsedSubtitleStyle = data.subtitleStyle![0];
      expandedSubtitleStyle = data.subtitleStyle![0];
    } else {
      final defaultStyle = Theme.of(context).textTheme.bodyMedium!;
      collapsedSubtitleStyle = defaultStyle;
      expandedSubtitleStyle = defaultStyle;
    }

    final alignment = decoration.bodyAlignment;
    final maxLines = data.subtitleMaxLines ?? 1;

    // Determine TextAlign based on AlignmentGeometry roughly
    TextAlign mappedTextAlign = TextAlign.start;
    if (alignment == Alignment.topCenter ||
        alignment == Alignment.center ||
        alignment == Alignment.bottomCenter) {
      mappedTextAlign = TextAlign.center;
    } else if (alignment == Alignment.topRight ||
        alignment == Alignment.centerRight ||
        alignment == Alignment.bottomRight) {
      mappedTextAlign = TextAlign.right;
    }

    // Smooth crossfade: use hard switch at midpoint to avoid flicker
    // We keep the Stack based approach to preserve the exact UI requested,
    // but ensure it's clean and handles progress correctly.
    final showCollapsedSubtitle = progress < 0.5;
    final showExpandedSubtitle = progress >= 0.5;

    children.add(
      Padding(
        padding: EdgeInsets.only(top: decoration.titleSubtitleGap),
        child: Stack(
          children: [
            if (showCollapsedSubtitle)
              Align(
                alignment: alignment,
                child: Text(
                  data.subtitle!,
                  maxLines: maxLines,
                  overflow: TextOverflow.ellipsis,
                  style: collapsedSubtitleStyle,
                  textAlign: mappedTextAlign,
                ),
              ),
            if (showExpandedSubtitle)
              ClipRect(
                child: Align(
                  alignment: alignment,
                  heightFactor: 1.0,
                  child: Text(
                    data.subtitle!,
                    style: expandedSubtitleStyle,
                    textAlign: mappedTextAlign,
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }

  if ((data.body != null || data.bodyBuilder != null) && progress > 0.0) {
    children.add(
      ClipRect(
        child: Align(
          alignment: decoration.bodyAlignment,
          heightFactor: progress.clamp(0.0, 1.0),
          child: Padding(
            padding: EdgeInsets.only(top: children.isEmpty ? 0 : 12),
            child: data.bodyBuilder?.call(context) ?? data.body!,
          ),
        ),
      ),
    );
  }

  if (children.isEmpty) return const SizedBox.shrink();
  if (children.length == 1) return children.first;

  return Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: children,
  );
}