Flutterando / modular

A smart project structure
https://pub.dev/packages/flutter_modular
Other
1.29k stars 251 forks source link

RouterOutlet routes persist even when parent route is popped #960

Open zssnyder opened 3 weeks ago

zssnyder commented 3 weeks ago

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
zssnyder commented 3 weeks ago

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