Describe the bug
The value in GetBuilder doesn't update if value is changed, even if update() is called.
To Reproduce
class LoadingWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<LoadingController>(
init: LoadingController(),
builder: (_) => AlertDialog(
content: Text(
_.loaderMsg,
),
),
);
}
}
class LoadingController extends GetxController {
String loaderMsg = 'TempLoading';
void showLoader({String msg = 'Loading...'}) {
loaderMsg = msg;
update();
Get.dialog(LoadingWidget()); //<-- even moved it above but not working.
}
void hideLoader() {
Get.back();
}
}
RaisedButton(
onPressed: () {
LoadingController().showLoader(msg: 'Loading some new content');
},
child: Text('Show Loader'),
),
Expected behavior
When clicking on Show Loader button the text is in LoadingWidget should update, but whenever the button is click, there's only TempLoading text which is the default text.
Flutter Version:
1.17.5
Get Version:
3.4.0
Describe on which device you found the bug:
Pixel 2 API 28 - Android.
You are not calling a function, you are creating a new instance, and accessing a function from that newly created instance.
RaisedButton(
onPressed: () {
LoadingController().showLoader(msg: 'Loading some new content'); // HERE
},
child: Text('Show Loader'),
),
You should have done something like this:
RaisedButton(
onPressed: () {
Get.find<LoadingController>().showLoader(msg: 'Loading some new content'); // HERE
},
child: Text('Show Loader'),
),
@jonataslaw Thanks for quick response. I also tried that but this didn't work.
โโโโโโโโ Exception caught by gesture โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The following message was thrown while handling a gesture:
LoadingController not found. You need call put<LoadingController>(LoadingController()) before
When the exception was thrown, this was the stack
#0 GetInstance.find
package:get/โฆ/instance/get_instance.dart:162
#1 Inst.find
package:get/โฆ/instance/extension_instance.dart:19
#2 LoginView.build.<anonymous closure>
package:bluebox/โฆ/login/login.view.dart:29
#3 _InkResponseState._handleTap
package:flutter/โฆ/material/ink_well.dart:779
#4 _InkResponseState.build.<anonymous closure>
package:flutter/โฆ/material/ink_well.dart:862
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#399b8
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(209.9, 353.9)
finalLocalPosition: Offset(48.2, 18.2)
button: 1
sent tap down
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
I don't want to have Get.put(LoadingController()); everywhere in every widget because I will need to show this dialog many times and in every widget. Is there any easy way?
try lazyput it in main.I just tried it on myapp and it works, shows correct text when open the dialog.
class DI {
static init() {
/// Services
Get.lazyPut<LoadingController>(() => LoadingController());
}
}
void main() {
DI.init();
runApp(GetMaterialApp(
home: MyApp(),
));
}
import 'package:get/get.dart';
import 'package:myapp/screens/loadingwidget.dart';
class LoadingController extends GetxController {
var loaderMsg = 'TempLoading'.obs;
void showLoader({String msg = 'Loading...'}) {
loaderMsg.value = msg;
Get.dialog(LoadingWidget());
update();
}
void hideLoader() {
Get.back();
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:myapp/services/loadingcontroller.dart';
class LoadingWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetBuilder<LoadingController>(
init: LoadingController(),
builder: (data) => AlertDialog(
content: Text(
'State: ${data.loaderMsg.value}',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
);
}
}
RaisedButton(
onPressed: () {
Get.find<LoadingController>()
.showLoader(msg: 'Loading some new content');
},
child: Text('Show Loader'),
),
Best!
but wait why did you use .obs? Is it required? I am not using it though.
Good :) Yeah String type will work if you use GetBuilder and update. I am not sure i think @jonataslaw has to answer but if it should be an "observerable" it needs .obs or Rx
@lundin thanks!
I know it and I am in love with Observables and have been using it in TypeScript.
But overuse of it is not good and shouldn't be used just for small tasks like changing the widget text.
Hello! i'd like to explain some things:
update() in that caseAs you know, update() is meant for rerender some Widget that is being show on the screen with a new value you want.
But if you pay attention, you are calling update() before the dialog show on screen! That means that you don't need update() in this occasion, because when dialog get show in the screen, it will get the value msgLoader has, and it will be the value inside the method, because you already changed the value before open a dialog
so, update() in that specific case is doing nothing.
.obs is not needed to work in that case toosince you don't need update, you don't need .obs too, because it is to update value of something that is already showing at the screen. Besides, when you using vars with obs, the widget you need to make it work is GetX(), not GetBuilder(). The reason it worked is because you didn't even needed in the first place
The reason you got this error when you applied the changes @jonataslaw requested is because on the RaisedButton the controller was not initialized yet. And that's why putting it in the main method works. A good thing to do is add fenix: true to controller, like that:
Get.lazyPut<LoadingController>(() => LoadingController(), fenix: true);
By default, controllers are disposed when you are not using them in some screen, so if for example, you use it on the first screen, but don't use it in the second, it will be disposed and in the third screen you will not have the controller to use and it will give error.
the fenix: true argument makes so that whenever you call this method and the controller does not exist anymore, it will recreate the instance for you to use (now the name makes a little more sense right? ๐
)
that's all. I hope it helps you understanding better this package.
In my opinion, "fenix" is a bad property name, it should just be "recreate".
In my opinion, "fenix" is a bad property name, it should just be "recreate".
or it can have the same name that is used in bindings SmartManagement: keepFactory
although this would probably confuse people. Or not, since it does the same thing
@joanofdart and @Nipodemos very appreciated thanks. You don't know how much I love this package. This is one of theeee besssst package ever i have seen. I am exploring it further.
Most helpful comment
@joanofdart and @Nipodemos very appreciated thanks. You don't know how much I love this package. This is one of theeee besssst package ever i have seen. I am exploring it further.