jonataslaw / getx

Open screens/snackbars/dialogs/bottomSheets without context, manage states and inject dependencies easily with Get.
MIT License
10.38k stars 1.62k forks source link

Get handles incorrectly Controller injection and life cycle #2433

Open Mihai-B opened 2 years ago

Mihai-B commented 2 years ago

I think there is something wrong on how Get handles Controller injection and life cycle

Describe the bug I think it is more easy to describe this with an example: We have 3 screens, the home screen, the second screen and the third screen. When the user navigates from homescreen to second screen, a SecondScreenController is put( Get.put(GenericController(timeStamp)) ). The generic controller receives a timestamp ( with value 1), and then there is a widget that displays that particular timestamp (aka 1). From the second screen there is a button to go to third screen, this is just a simple widget screen, and on the third screen there is a button to go to second screen and remove all previous screens with Get.offAllNamed('/second'); Now the Second screen is instantiated, the screen displays correctly the new timestamp (2) but then instead of closing the controller that was generated, the one with timestamp 1, it will close controller with timestamp 2.

**Reproduction code

I have a simple flutter app, that only has get: 4.6.5. I have given an example of the code below

example:

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      getPages: getPagesList,
      initialRoute: '/',
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Homepage"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Current page:'),
            Text('Homepage', style: Theme.of(context).textTheme.headline4),
            ElevatedButton(
              child: const Text('Go to Second page'),
              onPressed: () {
                Get.toNamed('/second');
              },
            ),
          ],
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  final timeStamp = DateTime.now().millisecondsSinceEpoch;
  SecondScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final GenericController controller = Get.put(GenericController(timeStamp));
    return Scaffold(
      appBar: AppBar(
        title: const Text("SecondScreen"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Current page:'),
            Obx(() => Text('SecondScreen ${controller.timeStamp}')),
            ElevatedButton(
              child: const Text('Go to Third page'),
              onPressed: () {
                Get.toNamed('/third');
              },
            ),
          ],
        ),
      ),
    );
  }
}

class ThirdScreen extends StatelessWidget {
  const ThirdScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("ThirdScreen"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Current page:'),
            const Text('ThirdScreen'),
            ElevatedButton(
              child: const Text('Go to Second page'),
              onPressed: () {
                Get.offAllNamed('/second');
              },
            ),
          ],
        ),
      ),
    );
  }
}

class GenericController extends GetxController {
  final RxInt timeStamp;

  GenericController(int newTime) : timeStamp = newTime.obs;

  @override
  void onClose() {
    print("Closing Controller $timeStamp");
    super.onClose();
  }
}

final List<GetPage> getPagesList = [
  GetPage(
    name: '/',
    page: () => const MyHomePage(),
  ),
  GetPage(
    name: '/second',
    page: () => SecondScreen(),
  ),
  GetPage(
    name: '/third',
    page: () => ThirdScreen(),
  ),
];

To Reproduce Steps to reproduce the behavior:

  1. Go to 'Second screen' by clicking the 'Go to second screen' button and notice the time stamp
  2. Click on ''Go to third screen'
  3. Click on ''Go to second screen'
  4. Notice the timestamp displayed on screen
  5. Notice that in the logs, the same timestamp is printed as being closed

Expected behavior It should close the first SecondScreenController not the one you are currently using

Flutter Version: 3.0.3

Getx Version: 4.6.5

Mihai-B commented 2 years ago

I just checked the pull requests, and I am not 100% sure but I think this pull request is related to my issue: https://github.com/jonataslaw/getx/pull/2421