Что такое Scope в контексте Dependency Injection в Flutter?

«Что такое Scope в контексте Dependency Injection в Flutter?» — вопрос из категории DI, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Scope (область видимости) в DI определяет время жизни и доступность зависимостей. В Flutter это особенно важно для управления ресурсами и предотвращения утечек памяти.

Основные типы scope в Flutter:

  1. Singleton (глобальный scope):
    
    // GetIt пример
    final getIt = GetIt.instance;

getIt.registerSingleton(ApiService()); // Один экземпляр на всё приложение


2. **Widget-level scope (ScopedModel/Provider):**
```dart
// Provider
Provider<MyModel>(
  create: (_) => MyModel(),
  child: MyWidget(),
  // Уничтожается при удалении виджета
)
  1. Riverpod scopes:
    
    // AutoDispose - автоматическое удаление
    final counterProvider = StateProvider.autoDispose<int>((ref) => 0);

// Family - разные экземпляры для разных параметров final userProvider = FutureProvider.family<User, String>((ref, userId) { return fetchUser(userId); });


**Проблемы неправильного scope:**
```dart
// ПЛОХО: Синглтон с контекстом
class BadService {
  BuildContext? context; // Утечка контекста!
}

// ХОРОШО: Локальный scope
class GoodService {
  void doSomething(BuildContext context) {
    // Контекст передается как параметр
  }
}

Best practices для scope в Flutter:

  • Используйте autoDispose в Riverpod для автоматической очистки
  • Избегайте хранения BuildContext в долгоживущих объектах
  • Используйте Disposable миксин для ручного освобождения ресурсов:

    mixin Disposable {
    final List<StreamSubscription> _subscriptions = [];
    
    void dispose() {
    for (final sub in _subscriptions) {
      sub.cancel();
    }
    }
    }

Пример жизненного цикла:

// Инициализация при создании виджета
@override
void initState() {
  super.initState();
  _service = locator<MyService>();
}

// Очистка при удалении
@override
void dispose() {
  _service.dispose();
  super.dispose();
}