flutter_hooksのuseEffectを使う際の注意点

2 min read読了の目安(約2100字

flutter_hooksを使いたかった

状態管理パッケージのRiverpodを導入しました。
同時に併用を推奨されているflutter_hooksも使い始めました。

useEffect

一度のみ実行したい場合や開放処理をする時に便利です。
初期化処理などでよく使うと思います。

ソースコード

  • State
class SampleStateController extends StateNotifier<SampleState> {
  SampleStateController() : super(SampleState());
  Future<void> getPackage() async {
    final package = await sample.getPackage(hogehogekey);
    state = state.copyWith(package: package);
  }
}
  • View
final sampleProvider StateNotifierProvider((ref) => SampleStateController());

class HooksSample extends HookWidget {
  Widget build(BuildContext context) {
    final sample = useProvider(sampleProvier);
    useEffect(() {
      sample.getPackage();
      return sample.dispose;
    }, const []);
    return Container(
        child: ...
    );
  }
}

問題

useEffectの中でStateをかえる処理をすると下記のエラーが出てしまいました。

E/flutter (32049): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: setState() or markNeedsBuild() called during build.
E/flutter (32049): This UncontrolledProviderScope widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.

ビルド中にStateを変えてはいけませんと怒られました。

じゃあビルドが終わってからStateを変えれば問題ないんでしょ?

WidgetsBinding.instance.addPostFrameCallback()を使えばクリアできそうです。
Widgetが描画されてから処理を実行してくれる便利なヤツ。
ソースコード

class HooksSample extends HookWidget {
  Widget build(BuildContext context) {
    final sample = useProvider(sampleProvier);
    useEffect(() {
      WidgetsBinding.instance.addPostFrameCallback((_) {
      	sample.getPackage();
      });
      return sample.dispose;
    }, const []);
    return Container(
        child: ...
    );
  }
}

無事解決

flutterもdartもまだまだ勉強中ですが、誰かの役に立てばと思い、書きました。
間違っているところや、もっとこうした方が良い等ありましたら、教えてくださると幸いです。
ありがとうございました。