Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use SharedPreferences and Injectable in Flutter?

Im using the library Injectable for Dependency Injection in flutter but Im getting a error where I cannot use SharedPreferences.

Error: Exception has occurred. FlutterError (ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. If you're running an application and need to access the binary messenger before runApp() has been called (for example, during plugin initialization), then you need to explicitly call the WidgetsFlutterBinding.ensureInitialized() first. If you're running a test, you can call the TestWidgetsFlutterBinding.ensureInitialized() as the first line in your test's main() method to initialize the binding.) I've tryed creating a class and put @lazySingleton

  Future<SharedPreferences> get prefs => SharedPreferences.getInstance();

and I tryed to put WidgetsFlutterBinding.ensureInitialized()

void main() { 
  WidgetsFlutterBinding.ensureInitialized();
  configureInjection(Environment.prod);
  runApp(MyApp());
}
like image 778
Silver Qui. Avatar asked Sep 01 '25 02:09

Silver Qui.


2 Answers

you can pre-await the future in SharedPreference by annotating with @preResolve

@module
abstract class InjectionModule {

//injecting third party libraries
   @preResolve
   Future<SharedPreferences> get prefs => SharedPreferences.getInstance();
}

and on the configureInjection class

final GetIt getIt = GetIt.instance;

@injectableInit
Future<void> configureInjection(String env) async {
 await $initGetIt(getIt, environment: env);
}

and also on the main class

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await configureInjection(Environment.prod);
 runApp(MyApp());
}

To actually use:

final prefs = getIt<SharedPreferences>();
await prefs.setString('city', city);

NOT:

final module = getIt<InjectionModule>();
module.prefs.setString('test', test);

Note differences between SharedPreferences and InjectionModule.

like image 169
Davii The King Avatar answered Sep 02 '25 19:09

Davii The King


Configure your pubspec.yaml:

dependencies:
  // Our service locator
  get_it: ^7.6.7
  shared_preferences: ^2.2.3
  // Generates code for us by providing annotations we can use
  injectable: 2.3.5


dev_dependencies:
  flutter_test:
    sdk: flutter
  analyzer: ^6.2.0
  injectable_generator:
  build_runner:

Create your module class shared_preferences.module.dart (Create a folder usually called 'di' in your project structure and place this class there):

import 'package:injectable/injectable.dart';
import 'package:shared_preferences/shared_preferences.dart';

@module
abstract class SharedPreferencesModule {

  @preResolve
  @injectable
  Future<SharedPreferences> get sharedPreferences => SharedPreferences.getInstance();

}

Now you can inject it by constructor wherever you need it, in my case:

import 'package:injectable/injectable.dart';
import 'package:shared_preferences/shared_preferences.dart';

@injectable
class SharedPreferenceUtils {
  
  SharedPreferences sharedPreferences;

  SharedPreferenceUtils({
    required this.sharedPreferences
  });

  Future<bool> saveData({required String key, required dynamic value}) async {
    if (value is String) {
      return await sharedPreferences.setString(key, value);
    } else if (value is int) {
      return await sharedPreferences.setInt(key, value);
    } else if (value is double) {
      return await sharedPreferences.setDouble(key, value);
    } else if (value is bool) {
      return await sharedPreferences.setBool(key, value);
    } else if (value is List<String>) {
      return await sharedPreferences.setStringList(key, value);
    }
    return await Future.value(false);
  }

  Object? getData({required String key}) {
    return sharedPreferences.get(key);
  }

  Future<bool> removeData({required String key}) async {
    return await sharedPreferences.remove(key);
  }
}

Now create a file to initialize get_it and the dependencies (It can also be in your 'di' folder) yo llamé al archvio injection_container.dart:

import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';

final getIt = GetIt.instance;

@InjectableInit()
Future<void> configureDependencies() async => getIt.init();

Remember in your main to ensure that your dependencies are initialized:

import 'package:flutter/material.dart';
import 'package:your_project/core/injection/injection_container.dart';
import 'package:your_project/features/main/common_main.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await configureDependencies();
  runApp(MyApp());
}

Finally run the following command:

flutter pub run build_runner build --delete-conflicting-outputs

This will create a class injection_container.config.dart al mismo nivel de tu clase injection_container.dart, which will take care of initializes the registration of main-scope dependencies inside of GetIt and it will be imported automatically, resolving errors you have in your injection_container.dart class.

like image 22
Andres Camilo Cortes Avatar answered Sep 02 '25 20:09

Andres Camilo Cortes