How to fix Android back button cold start bug in Flutter (flutter/flutter#117061)

Kształt kółka
Kształt półkola
Ksztalt koła dekoracyjny
How to fix Android back button cold start bug in Flutter (flutter/flutter#117061)

How to fix Android back button cold start bug in Flutter (flutter/flutter#117061)

While developing the ToPWR application in Flutter (go ahead and give us a star!), we encountered a bug related to the Android back button. When you use the back button on Android to return to the  previous screen, once you reach the root screen, it should leave the app and put it in the background.

However, in the recent Flutter version, the app gets killed every time you reach the root screen and try to pop back further. When you reenter the app, it starts from a cold start, losing the previous state and requiring a lengthy initial load. The same with the back gesture!

The video showcasing the issue:

 

 

The app may still be usable like that, but it's irritating at least. It's not a user-friendly experience at all. So, we've researched the web and stumbled upon a flutter issue: flutter/flutter#117061, which describes our problem. Unfortunately, at the moment of writing this, it's still an open issue, and none of the simple workarounds worked for us. 

That's where my flutter/android dev Mikołaj Jałocha (go ahead and give him a follow!) stood up and got to work. As a result, we've got a piece of good news! A working fix!

That's right. It wasn't fully working at first. There was still one problem: this solution involved WillPopScope, which is a recently deprecated widget. Its replacement PopScope didn't have the feature we required. However, with my humble assistance, we've managed to solve this as well, resulting in a pretty decent workaround. 

The fix

For this article, I've created a brand-new fresh flutter project that shows how to fix this issue. I'm describing it step-by-step in the article, but you can just jump straight into the repo: https://github.com/Solvro/mobile-flutter-back-btn-android-fix

If you prefer to see this fix in a wild environment, you can check out the PR in our project regarding the matter. However, we have a bit more complicated setup there (nested navigator etc): https://github.com/Solvro/mobile-topwr/pull/80

Step-by-step instructions

The idea is that we need to intercept pop callback and use MethodChannel to send a signal to our native Android layer that will put the app into the background. We want to fully block the flutter's navigator pop callback (only in this specific scenario).

The following steps provide a minimal reproducible solution:

1. I've started with a fresh project: android_back_btn_fix_example and moved the example counter app to a folder app. At the moment issue exists, as is presented in the clip above.

2. Let's start with the logic. I'm using a handy extension to wrap it in one place. File lib/fix/fix_logic.dart: 

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

extension AndroidPopBugWorkaround on NavigatorState {
  static const platform = MethodChannel('example.app.android.channel');

  bool get androidSpecialPopTreatment =>
      Platform.isAndroid && canPop() == false;

  void handleAndroidSpecialPop() {
    platform.invokeMethod('putAppInBackground');
  }
}

3. Now you can use this logic to create a widget that will act as a wrapper that fixes the mentioned issue. Here I'm using the default Navigator.of(context) navigator, but you can adjust that to your app however needed. It should still work in most situations with few simple changes at most. File lib/features/fix_widget.dart:

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

class FixAndroidPopBug extends StatelessWidget {
  const FixAndroidPopBug({
    super.key,
    required this.child,
  });

  final Widget child;

  @override
  Widget build(BuildContext context) {
    final navigator = Navigator.of(context); // or any other way you access your navigator

    return PopScope(
      canPop: !navigator.androidSpecialPopTreatment,
      child: NavigatorPopHandler(
        enabled: navigator.androidSpecialPopTreatment,
        onPop: navigator.handleAndroidSpecialPop,
        child: child,
      ),
    );
  }
}

4. The clue of the whole fix is on the native Android side, where you catch the signal and put the app in the background. To do that, you need to modify your MainActivity.kt in the android folder. In my example app, the whole file path is android/app/src/main/kotlin/com/example/android_back_button_fix_example/MainActivity.kt

package com.example.android_back_btn_fix_example

import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
    private val CHANNEL = "example.app.android.channel"

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "putAppInBackground") {
                moveTaskToBack(true)
                result.success(null)
            } else {
                result.notImplemented()
            }
        }
    }
}

5. The only step left is to put your FixAndroidPopBug widget somewhere in your root screen and tada! Your app works as it's supposed to.

@Override
Widget build(BuildContext context) {
  return FixAndroidPopBug(
    child: Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      // ...
    ),
  );
}

The result

By following these steps, you can effectively fix the Android back button cold start bug in your Flutter app. This solution ensures that your app behaves as expected, providing a smoother and more user-friendly experience.

Properly working app video:

 

 

 

If you find this guide helpful, please consider giving our ToPWR application a star and follow Mikołaj Jałocha and Me on GitHub or elsewhere!

Happy coding!

Awatar Szymon Kowaliński

Szymon Kowaliński

Wiceprezes 2024/2025, Flutter Techlead

Paradox Call To Action Image
Paradox Shape Image

Dołącz do naszej ekipy!

Skontaktuj się

DołączDołącz