#FlutterでWidgetの位置やサイズを取得したい
検索しても地図上の位置を取得するのばっかヒットしてつらい
###GlobalKeyを使ってRenderBoxを取得する
参考サイト:https://medium.com/@diegoveloper/flutter-widget-size-and-position-b0a9ffed9407
//class TestWidgetは省略
GlobalKey globalKey = GlobalKey(); //←これが重要
class _TestState extends State<TestWidget>{
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text("test"),
onPressed: (){
//↓変数はRenderBoxで宣言(.findRenderObject()で帰ってくるのは"RenderObject"のため
RenderBox box = globalKey.currentContext.findRenderObject();
print("ウィジェットのサイズ :${box.size}");
print("ウィジェットの位置 :${box.localToGlobal(Offset.zero)}");
},
),
),
Center(
child: Text("このウィジェットのサイズ",
key: globalKey, //←知りたいWidgetにGlobalKeyをセット
),
),
],
);
}
}
RenderBox box = globalKey.currentContext.findRenderObject();
↑
これでGlobalKey
をセットしたWidget
を元に描画されたRenderBox
インスタンスを取得出来ます。(返ってくるのはRenderObject
なので変数の宣言はRenderBox
にする)
結果
ウィジェットのサイズ :Size(88.0, 48.0)
ウィジェットの位置 :Offset(0.0,24.0)
.localeToGlobal(Offset)で取得している位置は、ウィジェットの左上の点
引数のOffsetがゼロでない場合、その分の座標が足される
###注意点
GlobalKeyを付けたWidgetが一度もBuildされていない場合、RenderBoxは取得出来ない
一度もWidget
がbuild
されていない場合、RenderBox
はそもそも描画されていないので取得出来ません。(大きさが可変のWidget
を考えてみれば分かる)
// 失敗するやり方
GlobalKey globalKey = GlobalKey();
class _TestState extends State<TestWidget> {
//サイズと位置を取得するメソッド
String _getLocaleAndSize() {
RenderBox box = globalKey.currentContext.findRenderObject();
return "ウィジェットのサイズ :${box.size}\n"
"ウィジェットの位置 :${box.localToGlobal(Offset.zero)}";
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text(_getLocaleAndSize()), // ←ボタンのタイトルにする
onPressed: () {
print(_getLocaleAndSize);
},
),
),
Center(
child: Text(
"このウィジェットのサイズ",
key: globalKey,
),
),
],
);
}
}
結果
エラー
The method 'findRenderObject' was called on null.
Receiver: null
Tried calling: findRenderObject()
###回避法
WidgetsBinding.instance.addPostFrameCallback(Function callback)
を使う
WidgetsBinding.addPostFrameCallback
を使うとBuild終了時に実行する処理が書けます
var globalKey = GlobalKey();
class _TestState extends State<TestWidget> {
String _getLocaleAndSize() {
RenderBox box = globalKey.currentContext.findRenderObject();
return "ウィジェットのサイズ :${box.size}\n"
"ウィジェットの位置 :${box.localToGlobal(Offset.zero)}";
}
String _text;//←変数を用意
@override
Widget build(BuildContext context) {
if (_text == null) // Build時、テキストがnullの場合↓を実行
WidgetsBinding.instance.addPostFrameCallback((cb){
setState(() {
_text = _getLocaleAndSize();
});
});
return Column(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text(_text ?? "テキストはまだない"),
onPressed: () {
print(_getLocaleAndSize);
},
),
),
Center(
child: Text(
"このウィジェットのサイズ",
key: globalKey,
),
),
],
);
}
}
注意
WidgetsBinding.addPostFrameCallback
は他にも色々使えて便利ですが、build
時に使用するときは無限ループしないように気をつけましょうね~