mcpField
F
flutter-di-get-it

flutter-di-get-it

github.com/junsuk5開発2444 回インストール24 週間前に更新
リポジトリ

flutter-di-get-it について

Flutter プロジェクト向けの get_it 依存注入ガイドです。単一の登録入口で依存順にセットアップし、Root ウィジェットから ViewModel を取得する規則を示します。画面間で ViewModel を共有しないようファクトリー登録を用います。

主な機能

  1. 01依存登録の入口を一元化
  2. 02依存順にオブジェクトを登録
  3. 03シングルトンとファクトリー登録の使い分け
  4. 04Root ウィジェットだけで ViewModel を取得
  5. 05インターフェース型による依存注入

活用シーン

  1. 01Flutter アプリの依存注入を設定
  2. 02データソース、Repository、UseCase を登録
  3. 03画面を開くたびに新しい ViewModel を生成
  4. 04Screen ウィジェットのテストやプレビューを容易にする

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/junsuk5/survival-flutter-skills --skill flutter-di-get-it

インストール後、Claude Code で/flutter-di-get-itを入力して読み込まれたか確認します。

詳細

Flutter DI — get_it

原則

  • DIは単一のエントリポイントである lib/core/di/di_setup.dart の diSetup() 関数で登録します。Featureごとに分散させません(単一パッケージ構成のため)。
  • main() では、runApp() を呼び出す前に必ず diSetup() を呼び出します。
  • Rootウィジェットでのみ getIt<T>() を使ってViewModelを取得します。Screenウィジェットや共通ウィジェットでは、絶対に getIt を使いません(テストしやすくするため)。
  • ViewModelは必ず registerFactory で登録します。シングルトンにすると、複数の画面で同じ状態を共有してしまいます。

基本のエントリポイント

// lib/core/di/di_setup.dart
final getIt = GetIt.instance;

void diSetup() {
  // 1) data source
  // 2) repository
  // 3) use case
  // 4) view model
}
// lib/main.dart
void main() {
  diSetup();
  runApp(const MyApp());
}

登録順序は、依存関係グラフのリーフから上へと進めます。DataSource → Repository → UseCase → ViewModelの順に登録すれば、getIt() から参照するときには、すでに登録されています。


レイヤーごとの登録パターン

DataSource(シングルトン)
getIt.registerSingleton<RecipeDataSource>(RemoteRecipeDataSourceImpl());
getIt.registerSingleton<LocalStorage>(DefaultLocalStorage());

インターフェース型で登録し、Root/ViewModelから実装クラスを直接参照しないようにします。

Repository(シングルトン)

コンストラクターパラメーターは getIt() で解決して渡します。ジェネリック型推論が必要な場合は、明示的に getIt<Foo>() と記述します。

getIt.registerSingleton<RecipeRepository>(
  MockRecipeRepositoryImpl(
    recipeDataSource: getIt(),
  ),
);
getIt.registerSingleton<BookmarkRepository>(MockBookmarkRepositoryImpl());
UseCase(シングルトン)

状態を持たない純粋な機能なので、シングルトンで十分です。型パラメーターは省略できます(実装クラスが1つだけなので、Dartが推論します)。

getIt.registerSingleton(
  GetSavedRecipesUseCase(
    recipeRepository: getIt(),
    bookmarkRepository: getIt(),
  ),
);
ViewModel(ファクトリー)

必ず registerFactory を使います。 画面を再度開いたときに、新しいインスタンスが必要になるためです。

getIt.registerFactory<IngredientViewModel>(
  () => IngredientViewModel(
    ingredientRepository: getIt(),
    procedureRepository: getIt(),
    getDishesByCategoryUseCase: getIt(),
    clipboardService: getIt(),
  ),
);

Rootウィジェットでの注入

class SavedRecipesRoot extends StatelessWidget {
  const SavedRecipesRoot({super.key});

  @override
  Widget build(BuildContext context) {
    final viewModel = getIt<SavedRecipesViewModel>();
    return ListenableBuilder(
      listenable: viewModel,
      builder: (context, _) => SavedRecipesScreen(
        recipes: viewModel.state.recipes,
        onAction: viewModel.onAction,
      ),
    );
  }
}

原則:getIt<>() の呼び出しは、*Root ウィジェットの build() 内でのみ行います。Screenウィジェットは state と onAction のみを受け取るようにし、プレビューやテストができる状態を保ちます。


シングルトンとファクトリー

登録関数使用する場面例
registerSingleton<T>(instance)アプリのライフサイクルを通じて1つのインスタンスのみ必要な場合。生成済みのオブジェクトをそのまま渡す場合Repository、DataSource、UseCase、HttpClient
registerLazySingleton<T>(() => ...)初回アクセス時まで生成を遅らせたい場合初期化コストの高いオブジェクト
registerFactory<T>(() => ...)呼び出すたびに新しいインスタンスが必要な場合ViewModel

このプロジェクトの実例(lib/core/di/di_setup.dart)では、Repository/DataSource/UseCaseには registerSingleton、ViewModelには registerFactory を使い、一貫して分けています。このルールに従ってください。


ファクトリーメソッドが必要な場合

コンストラクターだけでは完結せず、ファクトリーメソッドを呼び出す必要がある場合は、ラムダ形式を使います。

getIt.registerSingleton<HttpClient>(HttpClientFactory.create(getIt()));

名前付きパラメーターが必要な場合も、同様にラムダ形式を使います。


チェックリスト — 新しいfeatureを追加する際のDI登録

  • DataSourceインターフェースが domain または data/data_source/ に定義されている
  • 実装クラスを registerSingleton<Interface> で登録する(インターフェース型を明示)
  • Repositoryのインターフェースと実装を登録する
  • UseCaseを登録する(状態を持たない場合はシングルトン)
  • ViewModelは必ず registerFactory
  • Rootウィジェットでのみ getIt<ViewModelType>() を使って取得する

アンチパターン

  • ❌ Screenウィジェット、共通ウィジェット、またはStatelessWidgetのツリーの深い位置で getIt<T>() を呼び出す → テストやプレビューができなくなります。
  • ❌ ViewModelを registerSingleton で登録する → 画面に再度入ったときに状態が汚染されます。
  • ❌ diSetup() の外で getIt.register* をひそかに呼び出す → 登録順序と依存関係グラフが分散します。
  • ❌ 実装クラス型で getIt から取得する(getIt<MockRecipeRepositoryImpl>())→ インターフェース型で取得し、実装を置き換えられるようにしてください。

コメント