GardenMobile & interfaces

Tracking route visibility in a Flutter application

A RouteObserver-based mixin that separates screen activation from application lifecycle events.

A screen can become covered by another route while the application remains in the foreground. This earlier mixin used route observation to name that narrower activation boundary.

The route-aware mixin

import 'package:flutter/material.dart';
import 'package:nft/my_app.dart';
import 'package:nft/utils/app_log.dart';

mixin RouteActiveMixin<T extends StatefulWidget> on State<T>
    implements RouteAware {
  bool _subscribed = false;
  bool active = false;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (!_subscribed) {
      MyApp.routeObserver.subscribe(this, ModalRoute.of(context));
      _subscribed = true;
    }
  }

  @override
  void dispose() {
    if (_subscribed) {
      MyApp.routeObserver.unsubscribe(this);
      _subscribed = false;
    }
    super.dispose();
  }

  @override
  void didPush() => onActive();

  @override
  void didPopNext() => onActive();

  @override
  void didPop() => onInactive();

  @override
  void didPushNext() => onInactive();

  /// Update active flag
  void onActive() {
    active = true;
    logger.d(
        '${runtimeType.toString()} onActive ${ModalRoute.of(context).settings.name}');
  }

  /// Update inactive flag
  void onInactive() {
    active = false;
    logger.d(
        '${runtimeType.toString()} onInactive ${ModalRoute.of(context).settings.name}');
  }
}

Connect the observer and state


class MyApp extends StatefulWidget {
  const MyApp({Key key}) : super(key: key);

  static final GlobalKey<NavigatorState> _navigatorKey =
      GlobalKey<NavigatorState>();
  static final RouteObserver<Route<dynamic>> routeObserver =
      RouteObserver<Route<dynamic>>();

  static NavigatorState get navigator => _navigatorKey.currentState;

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: MyApp._navigatorKey,
      debugShowCheckedModeBanner: false,
      initialRoute: AppConstant.rootPageRoute,
      onGenerateRoute: AppRoute.generateRoute,
      navigatorObservers: <NavigatorObserver>[MyApp.routeObserver],
    );
  }
}
class _HomePageState extends State<HomePage>
    with RouteActiveMixin<HomePage> {

  @override
  Widget build(BuildContext context) {
    return const ScreenWidget(
      body: HomeScreenBody(),
    );
  }
}

The observer instance used by the navigator must match the one the route-aware state subscribes to. Subscription and unsubscription belong to the state’s lifetime.

Keep route names available

import 'package:flutter/material.dart';
import 'package:nft/pages/base/content_page.dart';
import 'package:nft/pages/counter/counter_page.dart';
import 'package:nft/pages/home/home_page.dart';
import 'package:nft/utils/app_constant.dart';

class AppRoute {
  /// Generate route for app here
  static Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case AppConstant.counterPageRoute:
        return MaterialPageRoute<dynamic>(
            settings: settings,
            builder: (_) => ContentPage(
                body: CounterPage(argument: settings.arguments as String)));
      case AppConstant.rootPageRoute:
      default:
        return MaterialPageRoute<dynamic>(
            settings: settings,
            builder: (_) => const ContentPage(body: HomePage()));
    }
  }
}
- Start app
flutter: [D]  _HomePageState onActive /

- Navigate to Counter Screen 
flutter: [D]  _HomePageState onInactive /
flutter: [D]  _CounterPageState onActive /counter_screen

- Navigate back to Home by pop
flutter: [D]  _HomePageState onActive /
flutter: [D]  _CounterPageState onInactive /counter_screen

The output is an earlier trace, not newly collected runtime evidence. Route settings, nested navigators, and replacement/removal operations can change which callbacks apply. Source: original template. No route interaction was executed for this revision.

← Back to the garden