(Flutter) Upgrading To NullSafety - flutter

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.

Related

How to make optional datetime parameter in Flutter with null safety

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

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

The argument type 'dynamic Function(bool)?' can't be assigned to the parameter type 'void Function(bool?)?' Flutter

recently I faced this issue but I don't have any idea where is it. I am getting this error.
The argument type 'dynamic Function(bool)?' can't be assigned to the parameter type 'void Function(bool?)?'
Where am I making mistake?
class GroceryTile extends StatelessWidget {
GroceryTile({Key? key, required this.item, required this.onComplete})
: textDecoration = item.isComplete != null
? TextDecoration.lineThrough
: TextDecoration.none,
super(key: key);
final GroceryItem item;
final Function(bool) onComplete;
final TextDecoration textDecoration;
#override
Widget build(BuildContext context) {
return Container(
height: 100.0,
// TODO: 20 Replace this color
color: Colors.red,
);
}
Widget buildCheckbox() {
return Checkbox(
value: item.isComplete,
onChanged: **onComplete** //error is occurring here
);
}
}
final void Function(bool?)? onComplete
Checkbox is a void function so u should also put void on you declaration for OnComplete if your not using nullsafety just remove this sign ?
Your member onComplete is of type
void Function(bool)
It needs to be
void Function(bool?)
It must be able to deal with a null parameter.
The error occured because checkbox onChanged methods needs a Function(bool?)? but you have supplied a dynamic Function(bool)?. void Function(bool?)? means that the value for a checkbox onChanged method can be a null or a function with a null parameter.
simple changing the signature of your onChange method will fix the issue.
final void Function(bool?) onComplete;
Better check the documentation in these kind of errors to find out the correct function signatures.
If you typed this code from reading the book Flutter Apprentice, you may want to replace final Function(bool) onComplete; with what was in the book, i.e. final Function(bool?)? onComplete;
The question marks we see here are Dart's notation to allow a variable to be null. The outermost question mark Function()? onComplete means that the onComplete function may be null. The innermost question mark Function(bool?) means that the calling argument may be either a boolean or a null.
This null protection also exists in JavaScript and TypeScript.See this article of FreeCodeCamp: https://www.freecodecamp.org/news/how-the-question-mark-works-in-javascript/

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 - The argument type 'String?' can't be assigned to the parameter type 'String'

After Flutter 2.0 null safety were introduced, but when it comes to getting the String variable Flutter is showing the error -
The argument type 'String?' can't be assigned to the parameter type 'String'.dart(argument_type_not_assignable)
I want to have a constructor, How to implement constructor with Null Safety? Also how to get the String variable.
I did used toString() method also but not sure whether it is the genuine way.
class OnbordingSliderTile extends StatelessWidget {
final String? title;
OnbordingSliderTile({this.title});
#override
Widget build(BuildContext context) {
return Container(
//This Text widget is showing the error
child: Text(title)
);
}
}
Type String? means it has to have a string or null in variable of this type. In your case, probably, you'd always need a title and this variable should not be nullable.
So the solution is to change type to String and add required keyword to named parameter in constructor.
class OnbordingSliderTile extends StatelessWidget {
final String title;
OnbordingSliderTile({required this.title});
#override
Widget build(BuildContext context) {
return Container(
child: Text(title)
);
}
}
I did used toString() method also but not sure whether it is the genuine way
if you use a toString() on String? it'll return value, if there is any, or simply "null" string, if it's null.
The error you got is caused by Text widget. It awares you that you were displaying as text something that can potentially contain null.
UPD: and please don't follow this advise:
use Text(title!) instead of Text(title) – Mehran Ullah
this is a very bad practice, destroying the whole point of null safety. Doing so means you insist there is not a null value within a nullable type variable, which can be avoided in most cases and is not safe as you might cause null error if you wouldn't have provided a value to your variable, which can happen as named parameter you were using was not required and didn't have any initial value, so you could simply not pass anything to your widget in constructor.
UPD
Is this the recommended way? Because what if I do not want the variable to be required? – user4258194
Text widget needs a String. It's a positional parameter, which means it's required. You can't have a Text widget without a text.
What are your options to provide it in your example widget?
a required parameter as shown in the initial part of the answer;
an optional non-nullable parameter with initial value:
class OnbordingSliderTile extends StatelessWidget {
final String title;
//'Placeholder text' will be in title variable if you
// haven't provided a value for it when initializing OnbordingSliderTile
OnbordingSliderTile({this.title = 'Placeholder text'});
#override
Widget build(BuildContext context) {
return Container(
child: Text(title)
);
}
}
a nullable parameter with no initial value as you had it and a null check when using it in Text widget:
class OnbordingSliderTile extends StatelessWidget {
final String? title;
//you can skip providing this parameter to the constructor and then it will be null
OnbordingSliderTile({this.title});
#override
Widget build(BuildContext context) {
return Container(
// You have to check if value in your variable is a String, not null
// and make sure you provided some text for the case if it null
child: Text(title ?? 'Placeholder text')
);
}
}
If you need this text to be empty when no value was provided, use empty String as Placeholder text in any of provided options.