scope_manager 0.1.1 copy "scope_manager: ^0.1.1" to clipboard
scope_manager: ^0.1.1 copied to clipboard

A package that helps with managing dependency scopes in the application

Scope Manager #

Scope Manager Logo

Owner Pub Version Pub points Pub Likes Downloads Coverage Status Contributors License


Overview #

Scope Manager is a lightweight lifecycle manager for dependency scopes in Flutter apps. It helps you group and control dependencies by lifetime (app-wide vs. feature-specific), automatically creating groups of dependencies when needed and disposing them when they’re no longer used.

Playground #

Actual release demo Upcoming release demo

Core concepts #

DependencyScope #

DependencyScope is a group of dependencies that share the same lifecycle. There are two types of scopes in this package:

  • RootScope: lives throughout the entire app lifecycle. Usually contains dependencies that should always be alive. This is a single-instance scope for the whole application; it supports asynchronous initialization and is never destroyed.

  • FeatureScope: created on demand and destroyed when no longer needed. A group of dependencies that share similar temporal reasoning for existence: an authenticated application zone, or part of a specific feature. An application can have multiple types of this scope related to various features, many instances of the same scope, or none at all.

There are no requirements for how to implement a scope. You can create a simple class with a manual implementation or use any third‑party library.

ScopeManager #

ScopeManager is the central class responsible for managing the existence of scopes. It holds and analyzes subscriptions to specific scopes and, based on stakeholder existence, creates and destroys scopes.

The rules are simple:

  • When the first subscription to a specific scope type is added, the manager creates a new instance of this scope.

  • When the last subscription to a specific scope type is removed, the manager destroys this scope instance.

How to use #

In this section, you'll find a step-by-step guide to integrating Scope Manager into your Flutter application.

Create RootScope #

Create a class that implements the RootScope interface. This class will hold all your app-wide dependencies. You can implement this class in any way you prefer—even using a third‑party library—but in this example it’s the most straightforward: a simple Dart class that uses only language capabilities.

/// App-level dependencies.
abstract interface class IAppScope implements RootScope {
  /// Environment configuration.
  Environment get env;

  /// Navigation manager.
  Coordinator get coordinator;

  /// Authentication service for managing user sessions.
  IAuthService get authService;

  /// Returns a new instance of [Foo] all the time.
  Foo fooFactory();
}

/// Scope of dependencies that live throughout the app's lifecycle.
class AppScope implements IAppScope {
  late final _env = Environment.instance;
  late final _baseClient = _initHttpClient();

  // repositories
  late final _authRepository = AuthRepository(
    httpClient: _baseClient,
  );

  // services
  late final _authService = AuthService(
    authRepository: _authRepository,
  );

  late final Coordinator _coordinator;

  @override
  Environment get env => _env;

  @override
  Coordinator get coordinator => _coordinator;

  @override
  IAuthService get authService => _authService;

  /// Create an instance of [AppScope].
  AppScope();

  @override
  Future<void> init() async {
    await _authService.init();

    _coordinator = Coordinator(
      guards: [],
    )..init();
  }

  @override
  Foo fooFactory() {
    return Foo();
  }

  BaseHttpClient _initHttpClient() {
    return BaseHttpClient(
      client: HttpClientFactory.instance.getClient(
        allowSelfSigned: _env.isDev,
      ),
    );
  }
}

Create FeatureScopes #

In the same manner, if necessary, create classes that implement the FeatureScope interface. These classes will hold dependencies related to specific features or parts of your application.

The only difference is that these classes have a dispose method, which will be called when the scope is no longer needed. In this method, you should release all resources, close connections, etc.

  @override
  void dispose() {
    _bar.dispose();
  }

Initialize ScopeManager #

Before you run your app, you need to initialize the ScopeManager with your RootScope implementation and define how each FeatureScope should be created.

void main() async {
  // ....

  // Dependency injection setup.
  final scopeManager = ScopeManager.instance;
  await registerDependencies(scopeManager);

  runApp(
    App(scopeResolver: scopeManager),
  );
}

Future<void> registerDependencies(ScopeRegistry registry) async {
  final appScope = AppScope();

  await registry.init(appScope);

  registry.registerScopeBinding(
    ScopeBinding<IAuthenticatedScope>(
      (resolver) => AuthenticatedScope(resolver: resolver),
    ),
  );

  // ... etc
}

Make ScopeManager available in the widget tree #

Wrap your app with the Scopes widget, which will make ScopeManager available to all its descendants.

/// Application widget.
class App extends StatelessWidget {
  final ScopeResolver scopeResolver;

  /// Creates an instance of [App].
  const App({
    super.key,
    required this.scopeResolver,
  });

  @override
  Widget build(BuildContext context) {
    return Scopes(
      resolver: scopeResolver,
      child: const _App(),
    );
  }
}

Now you can access methods to subscribe, unsubscribe, and resolve dependencies from a widget in your app, using:

Scopes.of(context)

Typical usage #

Usually, a visual element accompanies the need for creating a scope (for example, a screen or a tab). Because of this, the preferred way to interact with scopes is from a widget.

For convenience, there is ScopeSubscriberMixin for State. When you use this mixin, it marks the specified FeatureScope as required while the widget is in the widget tree and releases it when the widget is removed. To access the held scope, use the scope property.

For example:

/// Widget that represents the authenticated zone of the app.
class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen>
    with ScopeSubscriberMixin<IAuthenticatedScope, HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          const Expanded(
            child: Placeholder(),
          ),
          Expanded(
            flex: 6,
            child: NavigationStack(
              coordinator: scope.coordinator,
            ),
          )
        ],
      ),
    );
  }
}

Even though the main use case is subscribing from a widget, you can subscribe and unsubscribe manually from anywhere in your code. Just be sure to unsubscribe when the scope is no longer needed.

Observability #

For debugging purposes, to help you monitor the state of scopes and their subscriptions, ScopeManager provides a way to observe changes.

First, you need to enable observability mode by calling the setObservability method on the ScopeObserver instance. This mode is also enabled by default in debug builds.

When observability mode is enabled, you can use subscribersPublisher and scopesPublisher to listen for changes in subscriptions and active scopes, respectively. There is also an ObservingInfo widget that displays this information in a simple UI, which you can include in your debug screen.

Maintainer #

Maintainer avatar

Mikhail Zotyev

Support #

We appreciate any form of support, whether it's a financial donation, a public sharing, or a star on GitHub and a like on Pub. If you want to provide financial support, there are several ways you can do it:

Thank you for all your support!

License #

This project is licensed under the MIT License. See LICENSE for details.

2
likes
160
points
169
downloads

Publisher

verified publishermbixjkee.dev

Weekly Downloads

A package that helps with managing dependency scopes in the application

Repository (GitHub)
View/report issues

Topics

#dependency-injection #scope #lifecycle

Documentation

API reference

Funding

Consider supporting this project:

github.com
buymeacoffee.com
www.patreon.com
boosty.to

License

MIT (license)

Dependencies

flutter

More

Packages that depend on scope_manager