Flutter. GetX the improper use of a GetX has been detected. with the RxList - flutter

RxList<ParticipantModel> participantList = RxList.empty();
........
Obx( <==== [Get] the improper use of a GetX has been detected. with the RxList
() => NCChipEditableList<ParticipantModel>(
key: const Key('StudioEditPage txt_add_main_actors'),
title: 'txt_add_main_actors'.tr.format(
[MediaConst.maxArtistTagLength],
),
addButtonText: 'txt_add_actor'.tr,
chipList: controller.media.participantList,
maxTextLength: MediaConst.maxArtistTagLength,
onAddChip: controller.onAddParticipant,
onDeleteChip: controller.onDeleteParticipant,
onEditedChip: controller.onEditedParticipant,
),
),
actually, i dont understand how to resolve this problem. I use initiated RxList as the parameter NCChipEditableList

Obx is made to update when an Rx<T> observable variable is used inside of it, so it should be used like this:
// controller
Rx<String> txt = "text".obs;
// view
Obx(() => Text(txt.value)), // works fine
if no Rx<R> variables are used inside of it :
// view
Obx(() => Text("normal raw text")), // throws the error
Then your error will be thrown from the Getx library to inform you that since there is nothing to update inside the Obx, there is no need to use it.
you should set your widget directly without wrapping with Obx if you don't need it.

Related

Obx widget throws an error: ''the improper use of a GetX has been detected..."

I am using only Text widget inside an Obx widget but I'm getting this issue
-
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
please solve this error.
This error is thrown when you use an Obx widget without using really any Rx<T> variables inside of it, so the library notifies you that there is no point wrapping some widget that will not update based on some Rx<T> with the Obx.
Example of what causes this:
Controller:
class ControllerExample extends GetxController {
Rx<String> exampleText = "example".obs;
}
View:
// Right
Obx(() => Text(Get.find<ControllerExample>().exampleText.value)), // Text widget takes an Rx<T> variable as value, so it will work fine
// Wrong
Obx(() => Text("anotherExample")), // this is just showing a Text widget which do not have any relation to an Rx<T> so it will throw the error you're facing
using Obx(()=>) without the Observable variable in view will throw this error.
you have to use observable variables like i.e.
RxString a = "".obs;
RxInt b = 0.obs;
RxBool c = false.obs;
you have to use .value when accessing value in the view.
Obx(
() => Text(controller.a.value),
),

Is it fine to use .obs on Widgets after assing to var?

Hey am Wondering is it fine to use ".obs" to widget like this :
var name= Text("foo").obs;
return name.value;
using .obs on whole widgets like this:
Rx<Widget> textWidget = Text("test").obs;
will work fine, as you can try in this exmaple:
// ...
child: Obx(
() => controller.textWidget.value,
),
and every time you assign a value to it, it will update, but this will cuase very unecessary recreating of that Text widget, since each time, a new Text widget will set in your UI, which may cause perferemnce issue.
instead you should declare primitive types as Rx observales then assignig them inside your UI widgets:
final textVal = "test".ons;
child: Obx(
() => Text(controller.textVal.value),
),

StreamProvider in flutter: do not always change the UI but set when

