Using RouteObserver to Refresh a widget when you go back

In a recent project, I ran into an issue where I needed to refresh a page when the user navigated back to it. After searching on google and asking ChatGPT, I found this simple and clean solution.

What is RouteObserver?

RouteObserver is part of the Flutter framework that helps you track navigation events in your app. It allows you to listen to changes in the route stack, such as when a route is pushed or popped. This is incredibly useful for scenarios where you need to refresh or update your UI based on navigation.

Setting Up RouteObserver

First, I created a UserSession class to hold a singleton instance of RouteObserver. Here’s the gist:

// user_session.dart
import 'package:flutter/material.dart';

class UserSession {
  static final UserSession _instance = UserSession._internal();
  static final routeObserver = RouteObserver<ModalRoute>();

  UserSession._internal();
}

By doing this, I could easily access routeObserver from anywhere in my app. My UserSession also holds other user session data, maybe a bettway is to create a separate file for RouteObserver. But for now, we just use the UserSession class.

Adding RouteObserver to Your App

Next, I needed to register this RouteObserver with my app’s navigator. I did this in the main app widget:

// main.dart
import 'package:flutter/material.dart';
import 'package:happy_notes/screens/account/user_session.dart';

class HappyNotesApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const InitialPage(),
      navigatorObservers: [UserSession.routeObserver],
    );
  }
}

By adding UserSession.routeObserver to navigatorObservers, we’re now tracking route changes across the entire app.

Using RouteObserver in Screens

To use RouteObserver, I extended my state classes with RouteAware and subscribed to the routeObserver in the initState method. Here’s an example from the Memories screen:

// memories.dart
import 'package:flutter/material.dart';
import '../account/user_session.dart';

class MemoriesState extends State<Memories> with RouteAware {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      UserSession.routeObserver.subscribe(this, ModalRoute.of(context)!);
    });
  }

  @override
  void didPopNext() {
    // Called when the top route has been popped off, and this route shows up
    _fetchMemories();
    setState(() {});
  }

  @override
  void dispose() {
    UserSession.routeObserver.unsubscribe(this);
    super.dispose();
  }
  
  void _fetchMemories() {
    // Logic to refresh memories
  }
}

Here’s what’s happening:

  1. Subscription: In initState, the screen subscribes to routeObserver.
  2. Route Events: The didPopNext method is triggered when the screen becomes visible again after another screen is popped off. This is where I refreshed the screen's data by calling _fetchMemories().
  3. Unsubscription: It’s important to unsubscribe in the dispose method to avoid memory leaks.

Conclusion

Using RouteObserver in Flutter allows you to manage your app’s navigation state effectively. By listening to route changes, you can ensure that your UI stays in sync with user actions, providing a seamless experience.