在我们实际的开发中,会有要获取某个widget的大小和位置的需求,但是widget本身并没有对应的属性获取size和position,怎么办呢?看官莫急,且往下看。
我们首先创建一个demo工程,长的是这个样子,column中分别有三个不同颜色的widget,底部两个按钮,点击分别获取size和position:
代码很简单:
//获取widget大小
_getSize() {
}
//获取widget位置
_getPosition() {
}
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
Flexible(
flex: 1,
child: Container(
color: Colors.red,
),
),
Flexible(
flex: 2,
child: Container(
color: Colors.green,
),
),
Flexible(
flex: 1,
child: Container(
color: Colors.orange,
),
),
Spacer(),
Padding(
padding: EdgeInsets.only(bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: MaterialButton(
child: Text(
"get size!",
style: TextStyle(
color: Colors.red,
),
),
onPressed: _getSize,
),
),
Expanded(
child: MaterialButton(
child: Text(
"get position!",
style: TextStyle(
color: Colors.red,
),
),
onPressed: _getPosition,
),
)
],
),
)
],
),
);
OK,回到之前的问题,我们要怎么获取这几个widget的size和position呢?
一、事件触发获取
我们不妨先聚焦到一个widget,比如上面绿色的widget,要想获取widget的size和position,首先需要给这个widget设置一个key:
GlobalKey _keyGreen = GlobalKey();
...
...
Flexible(
flex: 2,
child: Container(
key: _keyGreen,
color: Colors.green,
),
),
一旦为widget设置了key,我们就可以通过key获取到widget对应的RenderBox,从而获取size和position.
获取size:
_getSize() {
final RenderBox renderBox = _keyGreen.currentContext.findRenderObject();
final sizeGreen = renderBox.size;
print("SIZE of green: $sizeGreen");
}
点击获取size的按钮,会打印如下内容:
flutter: SIZE of green: Size(375.0, 214.0)
这就是我们绿色widget的宽和高。
获取position:
_getPosition() {
final RenderBox renderBox = _keyGreen.currentContext.findRenderObject();
final positionGreen = renderBox.localToGlobal(Offset.zero);
print("POSITION of green: $positionGreen");
}
同样点击获取position的按钮,打印如下内容:
flutter: POSITION of green: Offset(0.0, 183.0)
这就是绿色widget左上角的坐标,即在整个屏幕中的位置。
二、进入页面获取
以上都是通过点击事件触发的获取size和position,如果需要在进入页面的时候就能获取widget的size和pisition又该如何操作呢?
首先想到的是在类的构造函数或者initState函数中调用我们上面实现的两个方法,然鹅很不幸,尝试过的朋友就会发现,运行报错类似如下:
flutter: Another exception was thrown: NoSuchMethodError: The method ‘findRenderObject’ was called on null.
这是为什么呢?这是因为构造器或者init方法执行的时候,我们的context还没有和state进行关联,此时获取到的currentContext必然是null,获取失败!
等到widget渲染完成后,此时currentContext即widget对应的element就已经和State完成绑定,那我们怎么知道何时widget渲染完毕呢?
其实方法也很简单,WidgetsBinding为我们提供了对应的回调,我们可以这样用:
@override
initState(){
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_afterLayout();
});
super.initState();
}
_afterLayout(){
_getSize();
_getPosition();
}
获取的结果也是没有任何毛病的:
flutter: SIZE of green: Size(375.0, 214.0)
flutter: POSITION of green: Offset(0.0, 183.0)
以上,就是flutter中获取特定widget的size和position的方式,既可以在某个时机获取,也可以进入页面就获取到,非常的方便!