Initialization topic

Initialization

Early initialization

By default, all providers are initialized lazily.

You can also tell Refena to initialize some providers right away by setting the initialProviders parameter:

void main() {
  runApp(
    RefenaScope(
      initialProviders: [
        databaseProvider,
      ],
      child: const MyApp(),
    ),
  );
}

Overriding providers

All providers are initialized synchronously.

If one of your providers needs to do some asynchronous work, you will need to override it at startup.

final persistenceProvider = Provider<PersistenceService>((ref) => throw 'Not initialized');

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final PersistenceService persistenceService = await initPersistenceService();

  runApp(RefenaScope(
    overrides: [
      persistenceProvider.overrideWithValue(persistenceService),
    ],
    child: const MyApp(),
  ));
}

You can have multiple overrides depending on each other.

The override order is important: An exception will be thrown on app start if you reference a provider that is not yet initialized.

If you have at least one Provider with overrideWithFuture, you should await the initialization with scope.ensureOverrides().

scope.ensureOverrides() returns a Future, so you can also show a loading indicator while the app is initializing.

final persistenceProvider = Provider<PersistenceService>((ref) => throw 'Not initialized');
final apiProvider = Provider<ApiService>((ref) => throw 'Not initialized');

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final scope = RefenaScope(
    overrides: [
      // order is important
      persistenceProvider.overrideWithFuture((ref) async {
        final prefs = await SharedPreferences.getInstance();
        return PersistenceService(prefs);
      }),
      apiProvider.overrideWithFuture((ref) async {
        // uses persistenceService from above
        final persistenceService = ref.read(persistenceProvider);
        final anotherService = await initAnotherService();
        return ApiService(persistenceService, anotherService);
      }),
    ],
    child: const MyApp(),
  );

  runApp(scope);
}

You can also override providers on demand:

// Access container for advanced use cases
RefenaContainer container = ref.container;

// Override the provider
container.set(persistenceProvider.overrideWithValue(persistenceService));

Explicit container

Since RefenaScope just creates an implicit container in the background, you can also create an explicit container and pass it to the RefenaScope.

This is useful for more advanced initialization scenarios before the first frame.

void main() async {
  final container = await init();

  runApp(
    RefenaScope.withContainer(
      container: container, // pass the container
      child: const MyApp(),
    ),
  );
}

Future<RefenaContainer> init() async {
  WidgetsFlutterBinding.ensureInitialized();

  final ref = RefenaContainer(
    overrides: [
      persistenceProvider.overrideWithFuture(getPersistenceService()),
    ],
  );

  // Optional if you have overrideWithFuture
  await ref.ensureOverrides();

  // Some initialization logic
  ref.read(persistenceProvider).init();
  ref.read(analyticsService).appStarted();
  ref.redux(profileProvider).dispatchAsync(InitProfileAction());

  return ref;
}

Classes

RefenaContainer Introduction Riverpod Initialization
The RefenaContainer holds the state of all providers. Every provider is initialized lazily.