Flutter and Getx: How to Pass parameters from UI to Getx controller? - flutter

I have this Getx controller for reading content of a Post from database:
class ReadSinglePostController extends GetxController {
var isLoading = true.obs;
var posts = Post(
postID: 1,
userID: 0,
thumbnail: 'thumbnail',
imageList: 'imageList',
title: 'title',
description: 'description',
createdTime: DateTime.now())
.obs; //yes this can be accessed
var postid = 2.obs; //I want this value to change when I click a post in the UI
#override
void onInit() {
super.onInit();
readPost(postid);
}
updateID(var postID) {
postid.value = postID;
print('im print ${postid.value}');
}//should update postid when a post is clicked in the UI
Future readPost(var postID) async {
try {
isLoading(true);
var result = await PostsDatabase.instance.readPost(postID);
posts.value = result;
} finally {
isLoading(false);
}
}
}
But the problem I'm now facing is that: to read a specific Post from database, I need the postID parameter. And as you can imagine, this parameter can be recorded when I click a specific Post in UI, but how do I pass that parameter to this Getx controller? Or maybe I am doing this whole thing wrong?

You can use the instance of your controller on the Ui.
For example, on the widget you call the controller:
final ReadSinglePostController _controller = Get.put(ReadSinglePostController());
//and when you need to change you do like this:
_controller.updateID(newId);
Inside the updateID method you can call the load method:
updateID(var postID) {
postid.value = postID;
print('im print ${postid.value}');
readPost(postID);
}

For those who may be using obs, you can do as follows:
In the controller, you can define
var postid = 0.obs
and from the view just add
controller.postid.value = 20;

Related

unexpected output while using get.put in flutter

To make my point clear I have created this small code..
it looks like I missing to understand concept of byref and byval here..
and what does actually Get.put works.
I have commented the lines what made me confused
GetxController
class UserController extends GetxController {
List<UserModel> _userlist = [];
UserModel? currentuser;
UserController() {
_userlist.add(UserModel(username: 'temp', password: 't123'));
}
List<UserModel> get getusers {
return _userlist;
}
}
this is main
void main()
{
final uc1=Get.put(UserController());
UserModel user1=UserModel(username: 'abc', password: 'a123');
uc1.currentuser=user1;
//showing print abc
print(uc1.currentuser!.username);
final uc2=Get.put(UserController());
UserModel user2=UserModel(username: 'xyz', password: 'x123');
uc2.currentuser=user2;
//showing printing result=xyz
print(uc2.currentuser!.username);
//showing printing result=xyz, instead of abc why?
print(uc1.currentuser!.username);
}
Get.put only puts a new instance when there isn't already one, (or the one that's there is marked as dirty). You can see it by digging deeper into the code. Ctrl + clicking the put method (in Android Studio at least) makes you able to see the implementation. The first step leads to
S put<S>(S dependency,
{String? tag,
bool permanent = false,
InstanceBuilderCallback<S>? builder}) =>
GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
then doing it on put again leads to
S put<S>(
S dependency, {
String? tag,
bool permanent = false,
#deprecated InstanceBuilderCallback<S>? builder,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder ?? (() => dependency));
return find<S>(tag: tag);
}
And digging into the _insert method gives:
/// Injects the Instance [S] builder into the `_singleton` HashMap.
void _insert<S>({
bool? isSingleton,
String? name,
bool permanent = false,
required InstanceBuilderCallback<S> builder,
bool fenix = false,
}) {
final key = _getKey(S, name);
if (_singl.containsKey(key)) {
final dep = _singl[key];
if (dep != null && dep.isDirty) {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
lateRemove: dep as _InstanceBuilderFactory<S>,
);
}
} else {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
);
}
}
Here you can see that if it already exists and isDirty is true, or if it doesn't exist yet, only then it inserts a new instance. Now I'm not entirely sure when isDirty is set, but I believe it happens when you change routes in your app. So the second time you call put you are actually retrieving the one that is already put there before. Now if you want to have multiple instances you can use Get.create(() => UserController()); followed by Get.find instead. Like:
void main()
{
Get.create(() => UserController());
UserController uc1 = Get.find();
UserModel user1=UserModel(username: 'abc', password: 'a123');
uc1.currentuser=user1;
//showing print abc
print(uc1.currentuser!.username);
UserController uc2 = Get.find();
UserModel user2=UserModel(username: 'xyz', password: 'x123');
uc2.currentuser=user2;
//showing printing result=xyz
print(uc2.currentuser!.username);
//showing printing result=abc
print(uc1.currentuser!.username);
}
Because you are using the put method two time
if you want to update a single instance then follow the below code:
void main()
{
final uc1=Get.put(UserController());
UserModel user1=UserModel(username: 'abc', password: 'a123');
uc1.currentuser=user1;
//showing print abc
print(uc1.currentuser!.username);
UserController uc2 = Get.find();
UserModel user2=UserModel(username: 'xyz', password: 'x123');
uc2.currentuser=user2;
//showing printing result=xyz
print(uc2.currentuser!.username);
//showing printing result=abc
print(uc1.currentuser!.username);
}

