Flutterando/modular

RouterOutlet routes persist even when parent route is popped

Opened this issue · 3 comments

Describe the bug
Calling Modular.to.pop() or Navigator.of(context).pop() in a widget wrapping a RouterOutlet doesn't dispose of the RouterOutlet's widget tree. This appears to be specific to RouterOutlets displaying a dynamic route. Each time the dynamic route is pushed and popped, the RouterOutlet builds an additional widget tree.

Environment
Add your flutter doctor -v

[✓] Flutter (Channel stable, 3.19.5, on macOS 14.2.1 23C71 darwin-arm64, locale en-US)
    • Flutter version 3.19.5 on channel stable at /Users/zssnyder/Projects/Libraries/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 300451adae (10 weeks ago), 2024-03-27 21:54:07 -0500
    • Engine revision e76c956498
    • Dart version 3.3.3
    • DevTools version 2.31.1

[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.


[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15F31d
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 4.1)
    • Android Studio at /Users/zssnyder/Library/Application
      Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.6953283/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

[✓] VS Code (version 1.89.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.90.0

[✓] Network resources
    • All expected network resources are available.

To Reproduce

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

void main() {
  runApp(ModularApp(module: AppModule(), child: const MainApp()));
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    Modular.setInitialRoute('/home');

    return MaterialApp.router(
      title: "RouterOutlet Test",
      routerConfig: Modular.routerConfig,
    );
  }
}

class AppModule extends Module {
  @override
  void routes(RouteManager r) {
    r.child('/', child: (context) => const HomePage(), children: [
      ChildRoute('/home', child: (context) => const ChildPage('0')),
    ]);
    r.module('/child', module: ChildModule());
  }
}

class ChildModule extends Module {
  @override
  void routes(RouteManager r) {
    r.child('/', child: (context) => const ParentPage(), children: [
      ChildRoute('/:id', child: (context) => ChildPage(r.args.params['id'])),
    ]);
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int id = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: const RouterOutlet(),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.arrow_forward_rounded),
        onPressed: () {
          Modular.to.pushNamed('/child/$id');

          setState(() {
            id++;
          });
        },
      ),
    );
  }
}

class ParentPage extends StatelessWidget {
  const ParentPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back_rounded),
          onPressed: () => Modular.to.pop(),
        ),
      ),
      body: const RouterOutlet(),
    );
  }
}

class ChildPage extends StatelessWidget {
  const ChildPage(this.id, {super.key});

  final String id;

  @override
  Widget build(BuildContext context) {
    print(id);

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text(id),
      ),
    );
  }
}

Expected behavior
Each time the floating action button is pressed to navigate to the Child Module's route, the "id" passed into the route should be printed once.

Screenshots
Running on iOS Simulator (17.4)
https://github.com/Flutterando/modular/assets/19160564/f9253516-f72f-45be-a92e-d47eb332cb85

Output:

flutter: 0
flutter: 1
flutter: 1
flutter: 2
flutter: 2
flutter: 2
flutter: 3
flutter: 3
flutter: 3
flutter: 3

pubspec.yaml

name: modular_bug
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0

environment:
  sdk: '>=3.3.3 <4.0.0'

dependencies:
  flutter:
    sdk: flutter

  flutter_modular: ^6.3.4

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0

flutter:
  uses-material-design: true

@zssnyder I was having the same problem, tested your solution and it worked, thanks!!
But the master branch, was breaking I had to change to the modular_lite branch

dependency_overrides:
  flutter_modular:
    git:
      url: https://github.com/zssnyder/flutter_modular.git
      ref: modular_lite
      path: flutter_modular
  modular_core:
    git:
      url: https://github.com/zssnyder/flutter_modular.git
      ref: modular_lite
      path: modular_core

same issue
flutter_modular: ^6.3.4

Flutter 3.26.0-0.1.pre • channel beta • https://github.com/flutter/flutter.git
Framework • revision ee624bc4fd (12 days ago) • 2024-09-10 17:41:06 -0500
Engine • revision 059e4e6d8f
Tools • Dart 3.6.0 (build 3.6.0-216.1.beta) • DevTools 2.39.0
class DemoHome extends StatelessWidget {
  const DemoHome({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () => Modular.to.pushNamed(Routes.s1),
            child: const Text('Scrren1'),
          ),
          ElevatedButton(
            onPressed: () => Modular.to.pushNamed(Routes.s2),
            child: const Text('Scrren2'),
          ),
        ],
      ),
    );
  }
}

class DemoPage extends StatelessWidget {
  const DemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color(0xFF162866),
        foregroundColor: Colors.white,
        centerTitle: true,
        title: const Text('Demo Page'),
        leading: const BackButton(color: Colors.white),
      ),
      body: const Center(
        child: RouterOutlet(),
      ),
    );
  }
}

class Scrren1 extends StatelessWidget {
  const Scrren1({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      color: Colors.blue,
      child: const Text('Scrren1'),
    );
  }
}

class Scrren2 extends StatelessWidget {
  const Scrren2({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      color: Colors.green,
      child: const Text('Scrren2'),
    );
  }
}

/// DemoModule
class DemoModule extends Module {
  .....
  void routes(RouteManager r) {
    
    r.child('demoHome', child: (_) => const DemoHome(), children: []);
    r.child(
      'demoPage',
      child: (_) => const DemoPage(),
      children: [
        ChildRoute(
          'scrren1',
          child: (_) => const Scrren1(),
          children: [],
        ),
        ChildRoute(
          'scrren2',
          child: (_) => const Scrren2(),
          children: [],
        ),
      ],
    );
  }
}

/// AppModule

class AppModule extends Module {
  .....
  @override
  void routes(RouteManager r) {
    
    r.module(
      '/demo',
      module: DemoModule(),
      transition: TransitionType.rightToLeft,
      guards: [],
    );
  }
}

////
  static String demo = '/demo/demoHome';
  static String s1 = '/demo/demoPage/scrren1';
  static String s2 = '/demo/demoPage/scrren12';