Create Getx controllers on demand with Bindings - flutter

I am looking forward to creating controller on demand with a different tag which binds with the Bindings defined. Is there any way to do this in Getx?

try this if this one you looking
Am using routenamed and bind the binding from GetPage
e.g.
bindings
class BindingControllers with Bindings{
#override
void dependencies(){
Get.lazyPut(()=> Controller1,fenix: true);
Get.lazyPut(()=> Controller2,fenix: true);
Get.lazyPut(()=> Controller3,fenix: true);
}
}
Controller
class Controller1 extends GetxController{
// as per origin using controller on the page
// my case i use get find.
final findController2 = Get.find<Controller2>();
final findController3 = Get.find<Controller3>();
// so in this case you won't to put this on UI but on the controller
// on the ui side you can call this controller.findController1 and other controller you did bind.
}
GetPage
final list = <GetPage>[
GetPage(
name: "/dashboard",
binding: BindingControllers(),
page:()=> const DashBoard()
),
];

I was able to figure out the answer it can be done using bindingBuilder which will create controller with bindings in runtime
The Documentation can be found here
https://chornthorn.github.io/getx-docs/dependency-management/binding/#bindingsbuilder

Related

Flutter GetX: Where Does Get.put Go in a Widget?

I am new to GetX and am trying to learn how to use it. I have read different tutorials that inject the controller outside of the widget's build method, and others that put it inside it.
class MyWidget extends StatelessWidget{
const MyWidget({Key? key}) : super(key:key);
//Outside...
final controller = Get.put(Controller()); //<---
#override
Widget build(BuildContext context) {
//Inside...
final controller = Get.put(Controller()); //<---
return Obx(
() => Text(controller.name)
);
}
}
Is there a difference between those two locations? If so, why?
Also, where should it go in a StatefulWidget? It seems it should not go inside the build method because it causes a stack overflow error for me.
Does the location of Get.put() matter inside a widget?
The normal way is to put the controller outside the widget, so it will be created once. if you but it inside the widget a new instance of the controller will be created each time you refresh (update) the widget.
Also, with GetX there is no need to use StatefulWidget.
When you put it inside the build method, it will create a new instance every time the widget is rebuilt, so it is better to but it outside and in most cases I think you do not need to use StatefulWidgetwith GetX even in animation.
The answers shared are correct. Thank you! I wanted to post another answer that I consider to be an even better way to do it as referenced in the documentation.
This method allows for controller access without ever putting Get.put in any of your widgets. This is also really helpful if you have a lot of controllers and need to reference them in widgets as well as from other controllers.
Here is an example:
//ControllerA
class ControllerA extends GetxController {
static ControllerA get to => Get.find();
final name = 'Bob'.obs;
someMethod(){
ControllerB.to.anotherMethod(); //'I am inside ControllerB!'
}
}
//ControllerB
class ControllerB extends GetxController {
//+++
static ControllerB get to => Get.find();
anotherMethod(){
print('I am inside ControllerB!');
}
}
And then inside Widgets:
class MyWidget extends StatelessWidget{
const MyWidget({Key? key}) : super(key:key);
#override
Widget build(BuildContext context) {
return Obx(
() => Text(ControllerA.to.name) //Bob
);
}
}
This requires that you add your Get.put declarations in main.dart so that you make sure they are all ready:
void main() {
Get.put(ControllerA());
Get.put(ControllerB());
runApp(
GetMaterialApp(...)
);
}
In my opinion, it's really clean like this--and very convenient!
If we want to keep a GetX controller in memory forever, then we should indeed instantiate it outside of a Widget's build() function (such as within main() as Clifton shows).
(Note that we can also use GetXService for persistent controllers, which allows manual disposal.)
Placing controllers inside build() functions, is the correct place when we want GetX to free memory & dispose the controller when the widget goes "out of scope". (e.g. when the user has "popped" the route from the stack.) There's also no danger in "recreating" the controller when its instantiated inside build(): Get checks for existence of a controller when Get.put() is called and skips it if the controller is already instantiated.
See this answer (and link to an explanation by one of Get's maintainers) for more info on why we should Get.put() inside build().

GetX Controller in Stack (Flutter)

Im using GetX Controller in Screen A and navigating to the Screen B which is also a same screen with different data's but same Controller, (its a concept of navigating inside and inside the users profile in social media app - reference: Instagram)
The issue is when i navigates from Screen A to Screen B, Screen B data is fetched from the API which is totally fine, then when we clicks back button it return back to Screen A, but the Screen A data is replaced with Screen B data
Initialized controller like
final ProfileViewController userProfileController =
Get.put(ProfileViewController());
Navigating to screen like
Get.to(() => ScreenB());
Expecting the best solution, Thanks in Advance!
I did this and it worked fine, hope it helps
in main.dart
Get.create(() => ProfileViewController());
then in the view
ProfileViewController_con = Get.find();
Get.to(() => const ScreenB(), preventDuplicates: false);
Try to find and use GetWidget in GetX
With GetWidget, you will have a separate new controller for each widget instance.
class ProfileViewWidget extends GetWidget< ProfileViewController > {...}
...
class YourAnyScreenBindings implements Bindings {
#override
void dependencies() {
Get.put(YourAnyScreenCtrl());
Get.create(() => ProfileViewController());
}
}
...
List<GetPage<dynamic>> getPages = [
GetPage(
name: '/your_any_screen',
page: () => YourAnyScreen(),
binding: YourAnyScreenBindings(),
),
]
Use tag with the controller
Initialize controller like
final ProfileViewController userProfileController =
Get.put(ProfileViewController(),tag:'a_string_may_be_the_profile_id');
usage:
var controller = Get.find<ProfileViewController>(tag:'a_string_may_be_the_profile_id')
By this way, more than one profile controller can be created and accessed

Binding without using "Get.to()" for GetX in Flutter

I want to bind a controller to view 1 but I don't want to go to that view 1 via using Get.to(Page());.
Instead I want to use view 1 directly inside view 2 by creating an object.
Simplified code (BTW I'm using Veiw1Controller variables inside View1 itself)
class Veiw2 extends GetView<Veiw2Controller>{
return View1();
}
When I'm doing the above code, it throws an error saying
"View1Controller" not found. You need to call
"Get.put(View1Controller())" or
"Get.lazyPut(()=>View1Controller())"
I did call Get.put(...) in the binding but I think since we are not calling Get.to()therefore GetX does not realize when we are using that view and it does not bind the dependencies
Here is what I've done
class View1 extends Bindings {
#override
void dependencies() {
Get.put<View1Controller>(
View1Controller(),
);
}
}
What is the best way to do that?
well, the Bindings API was made to work together with the Getx navigation features, without actually Get.to(), Get.toNamed()..., you should not expect the automatic dependency injection of Getx.
However, you still could inject those dependencies manually: like this:
class BindingsOne extends Bindings {
#override
void dependencies() {
Get.put<View1Controller>(
View1Controller(),
);
}
}
Now when you want to get a Widget:
Widget getViewWidget() {
BindingsOne().dependencies(); // this will inject all your dependencies from the bindings.
return YourWidget();
}
you could also inject them before you use Navigator routing features.
// ...
BindingsOne().dependencies(); // this will inject all your dependencies from the bindings.
Navigator.of(context).push(MaterialPageRoute(builder: (context) => YourWidget()));
}

HomeController" not found. You need to call "Get.put(HomeController())" or "Get.lazyPut(()=>HomeController())"

I am using getX. I just need once the application opened, the data from database shown. I created a future function inside the repository and need to auto-display it. if it call this function inside onInit inside the controller the lat variable will not be initialized yet. i need to call this function inside the binding but this error occurs.
HomeController" not found. You need to call "Get.put(HomeController())" or "Get.lazyPut(()=>HomeController())"
The Code Inside Binding Is:
Get.lazyPut<IHomeRepository>(
() => HomeRepoFirebaseImplimentation(),
);
// Get.lazyPut<HomeController>(
Get.putAsync(
() async=>await HomeController(homeRepository: Get.find()).fetchProductsFromDB(),
);
I was experiencing the same problem, and nothing worked.
But studying my structure further and in more detail, I had the following tree:
BasePage
| -> HomePage
| -> CartPage
| -> OrdersPage
And what happened was that I was trying to put the binding on my HomePage directly, but as my base that would do the navigation through the PageView, I needed to create a Binding in the Base that stores the Controllers of each view.
This made it work perfectly.
More practically, I did this:
class BaseBindings implements Bindings {
#override
void dependencies() {
Get.put(HomeController());
Get.put(CartController());
Get.put(OrdersController());
}
}
And in getPages I did the following:
GetPage(
name: '/base',
page: () => const BasePage(),
binding: BaseBindings(),
),
This made the application work normally and I had more peace of mind in the development; that's what worked.

How to return data when popping multiple screens?

I know I can return data to the previous screen by using
Navigator.pop(context, 'Value');
But in my case I need to pop multiple screens by using
Navigator.popUntil(context, ModalRoute.withName('/login'));
I wonder in this case how do I pass the data back to the corresponding widget?
Thanks in advance.
you can send DATA in few ways
as a Parameter
using Shared_Preferences
using Static Variables
Only for Current Session
if you just need the DATA for Current Session you can go for Static Variables
step 1 : Create a Class and have Static Variable in it.
class Globaldata{
static String value;
}
step 2 : Initialise variable by
Globaldata.value="some_value";
step 3 : use of variable
String assigned_value = Globaldata.value;
The flutter API does not have that feature and from this https://github.com/flutter/flutter/issues/30112 discussion, that feature is not on the table yet. A walkaround was suggested though using the Page API.
However, in my opinion, it is cleaner to use the provider package https://pub.dev/packages/provider as part of your app state management to keep the data you want and make it available to any screen of interest. Follow these steps to achieve that.
Add the provider to your pubspec.yaml. Check the link above to see detailed instructions.
Create a notifier class that extends ChangeNotifier class as shown below. ChangeNotifier class is part of the flutter API.
class MyDataProvider extends ChangeNotifier {
//define your private data field(s). I'm using int here.
int _mydata;
//define a getter
int get myData => _myData;
// define a setter
set myData(newData){
_myData = newData;
notifyListeners();
}
}
Wrap your uppermost widget (or the parent of the screens where you want to pass the data) with the provider and instantiate it. I'm using main here.
void main(){
runApp(
ChangeNotifierProvider(create: (context) => MyDataProvider()),
child: MyApp(),
)
}
Assuming you have five screens: Screen1, Screen2, ..., Screen5 and you want to navigate to screen5, do some operations and return to screen1 with some data. On 1st screen, define a local variable for myData and create an instance of your myDataProvider. When a button is pressed to start the navigation, wrap up the push navigation in an asynchronous call.
//Screen1
int currentLocalData = 78;
MyDataProvider myProvider = Provider.of<MyDataProvider>(context);
onPressed: () async {
//Assign localData to myData in the provider
myProvider.myData = currentLocalData; //calls the setter define in the provider.
await Navigator.push(context, MaterialPageRoute(
builder: (context) => Screen5()
));
//Retrieve myData from the provider and assign it to currentLocalData.
//This executes after navigating back from Screen5
currentLocalData = myProvider.myData;
}
Let assume in Screen5 you retrieved the data and added 100 to it. Your aim is to return to Screen1 and make use of the new data, i.e 178. Here you will instantiate the provider again in Screen5 and assign the value 178 to myData.
//Screen5
MyDataProvider myProvider = Provider.of<MyDataProvider>(context);
myProvider.myData += 100;
//Use navigation.popUntil
Navigation.popUntil(context, ModalRoute.withName('/Screen1'));
Say you have Screen A,Screen B, Screen C. If you want to pop till Screen A and pass some data. Here is what you have to do.
1. Navigate from Screen A to Screen B
Navigator.pushNamed(context, '/screenb')
.then((value) {
//you will get return value here
});
2. To pop till Screen A.
//add thi code in Screen C
var nav = Navigator.of(context);
nav.pop('refresh');
nav.pop('refresh');