Flutter中使用injectable与get_it的性能问题及页面级单例生命周期管理问询
Great question! As your Flutter project scales, managing dependency lifecycles is key to avoiding memory bloat and slow app startup. Here's how you can configure your Blocs to only exist while their corresponding pages are active, and automatically clean up when the page is popped:
1. Use GetIt's Scoped Registrations
GetIt has built-in support for scopes—isolated dependency containers that can be created and destroyed on demand. This is perfect for page-level Blocs:
- Create a scope when navigating to a page
- Register the Bloc inside that scope
- Destroy the scope when the page is popped, which triggers the Bloc's
disposemethod automatically
Step 1: Simplify Your Global $initGetIt
First, trim down your global initialization to only include dependencies that need to exist for the app's entire lifetime. Use lazySingleton for all of them to avoid unnecessary startup overhead:
_i1.GetIt $initGetIt(_i1.GetIt get, {String? environment, _i2.EnvironmentFilter? environmentFilter}) { final gh = _i2.GetItHelper(get, environment, environmentFilter); // Lazy-load global dependencies to reduce startup delay gh.lazySingleton<_i3.DioConfig>(() => _i3.DioConfig()); gh.lazySingleton<_i4.DioMethod>(() => _i4.DioMethod(get<_i3.DioConfig>())); gh.lazySingleton<_i5.MovieApiProvider>(() => _i5.MovieApiProvider(get<_i4.DioMethod>())); gh.lazySingleton<_i6.Repository>(() => _i6.Repository()); return get; }
Step 2: Manage Bloc Scopes in Page Widgets
For each page, create a scope when the page initializes and dispose it when the page is destroyed. Here's an example for MoviesPage:
class MoviesPage extends StatefulWidget { const MoviesPage({super.key}); @override State<MoviesPage> createState() => _MoviesPageState(); } class _MoviesPageState extends State<MoviesPage> { late MoviesBloc _moviesBloc; late GetItScope _pageScope; @override void initState() { super.initState(); // Create a dedicated scope for this page _pageScope = getIt.createScope( 'movies_page_scope', init: (scope) { // Register the Bloc with a dispose callback scope.registerLazySingleton<MoviesBloc>( () => MoviesBloc(getIt<_i6.Repository>()), dispose: (bloc) => bloc.dispose(), ); }, ); // Retrieve the Bloc from the scope _moviesBloc = getIt<MoviesBloc>(instanceName: 'movies_page_scope'); } @override void dispose() { // Destroy the scope to clean up the Bloc _pageScope.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return BlocProvider.value( value: _moviesBloc, child: Scaffold( // Your page content here ), ); } }
Step 3: (Optional) Use a Route Observer for Cleaner Code
To avoid repeating scope logic in every page, create a RouteObserver to automatically manage scopes when pages are pushed/popped:
class BlocScopeObserver extends RouteObserver<PageRoute<dynamic>> { @override void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPush(route, previousRoute); if (route.settings.name == '/movies') { // Create scope and register Bloc when pushing the page final scope = getIt.createScope( 'movies_scope', init: (s) => s.registerLazySingleton<MoviesBloc>( () => MoviesBloc(getIt<_i6.Repository>()), dispose: (b) => b.dispose(), ), ); // Store the scope in route arguments for later access route.settings = route.settings.copyWith(arguments: scope); } } @override void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPop(route, previousRoute); if (route.settings.name == '/movies') { // Dispose the scope when popping the page final scope = route.settings.arguments as GetItScope; scope.dispose(); } } }
Add this observer to your MaterialApp:
MaterialApp( navigatorObservers: [BlocScopeObserver()], routes: { '/movies': (context) => const MoviesPage(), // Other routes }, )
Then retrieve the Bloc in your page without manual scope management:
class MoviesPage extends StatelessWidget { const MoviesPage({super.key}); @override Widget build(BuildContext context) { final scope = ModalRoute.of(context)!.settings.arguments as GetItScope; final moviesBloc = getIt<MoviesBloc>(instanceName: scope.name); return BlocProvider.value( value: moviesBloc, child: Scaffold(/* Page content */), ); } }
Key Benefits of This Approach
- Reduced Memory Usage: Blocs are only kept in memory while their page is active, and cleaned up immediately after the page is popped.
- Faster Startup: Global dependencies are lazy-loaded, so only necessary objects are initialized when the app starts.
- Maintainable Code: Your
$initGetItstays lean, with page-specific dependencies managed close to where they're used.
内容的提问来源于stack exchange,提问作者Vu Thanh

