1. 固定区域智能显示文本框
需求:给定一个固定大小的区域,可以智能的显示文本(列排版):
- 可以智能的调整字体大小的显示
- 如果,小于或等于最小的字体,说明字体太多排不下,则增加滑动。
- 如果超出或等于最大字体,说明可以排版下,这时候使header和content还有bottom都居中显示。
2. 思路
对于给定的区域,先用 Column
中的 Expand
排版,进行计算;待计算出结果后,进行状态的更新。
- 获取可用的区域
Expanded(
child: Container(
child: LayoutBuilder(builder: (context, constraints) {
_calculateAndSetFontSize(Size(constraints.maxWidth, constraints.maxHeight));
return Container(
child: Text('empty'),
);
}),
),
)
- 重新构建方法设置
void _calculateAndSetFontSize(Size size) {
print('size = $size');
......
Future.delayed(Duration(milliseconds: 0), () {
setState(() {});
});
}
这里需要进行延迟 Future.delayed
,否则就是正在 b
uild中又设置
markNeedsBuild = true,需要再次
build`。会出现下面的异常:
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building LayoutBuilder:
flutter: setState() or markNeedsBuild() called during build.
flutter: This _FitTextBox widget cannot be marked as needing to build because the framework is already in the
flutter: process of building widgets. A widget can be marked as needing to be built during the build phase
flutter: only if one of its ancestors is currently building. This exception is allowed because the framework
flutter: builds parent widgets before children, which means a dirty descendant will always be built.
flutter: Otherwise, the framework might not visit this widget during this build phase.
flutter: The widget on which setState() or markNeedsBuild() was called was:
flutter: _FitTextBox(state: __FitTextBoxState#231de)
flutter: The widget which was currently being built when the offending call was made was:
flutter: LayoutBuilder(renderObject: _RenderLayoutBuilder#e4bfa relayoutBoundary=up1 NEEDS-LAYOUT
flutter: NEEDS-PAINT)
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3502:11)
flutter: #1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3528:6)
flutter: #2 State.setState (package:flutter/src/widgets/framework.dart:1132:14)
flutter: #3 __FitTextBoxState._calculateAndSetFontSize (package:hello/home/base/fitText/wisdom_fit_text.dart:93:5)
3. 具体实现代码
- 使用方法
Container(
width: double.infinity,
height: 100.0 + 30.0 + 50.0,
color: Colors.red[200],
child: _FitTextBox(
showText: textString,
fixedHeightHeaderWidget: Container(
width: 100.0,
height: 30.0,
color: Colors.blue,
),
fixedHeightBottomWidget: Container(
width: 100.0,
height: 50.0,
color: Colors.yellow[300],
),
),
)
- 实现代码
class _FitTextBox extends StatefulWidget {
final String showText;
final Widget fixedHeightHeaderWidget;
final Widget fixedHeightBottomWidget;
_FitTextBox({this.showText, this.fixedHeightHeaderWidget, this.fixedHeightBottomWidget});
@override
__FitTextBoxState createState() => __FitTextBoxState();
}
class __FitTextBoxState extends State<_FitTextBox> {
double _realFontSize;
bool _layoutReady = false;
bool _needCenter = false;
bool _needScroll = false;
void _calculateAndSetFontSize(Size size) {
print('size = $size');
double retFontSize = TextSize.calculateFontSizeSync(
size,
widget.showText,
);
_realFontSize = retFontSize;
if (_realFontSize >= TextSize.maxFontSize) {
_needCenter = true;
} else {
_needCenter = false;
}
if (_realFontSize <= TextSize.minFontSize) {
_needScroll = true;
} else {
_needScroll = false;
}
print('needCenter = $_needCenter');
_layoutReady = true;
Future.delayed(Duration(milliseconds: 0), () {
setState(() {});
});
}
@override
void didUpdateWidget(_FitTextBox oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget");
if (oldWidget.showText != widget.showText) {
_layoutReady = false;
}
}
@override
Widget build(BuildContext context) {
print('__FitTextBoxState build');
var headerWidget = widget.fixedHeightHeaderWidget ?? Container();
var bottomWidget = widget.fixedHeightBottomWidget ?? Container();
if (!_layoutReady) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
headerWidget,
Expanded(
child: Container(
child: LayoutBuilder(builder: (context, constraints) {
_calculateAndSetFontSize(Size(constraints.maxWidth, constraints.maxHeight));
return Container(
child: Text('empty'),
);
}),
),
),
bottomWidget,
],
);
}
Widget childWidget = Container(
child: Center(
child: Text(
widget.showText,
style: TextStyle(
fontSize: _realFontSize,
),
),
),
);
if (_needScroll) {
childWidget = SingleChildScrollView(
child: childWidget,
);
}
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
headerWidget,
_needCenter
? childWidget
: Expanded(
child: childWidget,
),
bottomWidget,
],
),
);
}
}