Skip to content
Haydar Demir

Writing4 min read

Effortless Lifecycle Management in Flutter Applications

In this article, we will delve into the details of an abstract class called AppLifecycleManager, designed to make lifecycle management in Flutter applications easier and more organized.

A smartphone with glowing lifecycle rings labeled Detach, Resume, Hide, Inactive, and Show
Created by ChatGPT

Hello Everyone!

App lifecycle management is a cornerstone in the app development process. As modern mobile applications offer increasingly complex features to meet user needs, they must behave appropriately when in the foreground, background, or closed states. Such scenarios require developers to not only deliver functionality but also to optimize resource management and user experience.

As your application grows, the operations tied to the app lifecycle can become more complex. For instance, running specific tasks in the background, maintaining sessions while the app is closed, or ensuring a seamless user experience when the app comes to the foreground are all requirements that demand careful structuring and management.

In this article, we will delve into the details of an abstract class called AppLifecycleManager, designed to make lifecycle management in Flutter applications easier and more organized. This structure allows you to manage lifecycle-related operations from a central point, making your development process more modular and sustainable.

Dwight Office Tv GIF by The Office

What is AppLifecycleManager?

By using AppLifecycleListener, you can make your code more organized and readable, focusing only on specific events you are interested in, thus managing your code in a more sustainable way.

For this reason, AppLifecycleManager incorporates the AppLifecycleListener class to effectively manage operations tied to your app’s lifecycle. This structure allows you to organize and control actions triggered during different app states (e.g., resume, pause, hide, etc.)

AppLifecycleManager provides three core methods:

  • Adding lifecycle functions (add): You can add functions that will be triggered during lifecycle events, such as when your app goes to the background or comes to the foreground.
  • Removing functions (remove): You can remove lifecycle functions that are no longer needed.
  • Clearing all functions (clear): You can clear all lifecycle functions at once.
abstract interface class AppLifecycleManager {
  factory AppLifecycleManager.instance() => _AppLifecycleManagerImpl();

  Future<void> add(IAppLifecycleUseCase usecase);
  void remove(IAppLifecycleUseCase usecase);
  void clear();
}

AppLifecycleManager Concrete Class

final class _AppLifecycleManagerImpl implements AppLifecycleManager {
  _AppLifecycleManagerImpl() {
    _listenLifecycle();
  }

  final List<IAppLifecycleUseCase> _usecases = [];

  @override
  Future<void> add(IAppLifecycleUseCase usecase) async {
    try {
      if (!_usecases.any((element) => element.id == usecase.id)) {
        _usecases.add(usecase);
        await usecase.init();
      }
    } catch (e) {
      AppLogger.e(e);
    }
  }

  @override
  void remove(IAppLifecycleUseCase usecase) {
    try {
      _usecases.removeWhere((element) => element.id == usecase.id);
      usecase.dispose();
    } catch (e) {
      AppLogger.e(e);
    }
  }

  void _listenLifecycle() {
    AppLifecycleListener(
      onDetach: () async {
        for (final usecase in _usecases) {
          await usecase.onDetach();
        }
      },
      onResume: () async {
        for (final usecase in _usecases) {
          await usecase.onResume();
        }
      },
      onPause: () async {
        for (final usecase in _usecases) {
          await usecase.onPause();
        }
      },
      onHide: () async {
        for (final usecase in _usecases) {
          await usecase.onHide();
        }
      },
      onInactive: () async {
        for (final usecase in _usecases) {
          await usecase.onInactive();
        }
      },
      onShow: () async {
        for (final usecase in _usecases) {
          await usecase.onShow();
        }
      },
    );
  }

  @override
  void clear() {
    _usecases.clear();
  }
}

IAppLifecycleUseCase

Each use case implements the IAppLifecycleUseCase interface. This structure provides methods to handle lifecycle events:

abstract class IAppLifecycleUseCase {
  AppLifecycleUseCase get id;
  Future<void> init() {}
  void dispose() {}
  Future<void> onDetach() {}
  Future<void> onResume() {}
  Future<void> onPause() {}
  Future<void> onHide() {}
  Future<void> onInactive() {}
  Future<void> onShow() {}
}

AppLifecycleUseCase

AppLifecycleUseCase is an AppLifecycleUseCase that defines different use cases responding to the application’s lifecycle events. This enum provides a unique identifier for each use case, enabling them to be easily managed and distinguished by AppLifecycleManager and IAppLifecycleUseCase.

enum AppLifecycleUseCase {
  sessionTimeout;
}

Example: SessionTimeoutUseCase

Below is an example of a separate class that implements the IAppLifecycleUseCase interface. This class provides functionality to check if the user’s session has timed out and redirect the user to the login screen.

final class SessionTimeoutUseCase extends IAppLifecycleUseCase {
  SessionTimeoutUseCase({
    required AuthFacade authFacade,
    required NavigationService navigationService,
  })  : _authFacade = authFacade,
        _navigationService = navigationService;

  final AuthFacade _authFacade;
  final NavigationService _navigationService;

  @override
  AppLifecycleUseCase id = AppLifecycleUseCase.sessionTimeout;

  @override
  Future<void> onInactive() async {
    // Checks if the user's session is active.
    final isSessionActive = await _authFacade.isSessionActive();
    if (!isSessionActive) {
      // Session has ended, redirect the user to the login screen.
      await _navigateToLogin();
    }
  }

  @override
  Future<void> onResume() async {
    // Can refresh the user's session duration.
    await _authFacade.refreshSession();
  }

  Future<void> _navigateToLogin() async {
    // Performs the redirection process.
    await _navigationService.navigateToLogin();
  }
}

Use Case:

final lifecycleManager = AppLifecycleManager.instance();

final sessionTimeoutUseCase = SessionTimeoutUseCase(
  authFacade: AuthFacade(),
  navigationService: NavigationService(),
);

lifecycleManager.add(sessionTimeoutUseCase);

Related writing

Type to search