👋
FlutterのRepaintBoundaryを使って再描画範囲を分離するのを試してみた
こんにちは、今回はFlutterのWidget、RepaintBoundaryを試してみたので、コードとともに紹介します。
RepaintBoundaryを使うと、子Widgetの描画を分離して、必要のない親Widgetの再描画を防ぐことができます。
ネットで検索しても、意外にシンプルな日本語の比較記事がなかったので、残しておきます。
実験内容
GridViewを使って、3つのケースを試して、再描画範囲がどうなるかを検証
- ケース1: RepaintBoundary使わない
- ケース2: RepaintBoundary使う
- ケース3: RepaintBoundary使う(SizedBox使わない)
再描画は、1秒ごとにTextの文字列をランダムに変えることで起きるようにする。
final cards = [
// ケース1: RepaintBoundary使わない
Container(
color: Colors.grey,
child: Center(
child: SizedBox(
width: 100,
height: 50,
child: Text(displayText.value),
),
),
),
// ケース2: RepaintBoundary使う
Container(
color: Colors.grey,
child: Center(
child: RepaintBoundary(
child: SizedBox(
width: 100,
height: 50,
child: Text(displayText.value),
),
),
),
),
// ケース3: RepaintBoundary使う(SizedBox使わない)
Container(
color: Colors.grey,
child: Center(
child: RepaintBoundary(
child: Text(displayText.value),
),
),
),
];
コード全体例
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class RepaintBoundaryPage extends HookConsumerWidget {
const RepaintBoundaryPage({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final displayText = useState<String>('text0');
useEffect(() {
// 1秒ごとに表示するTextの内容を変えて再描画を起こす
final timer = Timer.periodic(const Duration(seconds: 1), (timer) {
final random = math.Random();
final randomNumber = random.nextInt(10);
displayText.value = 'text$randomNumber';
});
return timer.cancel;
}, []);
final cards = [
// ケース1: RepaintBoundary使わない
Container(
color: Colors.grey,
child: Center(
child: SizedBox(
width: 100,
height: 50,
child: Text(displayText.value),
),
),
),
// ケース2: RepaintBoundary使う
Container(
color: Colors.grey,
child: Center(
child: RepaintBoundary(
child: SizedBox(
width: 100,
height: 50,
child: Text(displayText.value),
),
),
),
),
// ケース3: RepaintBoundary使う(SizedBox使わない)
Container(
color: Colors.grey,
child: Center(
child: RepaintBoundary(
child: Text(displayText.value),
),
),
),
];
return Scaffold(
appBar: AppBar(
title: const Text('RepaintBoundary Page'),
),
body: GridView.builder(
itemCount: cards.length,
itemBuilder: (context, index) {
return cards[index];
},
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 3,
crossAxisSpacing: 3,
),
),
);
}
}
結果
devtoolsのinscepctorのHighlight repaintsの結果が以下の画像です

| ケース | 結果 |
|---|---|
| ケース1: RepaintBoundary使わない | 親WidgetのContainerが再描画されている |
| ケース2: RepaintBoundary使う |
RepaintBoundaryの子のSizedBoxが再描画されている再描画の分離ができている |
| ケース3: RepaintBoundary使う (SizedBox使わない) |
TextとContainerが再描画されている |
よって、RepaintBoudaryを使うことにより、再描画範囲を分離する(ケース2)ことができました。
ただし、ケース3のように、RepaintBoundaryを使っても再描画範囲を分離できない場合もあることがわかりました。
SizedBoxなどを使って、明示的に大きさを分けることが重要なのかな?と思います。
Discussion