Flutter 3.29で追加されたbackdropGroupKey, BackdropFilter.groupedのまとめ
はじめに
株式会社Sally 所属エンジニアの @wellPicker です。
弊社では、スマホやパソコンでマーダーミステリーを遊べるアプリであるウズや、マダミス情報・予約管理サイトマダミス.jp、マダミス開発ツールウズスタジオを開発しています。
Flutter 3.29 において、BackdropFilterクラスの新たなプロパティであるbackdropGroupKey,および新たなコンストラクタであるBackdropFilter.groupedが追加され、BackdropFilterのパフォーマンスが改善されました。
ウズ内でもBackdropFilterを使用している箇所は多数あり、今回のアップデートに伴ってこれらの情報について調べる機会がありました。
そこで本記事では、この新機能の概要や使用方法について詳しく解説していきます。
そもそもBackdropFilterとは何か?
BackdropFilterは、子要素の背景に対してブラー効果や色調変更などのフィルターをかける際に使用するWidgetです。エフェクトがかかる範囲は、BackdropFilterの祖先に存在するClipによって指定できます。
子要素そのものにはフィルターがかからない点や、Clipしない場合は全画面にフィルターが広がってしまう点など、慣れていない人にとっては少し意外な挙動かもしれません。下記のサンプルコードは、Flutter の新規プロジェクトの実装を改変したものなので、そもそもBackdropFilterに慣れていない人はこちらのコードから実際の挙動を確認してみましょう。
...
body: Center(
child: Stack(
children: [
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
Center(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
width: 150,
height: 150,
child: Text(
'backdrop filter',
style: TextStyle(fontSize: 20, color: Colors.green),
),
),
),
),
),
],
),
),
...
BackdropFilterが抱えていた問題点
Flutter3.29 よりも前のバージョンでは、複数の BackdropFilter を使用する場合、それぞれのフィルターを個別に計算するため、例えばblurを大量に使用すると描画時間が遅くなるという問題点がありました。
ウズ内でも、ブラーがかかったコンポーネントを複数並べている箇所があり、上記の問題を改善する必要がありました。特に、Flutter の新しいレンダリングエンジンである Impeller を使用した際にこの問題が顕著に発生していたため、GitHub に issue をあげて Flutter チームともやりとりをした結果、この後話す BackdropFilter.grouped による解決につながりました。
backdropGroupKey, BackdropFilter.groupedについて
Flutter 3.29では、BackdropFilterクラスに新たにbackdropGroupKeyというプロパティが追加されたほか、BackdropFilter.groupedというコンストラクタが導入されました。
backdropGroupKey: 同じキーを持つBackdropFilterは、一括でフィルターエフェクトの計算が行われます。
BackdropFilter.grouped: BackdropGroup という専用のラッパーの配下で使用します。BackdropGroup も Flutter 3.29 で新たに追加されたクラスで、同じ BackdropGroup を祖先にもつ BackdropFilter.grouped のインスタンスは、一括でフィルターエフェクトの計算が行われます。
複数の要素に同じ背景フィルターを適用する場合、以下のように実装することですべてのBackdropFilter の計算を一度で済ませることができます。
基本的にはBackdropGroupの配下でBackdropFilter.groupedを使う方法が推奨されますが、特定のケースで柔軟にフィルターの計算範囲を指定したい場合には、明示的にbackdropGroupKeyを指定することも可能です。
BackdropFilter.grouped の使用例
...
return BackdropGroup(
child: ListView.builder(
itemCount: 60,
itemBuilder: (BuildContext context, int index) {
return ClipRect(
child: BackdropFilter.grouped(
filter: ui.ImageFilter.blur(
sigmaX: 5,
sigmaY: 5,
),
child: Container(
color: Colors.black.withValues(alpha: 0.2),
height: 50,
child: const Text('Blur item'),
),
),
);
}
),
);
...
backdropGroupKeyの使用例
...
final _key = BackdropKey();
return Column(
children: [
BackdropFilter(
backdropGroupKey: _key,
filter: ui.ImageFilter.blur(
sigmaX: 5,
sigmaY: 5,
),
child: Container(
color: Colors.black.withValues(alpha: 0.2),
height: 50,
child: const Text('Blur item 1'),
),
),
BackdropFilter(
backdropGroupKey: _key,
filter: ui.ImageFilter.blur(
sigmaX: 5,
sigmaY: 5,
),
child: Container(
color: Colors.black.withValues(alpha: 0.2),
height: 50,
child: const Text('Blur item 2'),
),
),
],
);
...
上記の実装を元にサンプルコードを実装して、dev tools でレンダリングのパフォーマンスを確認したところ、BackdropGroupを使用しなかった場合と比べて、BackdropGroupを使用した場合は frame time が実際に大きく減少している様子を確認できました。
(BackdropGroupを使用しなかった場合)
(BackdropGroupを使用した場合)
まとめ
Flutter 3.29 の backdropGroupKey、および BackdropFilter.grouped の導入により、BackdropFilterのパフォーマンスが改善されました。
UIのリッチ化とパフォーマンスの向上を実現するために、これらの機能について知っておいて損はないと思います。
今回の記事が皆さんのアプリ開発に役立てば幸いです。
Discussion