Added data is only showing after reloading in flutter

here is a popup screen to add the transaction to the app, as you can see here
and when the add button pressed the data will add to database and also to the dislpay , here is the code
ElevatedButton(
//on pressed
onPressed: () async {
final _categoryName = _nameEditingController.text;
if (_categoryName.isEmpty) {
return;
}
final _type = selectedCategoryNotifier.value;
//sending the data to model class
final _category = CategoryModel(
id: DateTime.fromMillisecondsSinceEpoch.toString(),
name: _categoryName,
type: _type,
);
//inserting the data to database
await CategoryDb.instance.insertCategory(_category);
//refreshing the ui
await CategoryDb.instance.refreshUI();
//and quitting the popup screen
Navigator.of(ctx).pop();
},
child: const Text('Add'),
),
and in this code you can see that I called 2 functions that for insert data and also refresh the UI, in the refresh UI function I added the function that to get all data from database to screen, here the code of all functions for CRUD operatins
const databaseName = 'category-database';
abstract class CategoryDbFunctions {
Future<List<CategoryModel>> getCategories();
Future<void> insertCategory(CategoryModel value);
}
//CRUD operations code
class CategoryDb implements CategoryDbFunctions {
CategoryDb._internal();
static CategoryDb instance = CategoryDb._internal();
factory CategoryDb() {
return instance;
}
ValueNotifier<List<CategoryModel>> incomeCategoryListListener =
ValueNotifier([]);
ValueNotifier<List<CategoryModel>> expenseCategoryListListener =
ValueNotifier([]);
#override
Future<void> insertCategory(CategoryModel value) async {
final _categoryDB = await Hive.openBox<CategoryModel>(databaseName);
await _categoryDB.add(value);
await refreshUI();
}
#override
Future<List<CategoryModel>> getCategories() async {
final _categoryDB = await Hive.openBox<CategoryModel>(databaseName);
return _categoryDB.values.toList();
}
Future<void> refreshUI() async {
final _allCategories = await getCategories();
incomeCategoryListListener.value.clear();
expenseCategoryListListener.value.clear();
await Future.forEach(
_allCategories,
(CategoryModel category) {
if (category.type == CategoryType.income) {
incomeCategoryListListener.value.add(category);
} else {
expenseCategoryListListener.value.add(category);
}
},
);
}
}
so I checked the all things , but I couldn't find where I'm missing parts,
and here is the main part, it is adding to the database also displaying after I refresh the UI or change the tab here you can see what I mean by 'changing the tab'
this is the problem I'm trying to fix this for 2 day, i couldn't find any solution or mistake in my code
There many ways you can handle this problem.
but I dont see where you notify youre ui that the data has been changed, flutter does only update the ui when you use setState etc.. these functions help flutter updating the ui where the data changed.
i would recommend you to use setState in the place you invoke youre dialog.
onTap:(){
setState(){
await dialogStuff();
}
}

How to update the data constantly after using API in Getx Flutter?

