能否获取被FittedBox调整后的Text组件字号并同步给其他Text组件
解决方案
你最初的思路不生效的核心原因是:FittedBox 对文本的缩放是通过绘制时施加变换矩阵实现的,并不会修改子Text组件本身的style.fontSize属性,你直接从Widget结构里读取的永远是最初设置的原始字号,不是缩放后的实际显示字号。
推荐实现方案(无二次渲染,性能最优)
我们可以通过TextPainter模拟FittedBox的缩放逻辑,提前计算出长文本适配容器后的实际字号,直接给所有需要同步的Text组件使用即可,不需要依赖组件渲染后再取值更新。
- 第一步:先实现字号计算工具函数
double calculateAdaptedFontSize({ required String text, required TextStyle style, required double maxWidth, required double maxHeight, double minFontSize = 1, double maxFontSize = 100, }) { double fontSize = maxFontSize; late TextPainter painter; while (fontSize >= minFontSize) { painter = TextPainter( text: TextSpan(text: text, style: style.copyWith(fontSize: fontSize)), maxLines: 1, textDirection: TextDirection.ltr, ); painter.layout(maxWidth: double.infinity); if (painter.width <= maxWidth && painter.height <= maxHeight) { break; } fontSize -= 1; } return fontSize; }
- 第二步:结合你的布局实现字号同步
class Bar extends StatefulWidget { const Bar({super.key}); @override State<Bar> createState() => _Foo(); } class _Foo extends State<Bar> { // 作为基准的长文本 final String baseText = 'Much Longer'; // 文本通用基础样式 final TextStyle baseStyle = const TextStyle(color: Colors.black); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { // 计算基准文本所在容器的宽高(根据你的布局结构得出) // 外层Column两个Expanded平分高度 final maxHeight = constraints.maxHeight / 2; // 内层Row两个Expanded平分宽度 final maxWidth = constraints.maxWidth / 2; // 计算适配后的统一字号 final adaptedFontSize = calculateAdaptedFontSize( text: baseText, style: baseStyle, maxWidth: maxWidth, maxHeight: maxHeight, ); return Column( children: [ Expanded( child: Row( children: [ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 短文本直接使用计算出的统一字号 Text( 'Short', style: baseStyle.copyWith(fontSize: adaptedFontSize), ), ], ), ), const Expanded(child: SizedBox()), ], ), ), Expanded( child: Row( children: [ const Expanded(child: SizedBox()), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 长文本也使用计算出的字号,无需再套FittedBox Text( baseText, style: baseStyle.copyWith(fontSize: adaptedFontSize), ), ], ), ), ], ), ), ], ); }, ); } }
备选方案(从渲染完成的FittedBox取实际字号)
如果你一定要保留FittedBox包裹基准Text的逻辑,可以通过GlobalKey获取渲染后的变换参数计算实际字号,该方案会触发一次额外重绘,首次加载会有短暂的字号跳动:
class _Foo extends State<Bar> { final GlobalKey _fittedBoxKey = GlobalKey(); double? _adaptedFontSize; // 基准Text原始字号设足够大,保证会被FittedBox缩放 final double _originalFontSize = 100; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { // 组件渲染完成后读取缩放比例 final renderBox = _fittedBoxKey.currentContext?.findRenderObject() as RenderBox?; if (renderBox == null) return; final transform = renderBox.getTransformTo(null); final scale = transform.getMaxScaleOnAxis(); // 计算实际字号并更新 setState(() { _adaptedFontSize = _originalFontSize * scale; }); }); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: Row( children: [ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Short', style: TextStyle(fontSize: _adaptedFontSize ?? 14), ), ], ), ), const Expanded(child: SizedBox()), ], ), ), Expanded( child: Row( children: [ const Expanded(child: SizedBox()), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ FittedBox( key: _fittedBoxKey, fit: BoxFit.fill, child: Text( 'Much Longer', style: TextStyle(fontSize: _originalFontSize), ), ), ], ), ), ], ), ), ], ); } }
内容的提问来源于stack exchange,提问作者Wesley DaBes
相关产品推荐
相关产品推荐

