while discovering Getx
source code, I faced a piece of code that make me confused, it's not related 100% to Getx
. it's about SetState(() {})
so, in the Getx
there is a state update that targets only the widgets with a specific id:
Disposer addListenerId(Object? key, GetStateUpdate listener) {
_updatersGroupIds![key] ??= <GetStateUpdate>[];
_updatersGroupIds![key]!.add(listener);
return () => _updatersGroupIds![key]!.remove(listener);
}
the _updatersGroupIds
is a HashMap<Object?, List<void Function()>>?
.
and when calling it from the StatefulWidget
, like this :
controller?.addListenerId(
widget.id,
setState(() {}),
);
the id
is passed from a property in the widget call, and as you see it's passing the whole SetState(() {})
of that StatefulWidget
in the function!
so the _updatersGroupIds
will be something like this :
_updatersGroupIds == {
"idExample": [
SetState(() {}),
]
};
right?
what confused me is when we try to update the state we call the update
method from the controller with desirables ids to update :
update(["idExample"]);
this is implemented as follows:
void update([List<Object> ids]) {
for (final id in ids) {
if (_updatersGroupIds!.containsKey(id)) {
final listGroup = _updatersGroupIds![id]!;
for (var item in listGroup) {
item();
}
}
}
}
so what I understand is that when we get the "exampleId" in the _updatersGroupIds
hashmap, we iterate over all the SetState(() {})
in that list, by calling them
so what I'm expecting vs what happens :
what I'm expecting:
that since we called all the SetState(() {})
in that List
, it will update the State
of all StateFulWidget
in the whole app, or at least it will update the last StatefulWidget
we run that function from.
what is happening:
it updates only the StatefulWidget
with the same "exampleId", and nothing else
my question is: how when we store the SetState(() {})
of multiple widgets like :
List<void Function()> staterList = [SetState(() {}), SetState(() {}), SetState(() {})];
when we call the second one:
staterList[1].call();
how does it know that it should update only the StatefulWidget
where that SetState((){})
came from and nothing else?
another format of question:
is there some referencing set on SetState(() {})
, so that it knows the StateFulWidget
where it came from when we call it somewhere outside?
please share your knowledge with me.
ListNotifierSingle
lib/get_state_manager/src/simple/list_notifier.dart: class ListNotifierSingle = ListNotifier with ListNotifierSingleMixin;
we care mostly about ListNotifierSingleMixin
ListNotifierSingleMixin
lib/get_state_manager/src/simple/list_notifier.dart: mixin ListNotifierSingleMixin on Listenable
is referenced in _updatersGroupIds
GetStateUpdate
lib/get_state_manager/src/simple/list_notifier.dart: typedef GetStateUpdate = void Function();
This is basically the same as setState, you could put any function that is void
with no parameters, but that may produce undesired results.
_updatersGroupIds
Your definition of _updatersGroupIds
is close, but incorrect. _updatersGroupIds
is a Object
reference key, mapped to a reference to a ListNotifierSingleMixin
. _updatersGroupIds
would be
{
Object(): ListNotifierSingle(), // references to instances of each object
Object(): ListNotifierSingle(),
}
addListenerId
When running the below code, an Object
reference key, and ListNotifierSingle
reference are added to the _updatersGroupIds
. Then the instance of ListNotifierSingle
has it's addListener
function run, which adds the setState
function to the ListNotifierSingle
instance's _updaters
. See the below functions with comments
controller?.addListenerId(
widget.id,
setState(() {}),
);
Disposer addListenerId(Object? key, GetStateUpdate listener) {
// init if is null
_updatersGroupIds![key] ??= ListNotifierSingle();
// pass listener (setState) to ListNotifierSingle reference addListener
// function, this is defined in ListNotifierSingleMixin
return _updatersGroupIds![key]!.addListener(listener);
}
@override
Disposer addListener(GetStateUpdate listener) {
// no disposed
assert(_debugAssertNotDisposed());
// add setState to list
_updaters!.add(listener);
// return a function that removes setState from list (_updaters) when called
return () => _updaters!.remove(listener);
}
update
lib/get_state_manager/src/simple/get_controllers.dart: void update([List<Object>? ids, bool condition = true])
, which is part of abstract class GetxController extends ListNotifier with GetLifeCycleMixin
, which extends ListNotifier
, which extends ListNotifierGroupMixin
; therefore, refreshGroup
is defined under ListNotifierGroupMixin
.
When calling update(["idExample"]);
all passed ids are refreshed using refreshGroup
in update
, see below.
void update([List<Object>? ids, bool condition = true]) {
// not called in our example
if (!condition) {
return;
}
// not called in our example
if (ids == null) {
refresh();
// called in our example, defined under ListNotifierGroupMixin
// all passed ids are refreshed using refreshGroup
} else {
for (final id in ids) {
refreshGroup(id);
}
}
}
refreshGroup
calls _notifyGroupUpdate
void refreshGroup(Object id) {
assert(_debugAssertNotDisposed());
_notifyGroupUpdate(id);
}
_notifyGroupUpdate
checks if passed id is in _updatersGroupIds
, and if it is it runs _notifyUpdate
function for the single instance of ListNotifierSingleMixin
with the key id in the _updatersGroupIds
map.
void _notifyGroupUpdate(Object id) {
// check if exists
if (_updatersGroupIds!.containsKey(id)) {
// run _updatersGroupIds function for specified
// ListNotifierSingleMixin instance
_updatersGroupIds![id]!._notifyUpdate();
}
}
_notifyUpdate
iterates over all the functions in _updaters
, which are for a single instance of ListNotifierSingleMixin, which in our case is the one with key "idExample"
. There should only be one function in _updaters
in our example, which is the setState function of the widget in our example. There will only be multiple values in _updaters
if you call controller?.addListenerId
with the same id more than once.
void _notifyUpdate() {
// if (_microtaskVersion == _version) {
// _microtaskVersion++;
// scheduleMicrotask(() {
// _version++;
// _microtaskVersion = _version;
// make sure list is not null
final list = _updaters?.toList() ?? [];
// iterate over functions in _updaters queue
// when the function is run it removes it's contained function
// from _updaters, see addListener. The inner function should be
// setState
for (var element in list) {
element();
}
// });
// }
}