🌱

【Flutter】レンダリング後のWidgetサイズを取得する

2023/11/19に公開2

経緯

Expanded なのどのフレキシブルな Widget のサイズを取得する必要があった。

Global Key を使用

レンダリングされた Widget を特定するために Global Key を使用する。
※Global Key は一意である必要があったり、メモリ使用量増加にもつながるので慎重な使用が必要。

keyの宣言
final GlobalKey _containerKey = GlobalKey();
keyを指定
Container(
    key: _containerKey,
)

サイズの取得

Global Key を使って Widget を特定し、その Widget の RenderBox を取得する。
※RenderBox はレンダリングされたオブジェクトの Size プロパティを持っている。

RenderBoxの取得
final RenderBox? renderBox = _containerKey.currentContext?.findRenderObject() as RenderBox?;
print(renderBox?.size.toString()); // Size(600.0, 600.0)

init 時の注意点

initState()でサイズを取得したい場合は、レンダリング後にサイズ取得を行う必要があるため、addPostFrameCallback()を使用する。
addPostFrameCallback()は引数のコールバック関数をレンダリング後に実行してくれる。

init時
void getContainerSize() {
    final RenderBox? renderBox = _containerKey.currentContext?.findRenderObject() as RenderBox?;
    setState(() {
      containerSize = renderBox?.size ?? Size.zero;
    });
  }

  
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => getContainerSize());
  }

RenderBox について

RenderBox はプロパティにサイズや位置などを持つ RenderObject のサブクラスです。
RenderObject はレイアウトツリーに挿入されるタイミングで createRenderObject()によって作成される。
位置情報の取得は下記のように行います。

位置の取得
RenderBox renderBox = _containerKey.currentContext.findRenderObject();
Offset position = renderBox.localToGlobal(Offset.zero);
print(position); // Offset(0.0, 100.0)

https://api.flutter.dev/flutter/widgets/Transform/createRenderObject.html

コードサンプル

実行例

コード全文
import 'package:flutter/material.dart';

class RenderingSample extends StatefulWidget {
  const RenderingSample({
    super.key,
  });
  
  State<RenderingSample> createState() => _RenderingSampleState();
}

class _RenderingSampleState extends State<RenderingSample> {
  final GlobalKey _containerKey = GlobalKey();
  Size containerSize = Size.zero;

  void getContainerSize() {
    final RenderBox? renderBox = _containerKey.currentContext?.findRenderObject() as RenderBox?;
    setState(() {
      containerSize = renderBox?.size ?? Size.zero;
    });
  }

  
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => getContainerSize());
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Container(
            color: Colors.blue,
            height: 100,
            width: double.infinity,
            child: const Center(
              child: Text(
                'Height: 100',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 32,
                ),
              ),
            ),
          ),
          Expanded(
            child: Container(
              key: _containerKey,
              color: Colors.red,
              child: Center(
                child: Text(
                  'ContainerSize: ${containerSize.width} x ${containerSize.height}\nMediaQuery   :${MediaQuery.of(context).size.width} x ${MediaQuery.of(context).size.height}',
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 32,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Discussion

JboyHashimotoJboyHashimoto

レンダリングされた Widget を特定するために Global Key を使用する。
※Global Key は一意である必要があったり、メモリ使用量増加にもつながるので慎重な使用が必要。

そんなことがあるんですね。知らなかったです。素晴らしい記事をありがとうございます!