You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中使用injectable与get_it的性能问题及页面级单例生命周期管理问询

Solution: Scoped Dependencies with GetIt for Page-Lifecycle Blocs

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 dispose method 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 $initGetIt stays lean, with page-specific dependencies managed close to where they're used.

内容的提问来源于stack exchange,提问作者Vu Thanh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 20:37:49