组件

已经封装好的组件,可以直接替代原有 Text 组件使用,这里也添加了一些便捷的样式用于 ListTile 中的使用。

其中的 Marquee 组件来自于 pub 中的插件,当然也可以去看看是怎么实现的,这里就不展开讲了,实现原理也比较简单,通过 ListView 等可滚动组件来模拟 Marquee 效果

class AdaptiveText extends StatelessWidget {
  final String text;
  final TextStyle? style;
  final double? maxWidth;
  final bool? isTileTitle;
  final bool? isTileSubtitle;
  final bool? isPrimaryTitle;
  final bool? isTitle;

  const AdaptiveText(
    this.text, {
    super.key,
    this.style,
    this.maxWidth,
    this.isTileTitle,
    this.isTileSubtitle,
    this.isPrimaryTitle,
    this.isTitle,
  });

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;
    final textScaler = MediaQuery.textScalerOf(context);
    var textStyle = style;
    if (isTileTitle == true) {
      textStyle = textTheme.bodyLarge?.copyWith(color: colorScheme.onSurface);
    }
    if (isTileSubtitle == true) {
      textStyle =
          textTheme.bodyMedium?.copyWith(color: colorScheme.onSurfaceVariant);
    }
    if (isTitle == true) {
      textStyle = textTheme.titleLarge;
    }
    if (isPrimaryTitle == true) {
      textStyle = textTheme.titleLarge?.copyWith(
        color: colorScheme.primary,
      );
    }
    return LayoutBuilder(
      builder: (context, constraints) {
        final textPainter = TextPainter(
          text: TextSpan(text: text, style: textStyle),
          textDirection: TextDirection.ltr,
          maxLines: 1,
          textScaler: textScaler,
        )..layout(maxWidth: maxWidth ?? constraints.maxWidth);
        return textPainter.didExceedMaxLines
            ? SizedBox(
                height: textPainter.height,
                width: maxWidth ?? constraints.maxWidth,
                child: Marquee(
                  text: text,
                  velocity: 20,
                  blankSpace: 20,
                  textScaler: textScaler,
                  pauseAfterRound: const Duration(seconds: 1),
                  accelerationDuration: const Duration(seconds: 1),
                  accelerationCurve: Curves.linear,
                  decelerationDuration: const Duration(milliseconds: 300),
                  decelerationCurve: Curves.easeOut,
                  style: textStyle,
                ),
              )
            : Text(
                text,
                style: textStyle,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
              );
      },
    );
  }
}

跑马灯

跑马灯也叫 Marquee 元素,常用于在有限的空间中展示更多的信息。

自适应

我们总是希望只有在文本宽度超过可以显示的大小时才显示 Marquee,使用 LayoutBuilder 可以很容易的做到这一点。只需要通过 TextPainter 对需要显示的文本进行渲染,同时限制最大宽度为可用的宽度,如果超出了需要显示的布局范围,就显示为跑马灯组件,否则返回一个普通的 Text 组件。