How can I listen to a String variable change and perform an action when there is a change? - flutter

Is it possible to execute a function, lets call it myFunction() if a variable _myString is changed, but have this happen on the fly?
What I have is a textfield with a controller
var _myString;
const TextField(
controller:_controller
)
Now elsewhere in my widget tree, I have a button that can change the value of _myString, in this case I'm changing '_myString' to 'Testing'
GestureDetector(
onTap:(){ _myString = 'Testing'; }
child: Text('Testing')
)
Now what I'm hoping to achieve is that when the value of _myString changes in any way, I can perform some action of my choosing. In this case, I want to edit the TextField using the _controller, but I don't only want to do that, but a few other things, so I think its better to listen to changes in that variable and then execute a function
void myFunction(){
///Do some stuff
}
I'm using riverpod for state management in my app, and I was thinking I could try to use it, but have no idea how to use it to watch a single variable, I'm more familiar with using it for entire widgets. Alternatively using riverpod for something like this might be overkill.
I just don't know how to approach this problem, so any guidance would be really appreciated!

I believe you could use a ValueNotifier for this, a value notifier is a class that holds some value and "notifies" its listeners when this value changes. It is a simpler version of ChangeNotifier:
ValueNotifier<String> _myString = ValueNotifier<String>('');
With the above, whenever you want to read or write the value, use the value getter/setter:
print(_myString.value);
_myString.value = 'some value';
Now, to listen to changes you should use the addListener method:
#override
initState() {
// update _controller with value whenever _myString changes
_myString.addListener(() => _controller.text = _myString.value);
// print value on change
_myString.addListener(() => print(_myString.value));
// do stuff
_myString.addListener(myFunction)
}

Related

Why does Flutter riverpod direct assignment does not work but methods do

Please check the two samples below.
The first sample does not rebuild the Widgets [Possibly 'listeners'
are not being 'notified']
The second sample works as
expected
To my understanding, i think all these two should work. Can someone brief me on the comprehension I'm lacking?
Thanks in advance.
Sample one (Does not rebuild) [ui changes do not take effect ]
onTap: (String? newValue) {
ref.watch(UserProvider).selectedMaritalStatusValue = newValue!;
UserModel().notifyAllListeners(); //triggers notifyListeners
},
Sample Two (Does rebuild)[working fine]
onTap: (String? newValue) {
ref.watch(UserProvider).setMaritalStatus(newValue!); // 'setMaritalStatus' has notifyListeners trigger within
},
Firstly, you should not use ref.watch inside any onTap callbacks. Use ref.read here instead. Read this for clarity on why this is the case.
Secondly, in your first code block where you write:
UserModel().notifyAllListeners();
UserModel() creates a new object altogether, and notifyAllListeners() is being called for this new object. This new object is not being watched inside the build method of this widget. This is why the first code block you posted fails to rebuild the widget.
Thirdly, as a best practice, methods like notifyListeners() and direct assignments of fields in any class should be done inside the class's code. Use your second code block as a reference in future. This is the correct and safest way.
You can use a private variable setter, then call notifyListeners in the setter after updating the variable like so:
class UserProvider extends ChangeNotifierProvider{
String? _selectedMaritalStatusValue;
String? get selectedMaritalStatusValue => _selectedMaritalStatusValue;
set selectedMaritalStatusValue(String? newValue){
_selectedMaritalStatusValue = newValue;
notifyListeners();
}
}
Now, this should work:
ref.watch(UserProvider).selectedMaritalStatusValue = newValue!;

is it possible to automatically refresh component when getx value changed flutter

