在某些需求下,我們需要獲取Widget
的大小或位置資訊。但Widget
物件本身沒有大小及位置資料,那麼想要拿到Widget
的大小及位置資訊,就需要通過與Widget
物件相關聯的RenderBox
物件來獲取。
下面就開始來獲取Widget
的大小與位置。
1、獲取RenderBox物件
要想獲取RenderBox
物件,其實很簡單。只需要呼叫BuildContext
的findRenderObject
方法即可。程式碼如下。
//context是一個BuildContext物件
RenderBox renderBox = context.findRenderObject();
複製程式碼
但有時候,並不能順利的拿到BuildContext
物件,那該怎麼辦尼?這時候就需要給Widget
物件設定一個Key,然後根據這個Key來拿到BuildContext
物件,從而獲取RenderBox
物件,程式碼如下。
class MyHomePageState extends State<MyHomePage> {
//定義一個key
GlobalKey _key = GlobalKey();
_getRenderBox() {
//獲取`RenderBox`物件
RenderBox renderBox = _key.currentContext.findRenderObject();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Flexible(
flex: 2,
child: Container(
//設定key
key: _key,
color: Colors.red,
),
),
],
),
);
}
}
複製程式碼
但在使用的時候,我們會發現如果在initState
方法通過上面的程式碼來獲取RenderBox
物件是不會成功且報錯的。這是為什麼尼?其實在呼叫initState
方法時,Widget
還未完成渲染,也就不能獲取RenderBox
物件。這時候只需要等待Widget
渲染完畢即可。程式碼如下。
class MyHomePageState extends State<MyHomePage> {
@override
void initState() {
//監聽Widget是否繪製完畢
WidgetsBinding.instance.addPostFrameCallback(_getRenderBox);
super.initState();
}
//定義一個key
GlobalKey _key = GlobalKey();
_getRenderBox(_) {
//獲取`RenderBox`物件
RenderBox renderBox = _key.currentContext.findRenderObject();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Flexible(
flex: 2,
child: Container(
//設定key
key: _key,
color: Colors.red,
),
),
],
),
);
}
}
複製程式碼
2、獲取Widget的大小
拿到與Widget
物件相關聯的RenderBox
物件來後,就可以很方便的獲取Widget
的大小。程式碼如下。
Size size = renderBox.size;
複製程式碼
下面來看一個示例。
class HomeState extends State<HomeWidget> {
@override
void initState() {
//監聽Widget是否繪製完畢
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
super.initState();
}
void _afterLayout(_) {
_getSizes();
}
_getSizes() {
final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();
final sizeRed = renderBoxRed.size;
//輸出背景為紅色的widget的寬高
print("size of red:$sizeRed");
final RenderBox renderBoxBlue = _keyBlue.currentContext.findRenderObject();
final sizeBlue = renderBoxBlue.size;
//輸出背景為藍色的widget的寬高
print("size of Blue:$sizeBlue");
final RenderBox renderBoxAmber =_keyAmber.currentContext.findRenderObject();
final sizeAmber = renderBoxAmber.size;
//輸出背景為的widget的寬高
print("size of Amber:$sizeAmber");
}
GlobalKey _keyRed = GlobalKey();
GlobalKey _keyBlue = GlobalKey();
GlobalKey _keyAmber = GlobalKey();
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Flexible(
key: _keyRed,
flex: 1,
child: Container(
color: Colors.red,
),
),
Flexible(
key: _keyBlue,
child: Container(
color: Colors.blue,
),
flex: 2,
),
Container(
key: _keyAmber,
width: 300,
height: 200,
color: Colors.amber,
),
],
);
}
}
複製程式碼
執行上面程式碼就可以把Widget
的寬高獲取到,輸出資訊如下。
size of red:Size(411.4, 134.5)
size of Blue:Size(411.4, 269.0)
size of Amber:Size(300.0, 200.0)
複製程式碼
可以發現,獲取的寬高都具有裝置無關性。
3、獲取Widget的位置
獲取Widget
的位置跟獲取其寬高一樣簡單,只需要呼叫不同API即可。如下。
Offset offset = renderBox.localToGlobal(Offset.zero);
複製程式碼
下面來看一個示例。
class HomeState extends State<HomeWidget> {
@override
void initState() {
//監聽Widget是否繪製完畢
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
super.initState();
}
void _afterLayout(_) {
_getPositions();
}
_getPositions() {
final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();
final positionsRed = renderBoxRed.localToGlobal(Offset(0, 0));
print("positions of red:$positionsRed");
final RenderBox renderBoxBlue = _keyBlue.currentContext.findRenderObject();
final positionsBlue = renderBoxBlue.localToGlobal(Offset(0, 0));
print("positions of Blue:$positionsBlue");
final RenderBox renderBoxAmber =
_keyAmber.currentContext.findRenderObject();
final positionsAmber = renderBoxAmber.localToGlobal(Offset(0, 0));
print("positions of Amber:$positionsAmber");
}
GlobalKey _keyRed = GlobalKey();
GlobalKey _keyBlue = GlobalKey();
GlobalKey _keyAmber = GlobalKey();
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Flexible(
key: _keyRed,
flex: 1,
child: Container(
color: Colors.red,
),
),
Flexible(
key: _keyBlue,
child: Container(
color: Colors.blue,
),
flex: 2,
),
//該Widget是居中顯示的
Container(
key: _keyAmber,
width: 300,
height: 200,
color: Colors.amber,
),
],
);
}
}
複製程式碼
執行上面程式碼就可以把Widget
的位置獲取到,輸出資訊如下。
positions of red:Offset(0.0, 80.0)
positions of Blue:Offset(0.0, 214.5)
positions of Amber:Offset(55.7, 483.4)
複製程式碼
Flutter
跟Android
一樣,都是以螢幕左上方為座標系的開始位置。而上面的輸出資訊就是Widget
到座標系的開始位置的距離。如下圖所示。
4、總結
通過上面的程式碼,就可以獲取到Widget
的大小及位置資訊,這在很多時候都是非常有用的。特別是當我們來自己實現一個自定義Widget
的點選、手勢等事件時,就需要跟位置資訊來獲取事件在Widget
中的位置。
【參考資料】