Help us understand the problem. What is going on with this article?

Flutterで、あるWidgetのレイアウト後の実際のサイズを取得する

More than 1 year has passed since last update.

FlutterでのUIビルディングは、最終的に表示されるUIの構造(とプロパティ)を、Widgetのツリーで定義します。アプリを作る開発者は、そのUIの各要素が、どのようにレイアウトされレンダリングされたかの詳細を知る必要は、ほとんどありません。

ただし、まれにではありますが、あるWidgetが、実際のレイアウト時にどのようなサイズになっているかを知りたいケースがある……かもしれません。

アプリ開発者が通常扱う Widget とは別の、より低レイヤの Render でレイアウトが行われており、ここまで踏み込むとサイズがわかります。

そこで以下に、あるWidgetのレイアウト・サイズを、コールバックとして受け取るSizeListenableContainerというクラスを実装してみました。

※コードはMITライセンスとします

import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';

typedef SizeChangedCallback = void Function(Size size);

class SizeListenableContainer extends SingleChildRenderObjectWidget {
  const SizeListenableContainer({
    Key key,
    Widget child,
    @required this.onSizeChanged,
  })  : assert(onSizeChanged != null),
        super(key: key, child: child);

  final SizeChangedCallback onSizeChanged;

  @override
  _SizeListenableRenderObject createRenderObject(BuildContext context) {
    return _SizeListenableRenderObject(onSizeChanged: onSizeChanged);
  }
}

class _SizeListenableRenderObject extends RenderProxyBox {
  _SizeListenableRenderObject({
    RenderBox child,
    @required this.onSizeChanged,
  })  : assert(onSizeChanged != null),
        super(child);

  final SizeChangedCallback onSizeChanged;

  Size _oldSize;

  @override
  void performLayout() {
    super.performLayout();

    final Size size = this.size;
    if (size != _oldSize) {
      _oldSize = size;
      _callback(size);
    }
  }

  void _callback(Size size) {
    SchedulerBinding.instance.addPostFrameCallback((Duration _) {
      onSizeChanged(size);
    });
  }
}

これを使う側の例は以下の通り。

class HomePage extends StatefulWidget {
  @override
  HomePageState createState() => HomePageState();
}

class HomePageState extends State<HomePage> {
  String _text = "あ";
  Size _size;

  @override
  Widget build(BuildContext context) {
    final String sizeText = _size?.toString() ?? "null";
    return Scaffold(
      appBar: AppBar(
        title: Text("Widget Size Listening"),
      ),
      body: Container(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          children: <Widget>[
            SizeListenableContainer(
              child: Container(
                color: Colors.blue,
                child: Text(
                  _text,
                  style: const TextStyle(color: Colors.white),
                ),
              ),
              onSizeChanged: (Size size) {
                setState(() {
                  _size = size;
                });
              },
            ),
            Text("上のWidgetのSizeは(${sizeText})です"),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          setState(() {
            _text += "あ";
          });
        },
      ),
    );
  }
}

以下のように、Textを含むContainerのサイズを取得できています。

スクリーンショット 2019-07-30 15.05.08.png

onSizeChangedでsetStateするのはよいですが、それによってSizeListenableContainer.childのサイズが変わるとリビルド・ループになるので、使い方は気をつけてください。


なお、サイズを知りたいというより、レイアウトをコントロールしたいというのが目的あれば CustomSingleChildLayoutCustomMultiChildLayout を使います。

najeira
Software developer。Google Cloud(特にApp Engine)とFlutterのGoogle Developers Expert。好きな言語はGo。他Flutter/Dart, Android, iOS, Python。
michael-inc
マイケルはコミュニティサービスをつくる会社、「Community Builder」です。インターネットを通して人生がより豊かになるよう、人がつながる場所をつくりつづけます。
https://michael-inc.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした