👋
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