I have a getx controller and a method to update data in the database, I just wonder that how can I refresh of update the UI constantly after fetching the API? Here is my controller
class AdditionalContactController extends GetxController {
var additionalContactList = <AdditionalContact>[].obs;
var isLoading = true.obs;
UserController _userController = Get.find();
#override
void onInit() {
super.onInit();
_userController.getMail().then((value) async {
await _userController.getUser(value);
getAdditionalContacts(_userController.user.value.id);
});
}
//Update contact
Future<bool> updateContact({...}) async {
var response = await http.post(
Uri.parse(
"https://..."),
body: {
...
});
var jsonData = jsonDecode(response.body);
if (jsonData == "failed") {
return false;
}
return true;
}
}
you can use the ever worker to call a method that executes every time an Rx have assigned with a new value:
class AdditionalContactController extends GetxController {
var additionalContactList = <AdditionalContact>[].obs;
#override
void onInit() {
super.onInit();
ever(additionalContactList, methodToExecute)
});
}
methodToExecute(list) {
/* Code that will execute every time the additionalContactList changes */
}
now everytime additionalContactList is changed like as example if we assign a new List to it:
additionalContactList.value = [];
Then the methodToExecute() will be executed automatically, and will do every single time.

Flutter API Fetch and Sending Data from One screen to another screen

When I am logged in through the API I am getting the value User_id which I am able to print on console, but I want to use it on another screen.
So how can I send the data to another screen?
// First Screen
Future postMethod() async {
var api = Uri.parse("https://demo.likemyfiles.com/DS/api/auth/otp");
Map mapeddate = {
'phone': _phone.text,
'otp': _otp.text,
};
final response = await http.post(api, body: mapeddate);
print(response.body);
var res = json.decode(response.body);
print(res['user_id']);
Navigator.pushNamed(context, StartActivity.id); //here i want to send the User_Id
}
//Second Screen (Start Activity) in this screen there is a function FetchData where i want to use the data
Future fetchdata() async {
var url = await http.get(Uri.parse(
"http://demo.likemyfiles.com/DS/api/api_supervisor/supervisor/3")); //Instead of 3 i want
to use the User_Id variable
}
You should try to declare constructor the first page accept the id and push this id to second page or screen like this below code is first page
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ABC.withId(
id,
),
),
)
the create constructor inside second page screen
class ABC extends StatefulWidget {
#override
_ABCState createState() => _ABCState();
var id;
ABC.withId(String uid) {
id = uid;
}
}
accept your id inside widget using widget.id

Can I use getx controller inside another getx controller?

I am using Get (getx) package to manage the state of my app, and I am trying to use a controller inside another one. For example I have a class the contains methods for firebase authentication
class FirebaseAuthController extends GetxController {
static FirebaseAuthController get to => Get.find<FirebaseAuthController>();
.....
Future<void> createUser(String email, String password) async {
try {
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
} catch (e) {
...
}
}
...
...
}
and I have another controller which is signUpController that interacts with the UI
class SignInController extends GetxController {
static SignInController get to => Get.find<SignInController>();
...
....
Future<void> clickSignInButton() async {
print(emailController.text);
print(passwordController.text);
if (formKey.currentState.validate()) {
try {
await FirebaseAuthController.to
.login(emailController.text, passwordController.text);
} catch (e) {
print(e);
}
}
}
}
when I try to do this, it gives me an error
lib/screens/authentication_screens/controller/sign_up_controller.dart:56:37: Error: Getter not found: 'to'.
await FirebaseAuthController.to
any idea what might be the issue?
You absolutely can, despite it's considered a bad practice. It's recommended to use something like repository or usecase classes instead unless you want to update the data shown on the screen attached to that controller from another controller.
And for the solution to your actual problem or error, just change static FirebaseAuthController get to => Get.find<FirebaseAuthController>(); to static FirebaseAuthController get to => Get.put(FirebaseAuthController());
yes you can use one controller in another controller and user its variable and also update it
Example
class HomeController extends GetxController {
var home = '';
String userName = '';
updateName() {}
}
class LoginController extends GetxController {
HomeController homeController = Get.put(HomeController());
String email = '';
String password = '';
signin() {
email = homeController.userName;
homeController.updateName();
homeController.update();
}
}