I've got a search-field in the appbar of my application. It's available on every page of the app. When something is selected in the seach-field, it routes to a named page that loads and displays details about the entity. It is also possible to use the search bar from the details page, which means, that it routes to the same page that it's already on. As the id of the entity is part of the url, the route is actually different and as such everything works. But...
When the page is opened, my GetX controller does some initialization, and on leave it disposes of resources such as TextController.
My problem is that when I navigate to the same page, onClose is not called before leaving which means that the resources are not disposed. Rather it open the 'new' page, calls onInit() then calls onClose() and disposes the resources that are in use.
When the action of the seach-field determines that the currentRoute is already on the details-page, it uses Get.offNamed('/details', parameters({id: entityId}))
, if it is on another page it simply uses Get.toNamed('/details', parameters({id: entityId}))
I've made a little app the illustrates the problem. The search-field has been replaced by buttons for simplicity.
https://flutlab.io/editor/994c9029-47e9-46e2-a0e6-cfd970b82079
To make it fail, try pressing 'To subpage' button twice.
Notice in the console:
I was expecting:
For reference this is the code:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
final Random random = Random();
final appBar = AppBar(
// The title text which will be shown on the action bar
title: const Text('Hello'),
actions: [
ElevatedButton(
child: const Text('To Subpage'),
onPressed: () {
if (Get.currentRoute.startsWith('/subpage')) {
Get.offNamed('/subpage',
parameters: {'id': '${random.nextInt(1000)}'});
} else {
Get.toNamed('/subpage',
parameters: {'id': '${random.nextInt(1000)}'});
}
}),
ElevatedButton(
child: const Text('Go Home'),
onPressed: () {
Get.offAllNamed('/', parameters: {'id': '${random.nextInt(1000)}'});
})
],
);
final pages = [
GetPage(name: '/', page: () => RootPage()),
GetPage(name: '/subpage', page: () => SubPage(), binding: PageBinding()),
];
class PageBinding implements Bindings {
@override
void dependencies() {
Get.put(SubPageController());
}
}
class SubPageController extends GetxController {
final TextEditingController myDomController = TextEditingController();
String? id;
@override
void onInit() {
id = Get.parameters['id'];
print('initialized with id $id');
// Now initialize and load a lot of stuff based on id
super.onInit();
}
@override
void onClose() {
print('closed with id $id');
myDomController.dispose(); // This is a problem!
super.onClose();
}
}
void main() => runApp(GetMaterialApp(
getPages: pages,
initialRoute: '/',
));
class RootPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBar,
body: const Center(
child: Text(
'Root page',
),
),
);
}
}
class SubPage extends GetView<SubPageController> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBar,
body: Center(
child: Column(
children: [
Text(
'Subpage page',
),
TextFormField(controller: controller.myDomController)
],
)),
);
}
}
```
in such cases for you, you can delete (run onClose()
) the controller you want simply by using:
Get.delete<SubPageController>();
if you have a tag for that controller:
Get.delete<SubPageController>(tag: yourTagHere);
so in the onPressed
of your buttons, you could call to delete the controller from the memory, then call it for your next use:
ElevatedButton(
child: const Text('Go Home'),
onPressed: () {
Get.delete<SubPageController>();
Get.offAllNamed('/', parameters: {'id': '${random.nextInt(1000)}'});
})
this will delete the controller if it's already on, then navigate to that root so it will be called again, and so on.