skip to Main Content

I have a Flutter app composed of "sub-apps" with different theme colors.
In a sub-app, the AppBar’s back button is working as expected: it navigates back to the previous sub-app page.
However the Android system’s back button is NOT working as expected: it navigates directly to the root app.

The expected back navigation in the following example should be: B3 B2 B1 A3 A2 A1. But it’s B3 A3 A2 A1.

In other words, I want the Android back button to work the same way as the Flutter back button.

The same problem happens with iOS "back swipe" gesture (iosPageTransition = true).

Please try the following code on an Android device or emulator and test with the system’s back button.

Note that I use multiple MaterialApps to apply a color theme to all screens of a sub-app.

Also note that WillPopScope doesn’t work since it’s not triggered by the system’s back button.

import 'package:flutter/material.dart';

void main() {
  runApp(_AppA(_PageA(1)));
}

class _ColoredApp extends StatelessWidget {
  final Color color;
  final Widget home;
  final iosPageTransition = false;
  _ColoredApp(this.color, this.home);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.light(primary: color),
        pageTransitionsTheme: iosPageTransition
            ? PageTransitionsTheme(
                builders: Map.fromIterable(
                  TargetPlatform.values,
                  value: (_) => const CupertinoPageTransitionsBuilder(),
                ),
              )
            : null,
      ),
      home: home,
    );
  }
}

class _AppA extends _ColoredApp {
  _AppA(Widget home) : super(Colors.red, home);
}

class _AppB extends _ColoredApp {
  _AppB(Widget home) : super(Colors.green, home);
}

class _PageA extends StatelessWidget {
  final int number;
  const _PageA(this.number);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('A$number')),
      body: Center(
        child: ElevatedButton(
          child: Text('Next'),
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) {
              if (number > 2) return _AppB(_PageB(1));
              return _PageA(number + 1);
            }));
          },
        ),
      ),
    );
  }
}

class _PageB extends StatelessWidget {
  final int number;
  const _PageB(this.number);

  @override
  Widget build(BuildContext context) {
    var scaffold = Scaffold(
      appBar: AppBar(
        leading: number == 1 ? BackButton(onPressed: () => Navigator.of(context, rootNavigator: true).pop()) : null,
        title: Text('B$number'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Next'),
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) {
              return _PageB(number + 1);
            }));
          },
        ),
      ),
    );
    return scaffold;
  }
}

2

Answers


  1. First of all you can’t set two MaterialApp in single flutter app its wrong way to use it, in your code it initialize 2 times.Just change your theme from the page you want to update,

    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
    
      final iosPageTransition = false;
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: _PageA(1),
        );
      }
    }
    
    class _PageA extends StatelessWidget {
      final int number;
      const _PageA(this.number);
    
      @override
      Widget build(BuildContext context) {
        return
    
          Theme(
            data: ThemeData(
              colorScheme: ColorScheme.light(primary: Colors.red),
            ),
            child: Builder(
                builder: (context) {
                  return  Scaffold(
                    appBar: AppBar(title: Text('A$number')),
                    body: Center(
                      child: ElevatedButton(
                        child: Text('Next'),
                        onPressed: () {
                          Navigator.push(context, MaterialPageRoute(builder: (context) {
                            if (number > 2) return _PageB(1);
                            return _PageA(number + 1);
                          }));
                        },
                      ),
                    ),
                  );
                }
            ),
          );
      }
    }
    
    class _PageB extends StatelessWidget {
    
      final int number;
      const _PageB(this.number);
    
      @override
      Widget build(BuildContext context) {
    
       return
    
         Theme(
           data: ThemeData(
             colorScheme: ColorScheme.light(primary: Colors.blue),
           ),
           child: Builder(
               builder: (context) {
                 return  Scaffold(
                   appBar: AppBar(
                     leading: BackButton(onPressed: () => Navigator.pop(context)) ,
                     title: Text('B$number'),
                   ),
                   body: Center(
                     child: ElevatedButton(
                       child: Text('Next'),
                       onPressed: () {
                         Navigator.push(context, MaterialPageRoute(builder: (context) {
                           return _PageB(number + 1);
                         }));
                       },
                     ),
                   ),
                 );
               }
           ),
         );
    
      }
    }
    
    Login or Signup to reply.
  2. As Dharini said you cannot use 2 material apps, but if you really want to, then here is the workaround for you

    Issue: When you try to B(n) it pops the B as it is new material app.
    |
    |_ A
      |_ A1
      |_ A2
    |_ B
      |_ B1
      |_B2
    

    Resolution: Pass the BuildContext and Route from B to A and remove route on back tap of icon or android back key.

    Navigator.removeRoute(context, route);

    Workaround

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(_AppA(_PageA(1)));
    }
    
    class _ColoredApp extends StatelessWidget {
      final Color color;
      final Widget home;
      final iosPageTransition = false;
    
      const _ColoredApp(this.color, this.home);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData(
            colorScheme: ColorScheme.light(primary: color),
            pageTransitionsTheme: iosPageTransition
                ? PageTransitionsTheme(
                    builders: Map.fromIterable(
                      TargetPlatform.values,
                      value: (_) => const CupertinoPageTransitionsBuilder(),
                    ),
                  )
                : null,
          ),
          home: home,
        );
      }
    }
    
    class _AppA extends _ColoredApp {
      const _AppA(Widget home) : super(Colors.red, home);
    }
    
    class _AppB extends _ColoredApp {
      const _AppB(Widget home) : super(Colors.green, home);
    }
    
    class _PageA extends StatelessWidget {
      final int number;
    
      _PageA(this.number);
    
      final bRoutes = <BuildContext, Route>{};
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text('A$number')),
          body: Center(
            child: ElevatedButton(
              child: const Text('Next'),
              onPressed: () {
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  if (number > 2) {
                    return WillPopScope(
                      onWillPop: () async {
                        if (number > 2 && bRoutes.isNotEmpty) {
                          Navigator.removeRoute(bRoutes.entries.last.key, bRoutes.entries.last.value);
                          bRoutes.remove(bRoutes.entries.last.key);
                          return false;
                        } else {
                          return true;
                        }
                      },
                      child: _AppB(_PageB(1, bRoutes)),
                    );
                  }
                  return _PageA(number + 1);
                }));
              },
            ),
          ),
        );
      }
    }
    
    class _PageB extends StatelessWidget {
      final int number;
      final Map<BuildContext, Route> bRoutes;
    
      const _PageB(this.number, this.bRoutes);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            automaticallyImplyLeading: false,
            leading: BackButton(onPressed: () {
                    if (bRoutes.isNotEmpty) {
                      final context = bRoutes.entries.last.key;
                      final route = bRoutes.entries.last.value;
                      bRoutes.remove(bRoutes.entries.last.key);
                      Navigator.removeRoute(context, route);
                    }
                  })
                ,
            title: Text('B$number'),
          ),
          body: Center(
            child: ElevatedButton(
              child: const Text('Next'),
              onPressed: () {
                final route = MaterialPageRoute(builder: (context) {
                  return _PageB(number + 1, bRoutes);
                });
    
                bRoutes[context] = route;
                Navigator.push(context, route);
              },
            ),
          ),
        );
      }
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search