想要获取widget的尺寸,必须要等widget的layout结束之后才能取到,目前有三种方式
- 通过
BuildContext获取 - 通过
GlobalKey获取 - 通过
SizeChangedLayoutNotifier获取
通过BuildContext获取
widget的尺寸存在于context?.findRenderObject()?.paintBounds?.size中,过早获取可能为空,需要延时获取.在flutter1.7之前duration=Duration()就能获取到,但是1.7之后必须要设置一百毫秒以上才行.
class FindSizeWidget extends StatefulWidget {
@override
_FindSizeWidgetState createState() => _FindSizeWidgetState();
}
class _FindSizeWidgetState extends State<FindSizeWidget> {
@override
Widget build(BuildContext context) {
/// 延时一下,需要等state layout结束之后才能获取size
Future.delayed(Duration(milliseconds: 100), () {
_printSize();
});
return _buildContentWidget();
}
Widget _buildContentWidget(){
return Container(
color: Colors.red,
child: Text(
'''
1 ... 1
2 ... 2
3 ... 3
4 ... 4
''',
style: TextStyle(fontSize: 30),
maxLines: null,
softWrap: true,
),
);
}
_printSize(){
if (!mounted) return;
var size = context?.findRenderObject()?.paintBounds?.size;
print(size.toString());
}
}
打印结果:
flutter: Size(277.0, 180.0)
通过GlobalKey获取
给FindSizeWidget增加构造方法,通过外部传入GlobalKey,方便以后寻找到FindSizeWidget.context对象.
class FindSizeWidget extends StatefulWidget {
const FindSizeWidget({Key key}) : super(key:key);
}
注意一个
GlobalKey只能对应一个widget对象,当心复用问题.
增加一个获取尺寸的按钮.点击之后获取尺寸.
class _MyApp extends State<MyApp> {
GlobalKey _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
FlatButton(
onPressed: () {
var size = _globalKey.currentContext
?.findRenderObject()
?.paintBounds
?.size;
print(size.toString());
},
child: Text("获取尺寸")),
FindSizeWidget(
key: _globalKey,
)
],
);
}
}
打印结果:
flutter: Size(277.0, 180.0)
通过SizeChangedLayoutNotifier获取
使用SizeChangedLayoutNotifier方式,Widget会在layout结束之后会发出一个LayoutChangedNotification通知,我们只需要接收这个通知,即可获取尺寸信息,但是SizeChangedLayoutNotifier的RenderObject是_RenderSizeChangedWithCallback类,它在第一次布局完成之后并不会发出通知,所以我们要自定义SizeChangedLayoutNotifier和_RenderSizeChangedWithCallback两个类.
_RenderSizeChangedWithCallback源码部分:
@override
void performLayout() {
super.performLayout();
// Don't send the initial notification, or this will be SizeObserver all
// over again!
if (_oldSize != null && size != _oldSize)
onLayoutChangedCallback();
_oldSize = size;
}
修改_RenderSizeChangedWithCallback,只需要去掉_oldSize != null的判断即可.
@override
void performLayout() {
super.performLayout();
// Don't send the initial notification, or this will be SizeObserver all
// over again!
if (size != _oldSize)
onLayoutChangedCallback();
_oldSize = size;
}
再修改_FindSizeWidgetState的build方法:
@override
Widget build(BuildContext context) {
return NotificationListener<LayoutChangedNotification>(
onNotification: (notification) {
/// 收到布局结束通知,打印尺寸
_printSize();
/// flutter1.7之后需要返回值,之前是不需要的.
return null;
},
child: CustomSizeChangedLayoutNotifier(
child: _buildContentWidget(),
),
);
}
打印结果:
flutter: Size(277.0, 180.0)