【Flutter】UIのイベントをデバウンスする

こんにちは。エキサイト株式会社 モバイルアプリエンジニアの克です。

今回は、FlutterにおいてUIのイベントをデバウンスする手法についてです。

デバウンスについて

デバウンスとは、短時間に複数回のイベントが発生することを防ぐための手法です。
例えばイベントに対して100msのデバウンスを設定する場合、最後のイベントが発火してから100msが経過したタイミングで初めてそのイベントが通知されます。
100msが経過する前に次のイベントが発火した場合は、そのイベントからさらに100msが経過するまで通知は延期されていきます。

似たものとしてスロットリングがありますが、こちらはまず最初に発火したイベントを通知し、そこから一定時間が経過するまでに発火したイベントは無視するものとなります。

用途としては最新のイベントを重視する場合にはデバウンス、時間あたりのイベント数を制限したい場合にはスロットリングというように使い分けます。

まずはUIを実装する

デバウンスを適用するUIですが、今回はPageViewとSliderを連動させ、Sliderの操作時に選択されたページをPageViewに表示するというものにします。
これは画像のビューアーなどでよく使われる構成です。

まずはこれらのUIを実装していきます。
動作イメージとコードは下記の通りです。

class SliderPager extends HookWidget {
  const SliderPager({super.key});

  @override
  Widget build(BuildContext context) {
    final selected = useState(0.0);
    final pageController = usePageController();
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            child: PageView.builder(
              controller: pageController,
              itemBuilder: (_, index) => Center(
                child: Text(index.toString()),
              ),
              itemCount: 100,
            ),
          ),
          Slider(
            value: selected.value,
            onChanged: (value) {
              pageController.jumpToPage(value.ceil());
              selected.value = value;
            },
            max: 100,
          ),
        ],
      ),
    );
  }
}

値の管理をシンプルにするためにflutter_hooksを使用しています。
選択中の位置はhooksを使いselected 変数で管理しています。

UIイベントにデバウンスを設定する

要件が単純な場合はこのままでも何も問題はないかと思います。
ただし、下記のようなケースではデバウンスを設定したほうがいい場合があります。

・アイテムの表示を計測しており、スライダーのシーク中は計測のイベントを送りたくない
・アイテムが表示された際に重い処理を実行しており、連続で実行されると困る

これらのケースを想定して、スライダーをシークした際のイベントにデバウンスを設定するようにします。

今回は公開されているライブラリのeasy_debounceを使用します。
内部的にはTimerを使用してデバウンスを実現しています。

コードを下記のように変更します。

EasyDebounce.debounce(
  'slider-debounce',
  const Duration(milliseconds: 100),
  () => pageController.jumpToPage(value.ceil()),
);

第1引数はデバウンスのタグで、この値を変えることで複数同時にデバウンスを設定することもできます。
第2引数はデバウンスの待機時間です。長くしすぎると操作時に違和感が出てしまうため調整したほうがいいでしょう。
第3引数が実際に実行する処理となります。

注意点として、selectedの更新にはデバウンスを設定しないようにします。
この値はSliderの表示に使用しているため、リアルタイムで更新する必要があります。

この変更による動作イメージは下記となります。

まとめ

デバウンスは便利な場面もありますが、多用するとUXに悪影響を及ぼします。
そのため、要件との折り合いを考えつつ手段の一つとしてスポットで活用するのがよさそうかなと思います。
様々な実装方法を組み合わせて使いやすいアプリを作っていきましょう。

採用情報

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを募集しております。
興味があれば是非ご連絡いただければと思います!

募集職種一覧はこちら www.wantedly.com