I have a StreamProvider as follows
Stream<List<Match>> streamMatches() =>
FirebaseFirestore.instance.collection('matches').snapshots()
.map((list) => list.docs.map((doc) => Match.fromJson(doc.data(), doc.id)).toList());
StreamProvider<List<Match>>(create: (context) => streamMatches(), initialData: [])
which I use it in a ListView inside a StatelessWidget
ListView(
shrinkWrap: true,
children: _getMatchesWidget(context.watch<List<Match>>())
This is nice because any update to the DB refreshes the stream. However I would like to not have the UI showing the list view change constantly in real-time for new updates (since I believe it might be a bad UX).
I would like to use a pull-on refresh (RefreshIndicator) and update the list view only onRefresh
Of course I also would like to update the list of matches in background when the user is not visualizing the list (e.g. he paused the app).
How can I tell the StreamProvider to update only in certain cases or what other Provider should I use for it?
You can change this code below:
context.watch<List<Match>>()
to this:
context.read<List<Match>>()
.read returns the List<Match> object without listening to it while .watch makes the widget listen to changes on List<Match>
Edit:
When you want to update the List<Match> object, you can call setState which will rebuild the UI and read the latest value of the stream.
You mentioned that you want to refresh the list using the RefreshIndicator.
You can just add the setState to your onRefresh method like below:
RefreshIndicator(
onRefresh: () {
setState(() {
//This refreshes the list
});
}
)

How to rebuild the state after pushing a new screen

When i push a new screen onTap with Navigator and pass a new class constructor, how can I have that new screen updates every time _playerTimer updates without having to click again
Since the state of my new class only updates onTap, please help!
The build method of FullScreenDialog is called once since its only being built when onTap is pressed
InkWell(
onTap: () {
return Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => FullScreenDialog(
_playerTimer,
));
},
child: VideoPlayer(
_controller,)
);
you have to use setState to rebuild the UI.
ex:
setState((){
_playerTimer = _playerTimer + 1;
});
that's about all the help I can give without seeing the rest of your code
When you instantiate a new class (in your code would be the FullScreenDialog) by passing an attribute, you're only saying to your code that a new class will be initialized by using the argument you provided.
If you want your FullScreenDialog class to always be updated when _playerTimer changes, you must observe this attribute inside this class by using setState(), which is a build-in function for StatefulWidgets that makes apps' UI update every time the observable attribute changes.
Example:
setState( () {
_playerTimer = getUpdatedTimer();
});
Supposing that inside getUpdatedTimer() method you will manage the logic for updating this variable, calling a service or so.
If you want this variable to be updated without interacting with interface, you probably will need a timer, too. Check this question to more details about it.
If you're starting with Flutter development, I suggest you to read this article (Adding interactivity to your Flutter app) about state management
and
setState method documentation.
Hope that helps.

Flutter: how to access context from Dismissible onDismissed

I'm trying to implement undo for a Dismissible list item in Flutter, and having problems accessing a BuildContext.
I have a flutter list, where each item is a card. The card is wrapped in a Dismissible, which allows the user to swipe to dismiss the card. Dismissible automatically removes the item from the list. Dismissible also has an onDismissed event - I'm using this event to update the item in Redux state store (setting an isDismissed flag to true), then show a snackBar which contains an UNDO button.
This is where I'm running into problems. I want the UNDO button to restore the item, by dispatching another action to the Redux store to set isDismissed to false. To do this I need a context, from which to get the store dispatcher. However when I try with the below code, I get an error when clicking on UNDO:
Looking up a deactivated widget's ancestor is unsafe
class ProductCard extends StatelessWidget {
final Product product;
const ProductCard(this.product);
#override
Widget build(BuildContext context) {
return Dismissible(
key: Key(product.id.toString()),
onDismissed: (direction) {
StoreProvider.of<AppState>(context).dispatch(DismissAction(product));
// Then show a snackbar to allow undo
Scaffold.of(context).showSnackBar(
SnackBar(
content: Row(
children: <Widget>[
Expanded(child: Text("Dismissed ${product.title}"),),
FlatButton(
onPressed: () {
// THIS IS WHERE I GET THE ERROR
StoreProvider.of<AppState>(context).dispatch(UndoDismissAction(product));
},
child: Text("UNDO"),
)
],
)
)
);
},
child: Card(
child: ...
)
);
}
}
From what I've read, I think what is going on is that the line StoreProvider.of<AppState>(context) inside the undo button's onPressed action is trying to use a context which belongs to the Card, but because the card has been removed from the list, it no longer exists.
I'm not sure how to do work around this. I've read about flutter keys, and think the answer may be to start passing around some kind of global key, but I can't quite get my head around how that works. I gave it a go and ran into another problem with 'inheritFromWidgetOfExactType' was called on null. Are keys the solution to this problem? If so where do I create the key, do I pass it in to the widget, what type of key should I use etc, or is there a better solution?
Many thanks!
Extract a single copy of the store into a local variable, which will then get captured by all the lambdas below.
#override
Widget build(BuildContext context) {
var store = StoreProvider.of<AppState>(context);
return Dismissible(
...
store.dispatch(DismissAction(product));