I am using getx get: ^4.3.8 as the state management component in flutter, when I using this code to update the controller value:
var expand = controller.newTaskExpanded.value;
controller.newTaskExpanded(!expand);
I found the component did not refresh, then I have to invoke the getx refresh function like this:
controller.refresh();
this is the controller properties define:
class HomeController extends GetxController {
List<Widget> widgetsList = List.empty(growable: true);
List<Todo> tasks = List.empty(growable: true);
var newTaskExpanded = true.obs;
var completedTaskExpanded = false.obs;
}
is is possible to auto refresh the flutter component when controller properties value changed? should I invoke the refresh function every time change the value of getx controller?
Reactive programming with Get is as easy as using setState.
Let's imagine that you have a name variable and want that every time you change it, all widgets that use it are automatically changed.
This is your count variable:
var name = 'Jonatas Borges';
To make it observable, you just need to add ".obs" to the end of it:
var name = 'Jonatas Borges'.obs;
And in the UI, when you want to show that value and update the screen whenever the values changes, simply do this:
Obx(() => Text("${controller.name}"));
For more , refer: https://pub.dev/packages/get/example
wrap you UI with Obx builder like
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
it will auto refresh the UI on value change
if you want to refresh the UI manually then use GetBuilder and when you need to update the UI just call controller.update();
As #Muhammad Sherad explained, your widget code should be wrapped with Obx() so that you widget can look for variable change and re-render the widget.

How use onTap function of treeview library flutter

Hi I am using this pubdev package https://pub.dev/packages/tree_view/example . My problem is that when I use the onTap function the expanded does not work, I was checking the library and I noticed that it is because of this code, however I do not know how to solve it or if there is another way to access that function from the library from the widget. Any ideas
If you notice it when the onTap function is different from empty, the toggleExpanded() function does not apply
So any ideas??
You need to control the expansion yourself using the startExpanded property of the TreeView widget.
First, you have a boolean variable (say _startExpanded) in your StatefulWidget that hold the state of the expansion and you can set it the default state (for instance, false).
bool _startExpanded = false;
Then, you pass the variable to your TreeView widget:
TreeView(
startExpanded: _startExpanded,
children: _getChildList(documentList),
),
To expand, you call:
setState((){
_startExpanded = true;
});
To close, you call:
setState((){
_startExpanded = false;
});

How do I add commands within a widget tree?

In flutter/dart, how do I add commands (for loops, if/then etc) within widget trees?
So let's say we have a tree
Scaffold(...)
How can I add command syntax in there. The only thing possible is ternary operators. (as far as I'm aware)
You can do that inside build() like:
Widget build(context) {
// use for loop or if else or anything you need
return Scaffold(...);
}
The better way to do is by creating a dedicated method like
void _doSomethingHere() {
// use for, if-else etc
}
And use this method in button pressed like:
RaisedButton(onPressed: _doSomething)
If you want to perform some action before build(), you can override initState() like:
void initState() {
super.initState();
// use for, if-else
}
You can do that inside initState() if you are using StatefulWidget. Else do that inside build() method.
The main reason why you can't use control structures(if-else) or loops(for,while,do-while) while creating your UI as a widget(-tree) is because of the way flutter tries to understand what UI are you trying to create.
The control structure if just accepts a block of code and executes it if the condition given to it satisfies.
if(condition)
{
//statment(s) to be executed
}
Whereas ternary operator simply replaces a particular line of code/value based on the condition given to it.
(condition)
?
value-or-code-to-replaced-if-the-condition-satifies
:
value-or-code-to-replaced-if-the-condition-does-not-satisfy;
Since we just return the UI, a widget tree to the build function using return keyword, adding control structures like if practically does not make any sense.

How can you get setState.of(context) in Flutter?

I have an app with a ModalProgresHUD on most pages. Usually I can pass a fucntion to, for example, onTap, to any widget in this tree, to turn this spinner on/off.
But sometimes this seems difficult and I'd like to access the fields and/or setState on a State somewhere else, up the WidgetTree.
One option seems to be to move all the logic into the top Widget, and pass handlers down to access these methods, but that feels cludgy.
class StatefullPage ..... {
String _someImportantField;
set someImportantField(String newValue) {
_someImportantField = newValue;
if(mounted) setState((){});
}
...
}
class StateOfSomethingElse ... {
Future doSomeWorkThatAffectsTheParent() async {
await something.then((String newResult) {
State.of(context).someImportantField = newResult;// HOW TO DO THIS
}
...
}
What kind of state management do you use? It looks that you are simply using setState and there's no problem with that. But to do the sort of think you want i think it's better to use something like provider or redux to handle the state of your app.
Using Provider for example, you can provide a value on the top of your tree and can get this value anywhere on your widget tree to do your logic.