LoginSignup
32
23

More than 3 years have passed since last update.

FlutterでWidgetの位置とサイズを取得する

Last updated at Posted at 2020-03-25

FlutterでWidgetの位置やサイズを取得したい

検索しても地図上の位置を取得するのばっかヒットしてつらい

GlobalKeyを使ってRenderBoxを取得する

参考サイト:https://medium.com/@diegoveloper/flutter-widget-size-and-position-b0a9ffed9407

TestState.dart
//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は取得出来ない
一度もWidgetbuildされていない場合、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時に使用するときは無限ループしないように気をつけましょうね~

32
23
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
23