Beroendeinjektion med get_it och injectable
Registrera och slå upp beroenden med get_it-service locate och kodgenerering via injectable.
Beroendeinjektion med get_it och injectable är en gratis lektion i Mobilutveckling med Flutter på CoddyKit. Detta är lektion 2 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Mobilutveckling med Flutter, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.
Varför en Service Locator?
I en ren, modulär Flutter-app vill ni att lagret presentation ska vara beroende av abstraktioner, inte av de konkreta klasser som skapar dem. Om objekt konstrueras manuellt (RemoteApi(HttpClient(...))) överallt läcker konstruktionsdetaljer ut och testning blir besvärlig.
En service locator centraliserar skapande och hämtning av objekt. get_it är den de facto-standardiserade service locatern för Dart/Flutter: ni registrerar hur en typ ska skapas en gång och hämtar den sedan var som helst med ett enda anrop.
- Frikoppling — widgets efterfrågar ett gränssnitt, inte en konstruktor.
- Testbarhet — byt ut en riktig implementation mot en fejk i tester.
- Livscykelkontroll — singleton eller en ny instans per förfrågan.
GetIt-instansen
GetIt exponerar en global singleton via GetIt.instance (vanligtvis aliasad som getIt eller sl). Ni kan också skapa isolerade instanser med GetIt.asNewInstance() för tester.
Det vanliga Dart-API:t motsvarar det som kodgenereringen senare kommer att producera, så det lönar sig att förstå det först. Nedan visas locator-mönstret med vanliga klasser — Flutter behövs inte.
// A tiny hand-rolled service locator to show the idea.
class Locator {
final _factories = <Type, Object Function()>{};
final _singletons = <Type, Object>{};
void registerFactory<T>(T Function() create) =>
_factories[T] = () => create() as Object;
void registerSingleton<T>(T instance) =>
_singletons[T] = instance as Object;
T get<T>() {
if (_singletons.containsKey(T)) return _singletons[T] as T;
final f = _factories[T];
if (f == null) throw StateError('No registration for $T');
return f() as T;
}
}
class ApiClient {
final String baseUrl;
ApiClient(this.baseUrl);
}
void main() {
final sl = Locator();
sl.registerSingleton<ApiClient>(ApiClient('https://api.example.com'));
final api = sl.get<ApiClient>();
print('Resolved ApiClient -> ${api.baseUrl}');
}Registreringarnas livslängd
get_it erbjuder tre centrala registreringstyper. Att välja rätt är det viktigaste arkitekturbeslutet:
registerFactory<T>()— kör buildern varje gång ni hämtar typen. Använd för kortlivade objekt med tillstånd (t.ex. en ny BLoC per skärm).registerSingleton<T>(instance)— ni tillhandahåller en färdig instans; den skapas direkt vid uppstart.registerLazySingleton<T>()— skapas första gången den hämtas och cachas sedan. Idealisk för repositories och API-klienter som inte behövs förrän de används.
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
void configureDependencies() {
getIt.registerLazySingleton<Dio>(() => Dio());
getIt.registerLazySingleton<AuthRemoteSource>(
() => AuthRemoteSource(getIt<Dio>()),
);
getIt.registerFactory<LoginBloc>(
() => LoginBloc(getIt<AuthRemoteSource>()),
);
}Registrera mot abstraktioner
Clean Architecture säger att domänlagret definierar ett gränssnitt (t.ex. AuthRepository) och att datalagret implementerar det (AuthRepositoryImpl). Registrera den abstrakta typen som generic-typ och returnera den konkreta implementationen från buildern.
Konsumenter hämtar getIt<AuthRepository>() och behöver aldrig känna till implementationen — ni kan byta ut den utan att ändra anropsställena.
abstract class AuthRepository {
Future<String> login(String email, String password);
}
class AuthRepositoryImpl implements AuthRepository {
final AuthRemoteSource remote;
AuthRepositoryImpl(this.remote);
@override
Future<String> login(String email, String password) =>
remote.authenticate(email, password);
}
void registerRepositories() {
// Generic is the ABSTRACTION, builder returns the IMPL.
getIt.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(getIt<AuthRemoteSource>()),
);
}Hämta beroenden i widgets
Från valfri widget anropar ni locatern direkt. Eftersom BLoCs vanligtvis registreras med registerFactory får varje skärm en ny instans, som ni avregistrerar tillsammans med widgeten.
Ett vanligt mönster är att hämta den factory-skapade BLoC:en i BlocProvider(create: ...), medan repositories och klienter förblir lata singletoner som delas i hela appen.
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider<LoginBloc>(
// Fresh BLoC from the factory registration.
create: (_) => getIt<LoginBloc>(),
child: const _LoginView(),
);
}
}Nu kommer injectable
Att skriva configureDependencies() för hand blir ohanterligt i en stor, modulär app. injectable är en kodgenerator som söker igenom annotationer och genererar registreringskoden åt er samt kopplar automatiskt ihop konstruktorparametrar.
Ni behöver tre beroenden för utveckling och körning:
injectable— annotationerna.get_it— den runtime-locator som den riktar sig mot.injectable_generator+build_runner— kodgenerering som endast behövs under utveckling.
# pubspec.yaml
dependencies:
get_it: ^7.7.0
injectable: ^2.4.0
dev_dependencies:
build_runner: ^2.4.0
injectable_generator: ^2.6.0Annotationen @injectable
Annotera en klass med @injectable, så registrerar generatorn den som en factory. Konstruktorparametrarna hämtas rekursivt från locatern, så ni behöver aldrig koppla in dem manuellt.
För att binda ett gränssnitt till en implementation annoterar ni implementationen och använder @Injectable(as: AuthRepository) — generic-registreringen blir då abstraktionen.
import 'package:injectable/injectable.dart';
@injectable
class LoginBloc {
final AuthRepository repository;
LoginBloc(this.repository); // injected automatically
}
@Injectable(as: AuthRepository)
class AuthRepositoryImpl implements AuthRepository {
final AuthRemoteSource remote;
AuthRepositoryImpl(this.remote);
// ...
}Singletoner och lata singletoner
injectable motsvarar get_its livslängder genom annotationer:
@singleton— eager singleton, skapas när DI konfigureras.@lazySingleton— skapas första gången den hämtas och cachas sedan.@injectable— factory (en ny instans varje gång).
Välj @lazySingleton för repositories, datakällor och klienter; @injectable för BLoCs/Cubits som är begränsade till en skärm.
@lazySingleton
class AuthRemoteSource {
final Dio dio;
AuthRemoteSource(this.dio);
}
@singleton
class AppConfig {
final String environment;
AppConfig() : environment = const String.fromEnvironment('ENV');
}Tredjepartstyper med @module
Ni kan inte annotera klasser som ni inte äger (Dio, SharedPreferences). En registreringsmodul löser detta: deklarera en abstrakt klass annoterad med @module och exponera getters/metoder som skapar dessa typer.
Asynkrona beroenden (som SharedPreferences.getInstance()) returnerar en Future och registreras som @preResolve, så att DI väntar på dem vid uppstart.
import 'package:injectable/injectable.dart';
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
@module
abstract class RegisterModule {
@lazySingleton
Dio get dio => Dio(BaseOptions(baseUrl: 'https://api.example.com'));
@preResolve
Future<SharedPreferences> get prefs => SharedPreferences.getInstance();
}Generera och koppla configureDependencies
Skapa en enda startpunkt annoterad med @InjectableInit. Kör generatorn, så skapar den *.config.dart som innehåller init(getIt), vilket ni anropar från er funktion.
Generera med:
dart run build_runner build --delete-conflicting-outputs
Anropa sedan configureDependencies() före runApp. När det finns beroenden med @preResolve är funktionen async och måste inväntas med await.
import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';
import 'injection.config.dart'; // generated
final getIt = GetIt.instance;
@InjectableInit(
initializerName: 'init',
preferRelativeImports: true,
asExtension: true,
)
Future<void> configureDependencies() => getIt.init();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await configureDependencies();
runApp(const MyApp());
}Miljöer och testöverskrivningar
Vid testning ersätter ni riktiga registreringar med fejkade. Med vanlig get_it anropar ni getIt.unregister<T>() och registrerar sedan en mock, eller använder en ny GetIt.asNewInstance(). Med injectable väljer annotationen @Environment (t.ex. @dev, @test) implementationer per miljö som skickas till init.
Mönstret nedan visar idén med testöverskrivning i vanlig Dart, så att det kan köras fristående.
abstract class Clock {
DateTime now();
}
class SystemClock implements Clock {
@override
DateTime now() => DateTime.now();
}
class FixedClock implements Clock {
final DateTime fixed;
FixedClock(this.fixed);
@override
DateTime now() => fixed;
}
void main() {
final registry = <Type, Object>{};
void register<T>(T impl) => registry[T] = impl as Object;
T resolve<T>() => registry[T] as T;
register<Clock>(SystemClock());
// Override for a deterministic test:
register<Clock>(FixedClock(DateTime.utc(2030, 1, 1)));
print('Test clock now: ${resolve<Clock>().now()}');
}Snabb kontroll
Ni har ett UserRepository som använder en Dio-HTTP-klient som delas i hela appen, samt en ProfileCubit som innehåller UI-tillstånd per skärm och måste avregistreras när skärmen stängs. Vilka injectable-annotationer passar bäst för respektive komponent?
Sammanfattning
Ni vet nu hur beroenden kopplas in i en modulär Flutter-app:
- get_it är service locatern vid körning —
registerFactory,registerSingletonochregisterLazySingletonstyr livslängden. - Registrera mot abstraktioner (
registerLazySingleton<AuthRepository>) så att anropsställena förblir frikopplade från implementationerna. - injectable genererar registreringskoden från annotationer:
@injectable(factory),@lazySingleton,@singletonoch@Injectable(as: ...)för att binda gränssnitt. - Använd
@moduletillsammans med@preResolveför tredjeparts- och asynkrona beroenden somDioochSharedPreferences. @InjectableInit+build_runnerskapargetIt.init(); inväntaconfigureDependencies()förerunApp.
Tumregel: lata singletoner för repositories/klienter, factories för BLoCs/Cubits som är begränsade till en skärm.
Lär dig Dart med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 22
- Lektioner
- 88
Vanliga frågor
Är lektionen ”Beroendeinjektion med get_it och injectable” gratis?
Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Mobilutveckling med Flutter, inklusive ”Beroendeinjektion med get_it och injectable”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.
Vad lär jag mig i ”Beroendeinjektion med get_it och injectable”?
Registrera och slå upp beroenden med get_it-service locate och kodgenerering via injectable. Ni övar på Mobilutveckling med Flutter med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Mobilutveckling med Flutter?
Du behöver inga förkunskaper. Utbildningen i Mobilutveckling med Flutter på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.
Hur lång tid tar lektionen ”Beroendeinjektion med get_it och injectable”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Mobilutveckling med Flutter-lektionen?
Ja. Varje Mobilutveckling med Flutter-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Gränser mellan domän-, data- och presentationslager
- Beroendeinjektion med get_it och injectable
- Funktionsbaserad mappstruktur och Melos-monorepon
- Either, feltyper och funktionell felhantering