How to make optional datetime parameter in Flutter with null safety - flutter

I'm new to Flutter development and trying to learn.
I want to create a model with a constructor, one of which contains a field of type DateTime which is optional.
I tried by making it like this:
import 'package:equatable/equatable.dart';
class Customer extends Equatable {
final int indexs;
final DateTime apply_date;
Customer({
required this.indexs,
this.apply_date,
});
#override
List<Object?> get props => throw UnimplementedError();
}
But an error message appears like this
The parameter 'apply_date' can't have a value of 'null' because of its
type, but the implicit default value is 'null'. Try adding either an
explicit non-'null' default value or the 'required' modifier.
I've tried to learn from this and this reference, and what I understand there are 3 ways:
Include required modifiers
Set initial value
Nulllabel parameter / Fill it with (?) => I don't understand this
So how to do this properly?
I don't want to make this field required, because it's optional.
I also don't know what to fill if I want to fill it with an initialvalue.
Thank you!

Making the attribute nullable is the same as making it an optional attribute.
You can do that by adding ? behind the attribute's type.
class Customer extends Equatable {
final int indexs;
final DateTime? apply_date;
Customer({
required this.indexs,
this.apply_date,
});
#override
List<Object?> get props => throw UnimplementedError();
}

Related

How to add positional parameter with default value in GetX Flutter

I'm trying to add a positional parameter with default value with GetX in Flutter but I got an error.
import 'package:get/get.dart';
class MainParentModel {
int id;
String name;
RxBool isSelected;
MainParentModel(this.id, this.name, {this.isSelected = false.obs});
}
Error: The default value of an optional parameter must be constant.dartnon_constant_default_value
How can I add this default value?
Thank you
.obs is a getter that returns an Rx<T> of whatever type you add it to. But it's not a constant value so that won't work. You'll notice that if you just use a regular bool and remove .obs the error goes away.
If it has to be type RxBool, then I'm pretty sure your only option is making it required and passing in false.obs whenever you create a new MainParentModel.
MainParentModel(this.id, this.name, {required this.isSelected});
Then a new instance would look like this.
final model = MainParentModel(1, 'Bob', isSelected: false.obs);
Another approach is creating reactive objects where you'll need, I never use classes with reactive variables, for example:
class MainParentModel {
int id;
String name;
bool isSelected;
MainParentModel(this.id, this.name, {this.isSelected = false});
}
In the controller:
Rx<MainParentModel> mainParentModel = MainParentModel().obs;
if you need to rebuild the page when changing isSelected variable:
mainParentModel.update((model){
model.isSelected = true;
});
or
mainParentModel.refresh();
This will trigger rebuild all Obx that you use mainParentModel.value.?

Riverpod state class default value

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;
}

Why do we need to add late modifier while declaring the variables?

