想要获取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)