[譯]Flutter: Widget Size and Position

翻滾的猿_開著窗戶睡覺發表於2019-03-05

原文連結
作者: Diego Velasquez

喜歡理由:可以更好的除錯佈局問題

[譯]Flutter: Widget Size and Position
我遇到過很多人問,如何在螢幕上獲取Widgets 的 size 和 position 的問題。在某些情況下,我們會發現我們總是會有一些原因需要去實現這個功能。但是 Widget本身是沒有 size 和 position。為了實現這一點,我們必須獲得與Widget上下文相關聯的RenderBox。

How do we do this?

我們可以建立一個demo,在Column中畫3個不同顏色的皮膚,分別是紅、紫、綠,再在底部寫兩個按鈕分別是 Get Sizes, Get Position.

demo 程式碼片段

   _getSizes() {
   }

   _getPositions(){
   }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
      ),
      body: Column(
        children: <Widget>[
          Flexible(
            flex: 2,
            child: Container(
              color: Colors.red,
            ),
          ),
          Flexible(
            flex: 1,
            child: Container(
              color: Colors.purple,
            ),
          ),
          Flexible(
            flex: 3,
            child: Container(
              color: Colors.green,
            ),
          ),
          Spacer(),
          Padding(
            padding: const EdgeInsets.only(bottom: 8.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                MaterialButton(
                  elevation: 5.0,
                  padding: EdgeInsets.all(15.0),
                  color: Colors.grey,
                  child: Text("Get Sizes"),
                  onPressed: _getSizes,
                ),
                MaterialButton(
                  elevation: 5.0,
                  color: Colors.grey,
                  padding: EdgeInsets.all(15.0),
                  child: Text("Get Positions"),
                  onPressed: _getPositions,
                )
              ],
            ),
          )
        ],
      ),
    );
  }
複製程式碼

效果圖:

[譯]Flutter: Widget Size and Position
現在的問題是:我如何得到每個皮膚的 size 和 position?

讓我們先關注上面的紅皮膚,當我們知道如何獲取它的 size 和 position,那麼對於其他皮膚也就迎刃而解了。

獲取 Widget 的 Size

為了實現這個,我們需要讓我們的Widget有一個Key,所以我們要建立一個GlobalKey並賦值給我們的Widget

//creating Key for red panel
GlobalKey _keyRed = GlobalKey();
...
//set key
    Flexible(
             flex: 2,
             child: Container(
               key: _keyRed,
               color: Colors.red,
             ),
     ),
複製程式碼

一旦我們Widget 有了 Key,我們就能通過下面的方式根據Key去獲取size。

_getSizes() {
    final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();
    final sizeRed = renderBoxRed.size;
    print("SIZE of Red: $sizeRed");
 }
複製程式碼

如果我們點選 Get Sizes 按鈕,在控制檯將顯示

flutter: SIZE of Red: Size(375.0, 152.9)
複製程式碼

現在我們知道 紅色皮膚的寬高分別是 375.0 152.9

很簡單對吧,讓我們繼續獲取Widget所處的位置資訊

獲取Widget的位置

與之前的方式一樣,我們的Widget必須有一個Key值,我們更新一下獲取Widget位置的方法,來獲取Widget 相對於定義位置左上角的位置(在這個例子中,我們使用 0.0來表示當前螢幕的左上角)

_getPositions() {
    final RenderBox renderBoxRed = _keyRed.currentContext.findRenderObject();
    final positionRed = renderBoxRed.localToGlobal(Offset.zero);
    print("POSITION of Red: $positionRed ");
  }
複製程式碼

如果點選 Get Positions 按鈕,將在控制檯列印出:

flutter: POSITION of Red: Offset(0.0, 76.0)
複製程式碼

這說明我們的Widget 在X軸上是0.0 在Y軸上是76.0(在螢幕的左上角)

為什麼是76.0?那是因為在這個例子中上面有個高度為76.0的AppBar

[譯]Flutter: Widget Size and Position
哇~ 到這裡,我們已經知道如何去獲取Widget的 size 和 position

But~ 如果我想在開始的時候就得到 size 和 position,而不是通過按鈕的的點選獲取,該如何?

[譯]Flutter: Widget Size and Position
那麼試試在建構函式中呼叫我們的方法

_MainSizeAndPositionState(){
     _getSizes();
     _getPositions();
   }
複製程式碼

執行一下demo,會出現錯誤資訊

flutter: The following NoSuchMethodError was thrown building Builder:
flutter: The method 'findRenderObject' was called on null.
flutter: Receiver: null
flutter: Tried calling: findRenderObject()
複製程式碼

那麼我們試試從InitState中呼叫會怎麼樣

@override
  void initState() {
    _getSizes();
    _getPositions();
    super.initState();
  }
複製程式碼

執行一下demo,與剛才有點不一樣,但還是報錯

flutter: Another exception was thrown: NoSuchMethodError: The method 'findRenderObject' was called on null.
複製程式碼

那我們要在開始的時候獲取大小和位置該如何去做呢?

[譯]Flutter: Widget Size and Position
上面報錯的原因是因為我們的state還沒有得到相應的context。

以下網址可以找到更多的資訊關於Widget的生命週期 medium.com/flutter-com…,掘金上有一篇翻譯的,但是作者與來源不一樣,內容差不多,可以參考juejin.im/post/5c768a…

因此我們必須等待Widget完成渲染後,再執行。但是該怎麼做?

下面有個簡單的實現方式:

@override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
    super.initState();
  }
 _afterLayout(_) {
    _getSizes();
    _getPositions();
  }
複製程式碼

這樣就可以確保佈局渲染完成後去呼叫你的方法
再執行一次demo,將會得到:

flutter: SIZE of Red: Size(375.0, 152.9)
flutter: POSITION of Red: Offset(0.0, 76.0)
複製程式碼

最後!!!

你也可以回顧一下我朋友Simon Lightfoot 建立的package pub.dartlang.org/packages/af…

總結

很多次我們都會把簡單的事情搞得非常複雜。所以仔細閱讀Flutter提供的官方文件其實很有必要,畢竟我們每天都在學習新的東西。

你可以在我的 flutter-samples repo中檢視我的原始碼 github.com/diegovelope…

參考文章

譯者語

文章中有些連結需要~~你懂的~

這是我第一篇譯文,全靠個人的語感翻譯的,歡迎指正~~~

相關文章