Its a general question. Not coding related. Everywhere I follow flutter tutorials I see the simple variable declarations. But when I write the same code (I think because of updating) it requires to add late modifier.
late modifier can be used while declaring a non-nullable variable that's initialized after its declaration. Declaration of variables that will be initialize later is done using late modifier.
That's what I read on the google.
Why it is necessary to declare non-nullable variables. Vscode always underlines the variable showing error. But ate modifier. What do we need to change when we change variables to late. Cause its making following tutorials very difficult. set state is not helping.
This is because of null safety in Dart. There are a lot of guides out there written before null safety was introduced.
It is ok to declare a class member as nullable with the ?, so this is valid, and means that i can have the value null, therefore it is allowed without initialising its value:
int? i;
If you don't use the ?, you can still declare a member, but you have to assign a value to it, this is also valid:
int i=1;
But this will be invalid, since you say that i can't be null, and you do not assign a value to it:
int i;
And here comes the late keyword. By using this you "promise" Dart, that you will initialise this value later, for example in an initState method like this:
class MyWidget extends StatefulWidget {
const MyProfile({Key? key}) : super(key: key);
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late int _i;
#override
void initState() {
super.initState();
_i = 1;
}
You have to keep this "promise", otherwise you will get a runtime error.
Another option is that you create a constructor for the class that has required members, it will ensure that these values can't miss in any instance created, so Dart is ok with it:
class MyClass {
int i;
MyClass({required this.i});
}
In this last example, if you omit required keyword, you will get an error, because i is not nullable, and if it is not required by the constructor, it could be null. Still, it will work without required, if you make i nullable by declaring it like int? i;.
the late modifier is part of the new null-safety by dart it's used to tell the compiler to treat this variable as non-nullable and will be initialized later without it the compiler will treat the variable as nullable and give error
late String name;
#override
void initState() {
super.initState();
name = "example";//the compiler will not see this to know that you have initialized the variable
}
#override
Widget build(BuildContext context) {
if(name=="example"){
...
}
}

Flutter: Equatable props getter with optional parameter

I have an object that extends Equatable and contains optional parameters. If I try to add that parameter into the props getter, I get an error The element type 'String?' can't be assigned to the list type 'Object'. However, not adding it would imply equality within objects that have a different value or no value in that parameter.
class Company extends Equatable {
final String name;
final String? logo;
....
#override
List<Object> get props {
return [
name,
logo, //error here
....
What would the appropriate solution be?
The base Equatable.props getter is declared to return a List<Object?>, not List<Object>. Fixing your override to match would allow you to store null.
i suggest to use dynamic instead of Object

(Flutter) Upgrading To NullSafety

Hi, I'm trying to upgrade an old code to null safety, but since I'm just starting to learn null safety, I'm encountering errors and I couldn't figure out why, I would be glad if you could help.
I've tried a few things, but I'm leaving it in its original form so as not to confuse things further for you. source code: https://github.com/MarcusNg/flutter_instagram
auth_event
part of 'auth_bloc.dart';
abstract class AuthEvent extends Equatable {
const AuthEvent();
#override
bool get stringify => true;
#override
List<Object> get props => [];
}
class AuthUserChanged extends AuthEvent {
final auth.User user;
const AuthUserChanged({required this.user});
#override
List<Object> get props => [user];
}
class AuthLogoutRequested extends AuthEvent {}
auth_state
part of 'auth_bloc.dart';
enum AuthStatus { unknown, authenticated, unauthenticated }
class AuthState extends Equatable {
final auth.User user;
final AuthStatus status;
const AuthState({
this.user,
this.status = AuthStatus.unknown,
});
factory AuthState.unknown() => const AuthState();
factory AuthState.authenticated({required auth.User user}) {
return AuthState(user: user, status: AuthStatus.authenticated);
}
factory AuthState.unauthenticated() =>
const AuthState(status: AuthStatus.unauthenticated);
#override
bool get stringify => true;
#override
List<Object> get props => [user, status];
}
The parameter 'user' can't have a value of 'null' because of its type, but the implicit default value is 'null'.
Try adding either an explicit non-'null' default value or the 'required' modifier.
(this.user)
auth_bloc
part 'auth_event.dart';
part 'auth_state.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final AuthRepository _authRepository;
StreamSubscription<auth.User> _userSubscription;
AuthBloc({
required AuthRepository authRepository,
}) : _authRepository = authRepository,
super(AuthState.unknown()) {
_userSubscription =
_authRepository.user.listen((user) => add(AuthUserChanged(user: user)));
}
#override
Future<void> close() {
_userSubscription?.cancel();
return super.close();
}
#override
Stream<AuthState> mapEventToState(AuthEvent event) async* {
if (event is AuthUserChanged) {
yield* _mapAuthUserChangedToState(event);
} else if (event is AuthLogoutRequested) {
await _authRepository.logOut();
}
}
Stream<AuthState> _mapAuthUserChangedToState(AuthUserChanged event) async* {
yield event.user != null
? AuthState.authenticated(user: event.user)
: AuthState.unauthenticated();
}
}
Non-nullable instance field '_userSubscription' must be initialized.
Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
A value of type 'StreamSubscription<User?>' can't be assigned to a variable of type 'StreamSubscription'.
Try changing the type of the variable, or casting the right-hand type to 'StreamSubscription'.
The argument type 'User?' can't be assigned to the parameter type 'User'.
(_authRepository.user.listen((user) => add(AuthUserChanged(user: user)));)
The receiver can't be null, so the null-aware operator '?.' is unnecessary.
Try replacing the operator '?.' with '.'. (_userSubscription?.cancel();)
The operand can't be null, so the condition is always true.
Remove the condition. (event.user != null)
The parameter 'user' can't have a value of 'null' because of its type, but the implicit default value is 'null'. Try adding either an explicit non-'null' default value or the 'required' modifier. (this.user)
This error is happening because you have set your user variable as non-nullable and final. If you're new to nullsafety, to make the variable nullable, simply add a ? at the end of the type. There's two different routes to fix this one:
If you know that user is never ever going to be null, then simply do this:
const AuthState({
required this.user,
this.status = AuthStatus.unknown,
});
required tells the compiler "hey, this variable can never be set to null".
If user could be null, then do final auth.User? user;. The ? tells the compiler "hey, this variable can potentially be null`.
However in your case you must go with option 2 because of this code:
factory AuthState.unauthenticated() =>
const AuthState(status: AuthStatus.unauthenticated);
Since you don't pass user in the constructor you have to tell Dart that the variable can be null.
Non-nullable instance field '_userSubscription' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Add the late identifier here: late StreamSubscription<auth.User> _userSubscription;. This tells Dart that yes, the variable won't ever be null, I am just not setting it when I define it. I will set it to a non-null value before it gets used elsewhere.
A value of type 'StreamSubscription<User?>' can't be assigned to a variable of type 'StreamSubscription'. Try changing the type of the variable, or casting the right-hand type to 'StreamSubscription'.
Dart has stricter type checking now due to nullsafety, so for this one I think you need to change this line: StreamSubscription<auth.User> _userSubscription; into StreamSubscription<auth.User?> _userSubscription; (don't forget to add late like I mentioned earlier).
The argument type 'User?' can't be assigned to the parameter type 'User'. (_authRepository.user.listen((user) => add(AuthUserChanged(user: user)));)
In AuthUserChanged, you made auth.User user required. In that line of code, you are assigning a nullable variable to a non-nullable variable. So you have to change the class to look like this:
class AuthUserChanged extends AuthEvent {
final auth.User? user;
const AuthUserChanged({this.user});
#override
List<Object> get props => [user];
}
The receiver can't be null, so the null-aware operator '?.' is unnecessary. Try replacing the operator '?.' with '.'. (_userSubscription?.cancel();)
This is just a warning, all you have to do is make _userSubscription?.cancel(); into _userSubscription.cancel();. The warning is just letting you know that _userSubscription will never be null so you don't need to check if it is null using the ? operator.
The operand can't be null, so the condition is always true. Remove the condition. (event.user != null)
Same thing as the previous condition. Just a warning, all you have to do is remove that line of code because it is unnecessary.
Hope this helps you! I'd suggest looking at Dart's documentation for nullsafety to get a better idea of how all the new operators and keywords work.