Upon now I always use getx observable declarations like this:
var someString = ''.obs;
var someNumber = 0.obs;
and so on...
But what if some variables don't have an initial value at first, and I actually want them to be null and later change them?
For non null-safe (pre Dart 2.12), you can declare your observable variables like this:
final someVariable = Rx<Type>();
For example:
final someString = Rx<String>();
final someNumber = Rx<int>();
And for null-safety (Dart 2.12 or later), Just use Rxn<Type> instead of Rx<Type>.
For example:
final someString = Rxn<String>();
final someNumber = Rxn<int>();
If you don't have an initial value for your [Rx] value (at the first time), you need to use
final Rx<YourObject?> yourObject = (null as YourObject?).obs;
Or for more organizing your code, you can create a separate class, like this
class RxNullable<T> {
Rx<T> setNull() => (null as T).obs;
}
and use:
final Rx<YourObject?> yourObject = RxNullable<YourObject?>().setNull()
If anyone else does face this problem.
final Rx<YourObject?> yourObject = (null as YourObject?).obs;
will work.
but if there's any message that says "Unnecessary cast. Try removing the cast.", just add this comment
// ignore: unnecessary_cast
above the line, and then save.
You can declare your observable variables like this for (dart 2.18 and above):
Rx varName = (T()).obs;
Getx in flutter,
I search lot of time How class as observable
so fixed,
Like this ,
Rx<HomeModel?> mainModel =HomeModel(content: []).obs;
Related
I am building a Flutter app with a ChangeNotifier provider.
When the app is started, I make a call to the Firebase api and save the results in a Provider variable:
Map<DateTime,List> datesMap;
How can I define another variable in the same Provider, based on the first variable?
for example:
List newList = datesMap[DateTime.now()]
If I try to do it I get an error:
The instance member 'params' can't be accessed in an initializer
And if I place the second variable in a Constructor, I will get an error because the first variable datesMap is null until the Firebase api is completed.
Example code:
class ShiftsProvider with ChangeNotifier {
Map<DateTime,List> datesMap;
List newList = datesMap[DateTime.now()];
Future<void> getDatesMapfromFirebase () {
some code...
datesMap = something;
notifyListeners();
return;
}
You can make getter:
List get newList {
return datesMap[DateTime.now()];
}
You can use late like this:
Map<DateTime,List>? datesMap;
late List? newList = datesMap?[DateTime.now()];
since like I see that datesMap variable is related to the specific class, you can mark it with static keyword, this will fix your problem:
class ShiftsProvider with ChangeNotifier {
static Map<DateTime,List> datesMap;
List? newList = datesMap[DateTime.now()];
Future<void> getDatesMapfromFirebase () {
some code...
datesMap = something;
notifyListeners();
return;
}
}
just note that if you want to use that static variable, you can access it like this:
ShiftsProvider.datesMap
I'm new to use Websocket in Flutter.
So I was reading some post (https://blog.logrocket.com/using-websockets-flutter/) to understand how to use websocket, and I have some question.
The guy in this post declare _etheWebsocket,_btcWebsocket variables as late as below.
class CoinbaseProvider {
late final WebSocketChannel _ethWebsocket;
late final WebSocketChannel _btcWebsocket;
CoinbaseProvider()
: _ethWebsocket = WebSocketChannel.connect(
Uri.parse('wss://ws-feed.pro.coinbase.com'),
),
_btcWebsocket = WebSocketChannel.connect(
Uri.parse('wss://ws-feed.pro.coinbase.com'),
);
But why not just declare without late like below??
What is the reason have to declare WebsocketChannel as late.
class CoinbaseProvider {
final WebSocketChannel _ethWebsocket = WebSocketChannel.connect(
Uri.parse('wss://ws-feed.pro.coinbase.com'),
);
final WebSocketChannel _btcWebsocket = WebSocketChannel.connect(
Uri.parse('wss://ws-feed.pro.coinbase.com'),
);
CoinbaseProvider();
Short explanation
You declaration looks better, since in your context you have the values to the variables _ethWebsocket and _btcWebsocket
But why? a dive into late and null-safety features
What's null-safety in Dart? This is a recent released feature (2021) that in summary it prevent us, developers, to create variables of a type that can be implicity null, then avoiding runtime errors as TypeError: can't read property '...' of null
But in summary, unlike before null-safety, now:
All variables can't be null implicity by default!
Show me the code
Before null-safety:
This snippet below is a valid statement before null-safety, this variable can be a String 'hi!' or null
String myVariable; // null by default
After null-safety:
The same snippet now is a invalid statement, the variable should be initialized because her can't be null
String myVariable; // Error!
To declare a variable that can be null by default, as like before the null-safety update you should write as:
String? myVariable; // Note the '?' after the Type
But what if you want to declare variable without the value and without explicitly declaring that it can be null?
The answer is late!
late is just a Dart language feature to allow you say to the compiler: "Hey, I don't have the value of this variable right now, but I promise you I'll set her somewhere else before using it, so her can't be null"
late String myVariable;
print(myVariable); // Throws an LateInitializationError instead of printing 'null'
Conclusion
Given these features and the null-safety use them to avoid wasting time with null pointer exceptions and improve your code typing.
So there's no right or better way, all depends on the context.
For the exact example you used I would say they are identical, and your version would be better. My guess it that they made it like that, is to allow adding additional constructors, maybe like
CoinbaseProvider.customUrl(String eth, String btc)
: _ethWebsocket = WebSocketChannel.connect(
Uri.parse(eth),
),
_btcWebsocket = WebSocketChannel.connect(
Uri.parse(btc),
);
I've always been thinking that in Flutter GetX library, if I have an observable variable defined in this way
Rxn<MyClass> myObsObject = Rxn<MyClass>();
I could update its value by doing:
myObsObject(newValue) / myObsObject(null)
or in an EQUIVALENT WAY
myObsObject.value = newValue / myObsObject.value = null
Today I discovered that the 2 methods are NOT equivalent, since doing the first way in some cases (don't know how to reproduce ATM) doesn't retrigger the builder of a GetX widget, while the second does.
So what is/are the difference(s) between the two methods up above?
You can check the implementation of the first way and you will see that it is this:
T call([T? v]) {
if (v != null) {
value = v;
}
return value;
}
So basically, it still does the assignment to value except when the value is null.
So myObsObject(newValue) should work fine in all cases except when newValue is null
In kotlin we can check if the 'late' type variables are initialized like below
lateinit var file: File
if (this::file.isInitialized) { ... }
Is it possible to do something similar to this in Dart..?
Unfortunately this is not possible.
From the docs:
AVOID late variables if you need to check whether they are initialized.
Dart offers no way to tell if a late variable has been initialized or
assigned to. If you access it, it either immediately runs the
initializer (if it has one) or throws an exception. Sometimes you have
some state that’s lazily initialized where late might be a good fit,
but you also need to be able to tell if the initialization has
happened yet.
Although you could detect initialization by storing the state in a
late variable and having a separate boolean field that tracks whether
the variable has been set, that’s redundant because Dart internally
maintains the initialized status of the late variable. Instead, it’s
usually clearer to make the variable non-late and nullable. Then you
can see if the variable has been initialized by checking for null.
Of course, if null is a valid initialized value for the variable, then
it probably does make sense to have a separate boolean field.
https://dart.dev/guides/language/effective-dart/usage#avoid-late-variables-if-you-need-to-check-whether-they-are-initialized
Some tips I came up with from advice of different dart maintainers, and my self-analysis:
late usage tips:
Do not use late modifier on variables if you are going to check them for initialization later.
Do not use late modifier for public-facing variables, only for private variables (prefixed with _). Responsibility of initialization should not be delegated to API users. EDIT: as Irhn mentioned, this rule makes sense for late final variables only with no initializer expression, they should not be public. Otherwise there are valid use cases for exposing late variables. Please see his descriptive comment!
Do make sure to initialize late variables in all constructors, exiting and emerging ones.
Do be cautious when initializing a late variable inside unreachable code scenarios. Examples:
late variable initialized in if clause but there's no initialization in else, and vice-versa.
Some control-flow short-circuit/early-exit preventing execution to reach the line where late variable is initialized.
Please point out any errors/additions to this.
Enjoy!
Sources:
eernstg's take
Hixie's take
lrhn's take
leafpetersen's final verdict as of 2021 10 22
Effective Dart
Self-analysis on how to approach this with some common-sense.
You can create a Late class and use extensions like below:
import 'dart:async';
import 'package:flutter/foundation.dart';
class Late<T> {
ValueNotifier<bool> _initialization = ValueNotifier(false);
late T _val;
Late([T? value]) {
if (value != null) {
this.val = value;
}
}
get isInitialized {
return _initialization.value;
}
T get val => _val;
set val(T val) => this
.._initialization.value = true
.._val = val;
}
extension LateExtension<T> on T {
Late<T> get late => Late<T>();
}
extension ExtLate on Late {
Future<bool> get wait {
Completer<bool> completer = Completer();
this._initialization.addListener(() async {
completer.complete(this._initialization.value);
});
return completer.future;
}
}
Create late variables with isInitialized property:
var lateString = "".late;
var lateInt = 0.late;
//or
Late<String> typedLateString = Late();
Late<int> typedLateInt = Late();
and use like this:
print(lateString.isInitialized)
print(lateString.val)
lateString.val = "initializing here";
Even you can wait for initialization with this class:
Late<String> lateVariable = Late();
lateTest() async {
if(!lateVariable.isInitialized) {
await lateVariable.wait;
}
//use lateVariable here, after initialization.
}
Someone may kill you if they encounter it down the road, but you can wrap it in a try/catch/finally to do the detection. I like it better than a separate boolean.
We have an instance where a widget is disposed if it fails to load and contains a late controller that populates on load. The dispose fails as the controller is null, but this is the only case where the controller can be null. We wrapped the dispose in a try catch to handle this case.
Use nullable instead of late:
File? file;
File myFile;
if (file == null) {
file = File();
}
myFile = file!;
Note the exclamation mark in myFile = file!; This converts File? to File.
I'm using boolean variable when I initiliaze late varible.
My case is :
I'm using audio player and I need streams in one dart file.
I'm sharing my code block this methodology easily implement with global boolean variables to projects.
My problem was the exception i got from dispose method when user open and close the page quickly
I need a list of my map assigned to a List variable outside of this map. How can I do that?
class Lists {
late var list = Map<int, List<int>>();
Lists() {
list[0] = [];
list[1] = [];
list[2] = [];
list[3] = [];
}
}
In another file I then try to assign the list[0] to a List variable:
List<int> listOutside = Lists.list[0];
I then receive this error:
What does the "?" mean and how can I fix that?
Thanks for the help.
Greetings
there are 2 major problems that are there.
You are trying to access a static variable outside a class which is wrong. This problem can be fixed by adding List<int> listOutside = Lists().list[0]; (notice the parentheses)
By accessing the list[0]; you are saying that the element always exists but here the compiler comes into play saying that this is a nullable list (List<int>?) which you are trying to assign to List<int> which is not possible as both have different types. This problem can be quickly fixed by using ! at the end Lists().list[0]!;
But note that this comes with a side effect that if there is no element at 0the index then this will throw NPE.
NOTE: Always avoid using ! in expressions where you are not sure that it is not nullable.
Seems to work when adding a "!".
List<int> listOutside = Lists.list[0]!;
I have no clue why, but anyways it works :D
? means that this field can have the null value. The Dart language now supports sound null safety and to indicate that a variable might have the value null, just add ? to its type declaration:
int? aNullableInt = null;
For your problem, you try to access list variable as a static usage and you use late but you initialize field immediately. For this reason, you can omit late and put static.
This usage could solve your problem:
static var list = Map<int, List<int>>();
List<int> listOutside = Lists.list[0];