How I can initialize _instance?
I get "Field '_instance' has not been initialized"
Or any other ways to get rid of nullSafety,,,
here is the code:
class ShoppingBasketData{
static late ShoppingBasketData _instance ;
late List<product> _basketItem;
ShoppingBasketData(){
_basketItem =<product>[];
}
List<product> get basketItem => _basketItem;
set basketItem(List<product> value) {
_basketItem = value;
}
static ShoppingBasketData getInstance(){
if (_instance == null){
_instance = ShoppingBasketData();
}
return _instance;
}
}
What is wrong is that you declared _instance to be late. What that means is that you as a developer promise to initialize it before you access it. If you break that promise, you get an exception.
It seems that you want null to be a valid value, so what you need to do is make it nullable:
static ShoppingBasketData? _instance
You may also want to look into How do you build a Singleton in Dart? so you don't have to reinvent the wheel, and you may want to look into Flutter state management, because singleton is probably the worst of all options.
Related
I saw these lazy singleton types in different projects, as far as I know, both are lazy singleton, the second one is better it's already final. But I'm curious about your opinions. What is the difference between them and what do you recommend to use them in different cases?
Thank you in advance.
// SINGLETON 1
class AppConstant1 {
static late AppConstant1? _instance;
AppConstant1._();
static AppConstant1 instance() {
_instance ??= AppConstant1._();
return _instance!;
}
}
// SINGLETON 2
class AppConstant2 {
AppConstant2._();
static final instance = AppConstant2._();
}
It seems that the first singleton pattern isn't even work:
// SINGLETON 1
class AppConstant1 {
static late AppConstant1? _instance;
AppConstant1._();
static AppConstant1 instance() {
_instance ??= AppConstant1._();
return _instance!;
}
}
it produces Uncaught Error: LateInitializationError: Field '_instance' has not been initialized. exception.
The reason for this is that :
_instance ??= AppConstant1._();
actually means
_instance = _instance ?? AppConstant1._();
In other words before assigning _instance we are reading _instance but _instance hasn't been initialized yet. Hence the error. So the code should be something like this:
class AppConstant1 {
static AppConstant1? _instance;
AppConstant1._();
static AppConstant1 instance() {
_instance ??= AppConstant1._();
return _instance!;
}
}
The main difference between first and second variants is that in first variant we could initialize singleton in particular moment and we can provide dependences as method arguments. That's it.
I am making first steps with Riverpod and just want to check if my understanding of handling changes of some data class properties using Riverpod is correct.
Imagine, I have a data class like that:
class MyClass {
final String name;
final int id;
const MyClass(this.name, this.id);
}
Then I create a StateNotifier:
class MyClassStateNotifier extends StateNotifier<MyClass> {
MyClassStateNotifier(MyClass state) : super(state);
void setName(String name) {
state.name = name;
}
}
And this won't work - UI will not be rebuilt after calling setName this way.
So I need to modify classes in the following way:
class MyClass {
final String name;
final int id;
const MyClass(this.name, this.id);
MyClass copyWith({name, id}) {
return MyClass(name ?? this.name, id ?? this.id);
}
}
and the StateNotifier as following:
class MyClassStateNotifier extends StateNotifier<MyClass> {
MyClassStateNotifier(MyClass state) : super(state);
void setName(String name) {
state = state.copyWith(name: name);
}
}
This pair will work and the UI will be rebuilt.
So, my question: does one always need to reinstantiate the object in this way?..
From my perspective, this is a bit strange (simple datatypes like String / int do not require this) and the boilerplate for copyWith method might become pretty huge if I have a dozen of object's properties.
Is there any better solution available for Riverpod or is it the only one and correct?..
Thanks in advance! :)
To trigger a state change you have to use the state setter. The implementation looks like this:
#protected
set state(T value) {
assert(_debugIsMounted(), '');
final previousState = _state;
_state = value;
/// only notify listeners when should
if (!updateShouldNotify(previousState, value)) {
return;
}
_controller?.add(value);
// ...
The internal StreamController<T> _controller needs to be triggered (add), to notify listeners (in this case riverpod) about updates.
By using state.name = something you're not informing the StateNotifier about a new state (not calling the state setter). Only your object holds the new value but nobody was notified.
Your state is mutable and that very often leads to such misbehavior. By using an immutable object you can prevent such errors in the first place. Write it yourself or use freezed.
Learn more about immutability in my talk
e.g. I have class ProfileModel with bunch of fields
many of them don't have default values unless they're initialising when I get user info from backend
with riverpod I need to write something like
final profileProvider = StateNotifierProvider((ref) => ProfileState());
class ProfileState extends StateNotifier<ProfileModel> {
ProfileState() : super(null);
}
I understand I need to pass something like ProfileState.empty() into super() method instead passing null
but in this case I have to invent default values for every ProfileModels fields
this sounds weird for me, I don't want to break my head to care about empty or default state of EVERY model in project
in my example there are no default values for user name, age etc
this is pure immutable class
what I'm doing wrong or missing?
or I can declare model as nullable extends StateNotifier<ProfileModel?>
but I'm not sure is this a good way
It is fine to use the StateNotifier with a nullable model. If you semantically want to indicate the value can be actually absent, I would say that that having null is alright.
However, what I usually do and what I think is better, is create a state model that contains the model, but also properties that relate to the different states the app could be in.
For example, while fetching the data for the model from an API, you might want to have a loading state to show a spinner in the UI while waiting for the data to be fetched. I wrote an article about the architecture that I apply using Riverpod.
A simple example of the state model would be:
class ProfileState {
final ProfileModel? profileData;
final bool isLoading;
ProfileState({
this.profileData,
this.isLoading = false,
});
factory ProfileState.loading() => ProfileState(isLoading: true);
ProfileState copyWith({
ProfileModel? profileData,
bool? isLoading,
}) {
return ProfileState(
profileData: profileData ?? this.profileData,
isLoading: isLoading ?? this.isLoading,
);
}
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ProfileState &&
other.profileData == profileData &&
other.isLoading == isLoading;
}
#override
int get hashCode => profileData.hashCode ^ isLoading.hashCode;
}
I have created a class, and I am getting this error
the operand can’t be null, so the condition is always false.
I am new to flutter and null safety. How can I fix this
class DatabaseHelper{
static late DatabaseHelper _databaseHelper;
factory DatabaseHelper{
if(_databaseHelper==null){
_databaseHelper=DatabaseHelper.internal();
return _databaseHelper;
}else{
return _databaseHelper;
}
}
DatabaseHelper.internal();
}
You've declared using late static late DatabaseHelper _databaseHelper;
Means it will never get null value before read time. And it's just a warning can be ignored.
But if you wish to check value is null or not, or can be null at time, make nullable
like static DatabaseHelper? _databaseHelper;
And when you return it you need to use bang operator ! in some cases,
like return _databaseHelper!;
More details
null-safety faq
null-safety
I'm new to flutter, and i bumped into a problem.
I have a Feed model in my app that looks like this:
import 'package:uuid/uuid.dart';
class Feed {
// Static Members
var uuid = new Uuid();
// Members
String id;
bool isScheduled;
DateTime createdTime;
DateTime feedingTime;
String deviceId;
// Constructors
Feed({this.feedingTime, this.deviceId, this.isScheduled}) {
id = uuid.v4();
createdTime = DateTime.now();
}
Feed.fromDevice(deviceId) {
Feed(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
}
}
Now i have my AddFeedForm that i'm trying to initialize with default values, in the InitState:
class _AddFeedFormState extends State<AddFeedForm> {
// Final Members
final _formKey = GlobalKey<FormState>();
final List<Machine> _devices = machinesFromServer;
// Members
Feed _feed;
#override
void initState() {
_feed = Feed.fromDevice(_devices.first.id);
super.initState();
}
But somehow after the initState the _feed parameter stays null!
Any ideas?
But somehow after the initState the _feed parameter stays null!
Are you sure this is the case, and not that you're getting a Feed instance that has null fields?
It looks like your named constructor is incorrect:
Feed.fromDevice(deviceId) {
Feed(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
}
Here you're calling the default Feed constructor inside a named constructor, but not doing anything with the result - this is creating another Feed and then throwing it away. The one returned by the named constructor has not been initialised.
What you probably wanted was this:
Feed.fromDevice(deviceId):
this(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
This makes the fromDevice constructor call the default constructor for initialisation of the instance, rather than creating another copy that goes unused.
Another option would be to make it a static method:
static fromDevice(deviceId) {
return Feed(deviceId: deviceId, feedingTime: DateTime.now(), isScheduled: false);
}
There wouldn't be much difference in this case.. Constructors seem nicer, but sometimes you might find that you want to a) make initialisation async (static methods can return a Future<Feed> but constructors cannot or b) do more processing of the arguments before they're passed to the real constructor that might not fit nicely in the initialiser call.