modular icon indicating copy to clipboard operation
modular copied to clipboard

Nested module should override parent binding

Open vgabler opened this issue 7 months ago • 2 comments

Describe the bug If i have a singleton binding of type T, then navigate to a child module route wich also binds to this same T (but a different implementation), the returned instance inside this child module comes from the parent instead of overriding.

Environment Flutter 3.32.2

Pubspec.yaml

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

environment:
  sdk: ^3.8.1

dependencies:
  flutter:
    sdk: flutter
  flutter_modular: ^6.4.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0

flutter:
  uses-material-design: true

To Reproduce From new flutter empty application Here is the minimal reproducible code:

main.dart

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

import 'modules.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(
    ModularApp(
      module: BaseModule(),
      child: MaterialApp.router(
        title: 'Child Module Bug',
        theme: ThemeData(primarySwatch: Colors.blue),
        routerConfig: Modular.routerConfig,
      ),
    ),
  );
}

modules.dart

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

import 'services.dart';

class BaseModule extends Module {
  @override
  void binds(Injector i) {
    i.addLazySingleton<GetHelloMessage>(GetHelloMessageBase.new);
  }

  @override
  void routes(RouteManager r) {
    r.child(
      '/',
      child: (context) => BasePage(
        title: "BASE MODULE",
        navigate: () => Modular.to.pushNamed("childModule/"),
      ),
    );
    r.module("/childModule/", module: ChildModule());
  }
}

class ChildModule extends Module {
  @override
  void binds(Injector i) {
    i.addLazySingleton<GetHelloMessage>(GetHelloMessageChild.new);
  }

  @override
  void routes(RouteManager r) {
    r.child(
      '/',
      child: (context) =>
          BasePage(title: "CHILD MODULE", navigate: () => Modular.to.pop()),
    );
  }
}

class BasePage extends StatelessWidget {
  const BasePage({super.key, required this.title, required this.navigate});

  final String title;
  final Function() navigate;

  @override
  Widget build(BuildContext context) {
    final getHelloMessage = Modular.get<GetHelloMessage>();

    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(getHelloMessage()),
            FilledButton(onPressed: navigate, child: Text("Navigate")),
          ],
        ),
      ),
    );
  }
}

services.dart

abstract class GetHelloMessage {
  String call();
}

class GetHelloMessageBase implements GetHelloMessage {
  @override
  String call() {
    return "Hello from BASE";
  }
}

class GetHelloMessageChild implements GetHelloMessage {
  @override
  String call() {
    return "Hello from CHILD";
  }
}

Expected behavior The child module should prioritize it's own bindings instead of the parent's. In "Child Module" page, we should see the message "Hello from CHILD"

Screenshots

Image

Image

vgabler avatar Jun 27 '25 20:06 vgabler