天天看點

Flutter中如何擷取widget的大小和位置?

在我們實際的開發中,會有要擷取某個widget的大小和位置的需求,但是widget本身并沒有對應的屬性擷取size和position,怎麼辦呢?看官莫急,且往下看。

我們首先建立一個demo工程,長的是這個樣子,column中分别有三個不同顔色的widget,底部兩個按鈕,點選分别擷取size和position:

Flutter中如何擷取widget的大小和位置?

代碼很簡單:

//擷取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的方式,既可以在某個時機擷取,也可以進入頁面就擷取到,非常的友善!