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.off Named: New page is built before controllers are removed from memory #1113

Open eduardoflorence opened 3 years ago

eduardoflorence commented 3 years ago

Describe the bug When using Get.offNamed (or Get.off for unnamed routes) the new page is built before the controllers on the previous page are removed from memory. If the new page uses the same controller as the previous page, instead of having a new controller instance, we have access to the same instance and the Get log displays the message that the controller was deleted.

Reproduction code

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

void main() {
  runApp(GetMaterialApp(
    initialRoute: '/splash',
    getPages: [
      GetPage(
        name: '/splash',
        page: () => SplashPage(),
        binding: SplashBinding(),
      ),
      GetPage(
        name: '/login',
        page: () => LoginPage(),
        binding: LoginBinding(),
      ),
    ],
  ));
}

class SplashPage extends GetView<SplashController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Splash')),
      body: Center(
        child: RaisedButton(
          onPressed: () => Get.offNamed('/login'),
          child: Obx(() => Text(
              'Login ${controller.service.title} >>> ${controller.service.counter}')),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.service.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

class SplashBinding extends Bindings {
  @override
  void dependencies() {
    Get.put(ServiceController()); // or lazyPut
    Get.put(SplashController(service: Get.find()));
  }
}

class SplashController extends GetxController {
  // final service = Get.find<ServiceController>();
  final ServiceController service;
  SplashController({
    this.service,
  });
}

class LoginBinding extends Bindings {
  @override
  void dependencies() {
    Get.put(ServiceController()); // or lazyPut
    Get.put(LoginController(service: Get.find()));
  }
}

class LoginController extends GetxController {
  // final service = Get.find<ServiceController>();
  final ServiceController service;
  LoginController({
    this.service,
  });
}

class LoginPage extends GetView<LoginController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: Center(
        child: Obx(() => Text(
            'Login ${controller.service.title} >>> ${controller.service.counter}')),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.service.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

class ServiceController extends GetxController {
  final title = Random().nextInt(99999).toString();
  final counter = 0.obs;

  increment() => counter.value++;

  @override
  void onInit() {
    print('onInit $counter');
    super.onInit();
  }

  @override
  void onClose() {
    print('onClose $counter');
    super.onClose();
  }
}

To Reproduce Steps to reproduce the behavior:

  1. Click on floatingActionButton three times
  2. Click on login button
  3. The same controller is being used instead of a new

Expected behavior When clicking on the login button, ServiceController should be deleted (Get.offNamed) and LoginBinding should create a new instance of the ServiceController, but the same instance is used, since counter has not returned to zero and neither has the random number changed, in addition Get.log showed that the controller was deleted and it was not shown that it was initialized, but we managed to use it.

Flutter Version: 1.22.6

Getx Version: 3.25.4

Describe on which device you found the bug: iOS Simulator

ARTonu commented 3 years ago

I am also facing same issue

nastaran-mohammadi commented 3 years ago

same here

OliverRhyme commented 3 years ago

same here, this causes the newly created object for the page to be deleted because of the wrong timings

quocviet1996 commented 3 years ago

same here

jonataslaw commented 3 years ago

I just did a PR that fixes this, however you should change Get.put to Get.lazyPut

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

void main() {
  runApp(GetMaterialApp(
    initialRoute: '/splash',
    getPages: [
      GetPage(
        name: '/splash',
        page: () => SplashPage(),
        binding: SplashBinding(),
      ),
      GetPage(
        name: '/login',
        page: () => LoginPage(),
        binding: LoginBinding(),
      ),
    ],
  ));
}

class SplashPage extends GetView<SplashController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Splash')),
      body: Center(
        child: RaisedButton(
          onPressed: () => Get.offNamed('/login'),
          child: Obx(() => Text(
              'Login ${controller.service.title} >>> ${controller.service.counter}')),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.service.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

class SplashBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => ServiceController()); // or lazyPut
    Get.lazyPut(() => SplashController(service: Get.find()));
  }
}

class SplashController extends GetxController {
  // final service = Get.find<ServiceController>();
  final ServiceController service;
  SplashController({
    required this.service,
  });
}

class LoginBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => ServiceController()); // or lazyPut
    Get.lazyPut(() => LoginController(service: Get.find()));
  }
}

class LoginController extends GetxController {
  // final service = Get.find<ServiceController>();
  final ServiceController service;
  LoginController({
    required this.service,
  });
}

class LoginPage extends GetView<LoginController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: Center(
        child: Obx(() => Text(
            'Login ${controller.service.title} >>> ${controller.service.counter}')),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.service.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

class ServiceController extends GetxController {
  final title = Random().nextInt(99999).toString();
  final counter = 0.obs;

  increment() => counter.value++;

  @override
  void onInit() {
    print('onInit $counter');
    super.onInit();
  }

  @override
  void onClose() {
    print('onClose $counter');
    super.onClose();
  }
}