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
Related
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();
}
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.
My Bloc state isn't updating and I've found the problem to potentially be a Map<String, Map<String, String> property that isnt being compared properly. Please correct me if I'm wrong, but the state updates when the other properties change, just not when the imageUrls property updates.
These are my state objects
abstract class PropertiesState extends Equatable {
const PropertiesState();
}
class PropertiesLoaded extends PropertiesState {
final int count;
final List<Property> properties;
final Map<String, Map<String, String>> imageUrls;
const PropertiesLoaded({
this.count,
this.properties,
this.imageUrls,
});
#override
List<Object> get props => [count, properties, imageUrls];
}
The imageUrls field can have any string key/value pairs.
I haven't been able to find any information on how I should do this.
Thanks for the help!
Because Equatable is trying to compare the reference of your List or Map object because they are not primitive type like int. You can try deep copy a Map object (using Map.from()) and replace the old one, and use hash code to do the comparison.
From Docs
Equatable properties should always be copied rather than modified. If
an Equatable class contains a List or Map as properties, be sure to
use List.from or Map.from respectively to ensure that equality is
evaluated based on the values of the properties rather than the
reference.
emit(PropertiesLoaded(count, properties,
Map<String, Map<String, String>>.from(imageUrls)));
I'm trying to make a generic dropdown widget with the current code:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
abstract class FormDropdownProtocol {
String get title;
}
class FormDropdown<FormDropdownProtocol> extends StatelessWidget {
const FormDropdown({this.value, this.items, this.onChanged});
final FormDropdownProtocol value;
final List<FormDropdownProtocol> items;
final ValueChanged<FormDropdownProtocol> onChanged;
#override
Widget build(BuildContext context) {
return DropdownButton<FormDropdownProtocol>(
value: value,
items: items.map<DropdownMenuItem<FormDropdownProtocol>>(
(FormDropdownProtocol value) {
return DropdownMenuItem<FormDropdownProtocol>(
value: value,
child: Text(value.title),
);
}).toList(),
onChanged: onChanged,
);
}
}
But I'm getting the following error when trying to use the title:
Error: The getter 'title' isn't defined for the class 'Object'.
- 'Object' is from 'dart:core'.
Try correcting the name to the name of an existing getter, or defining a getter or field named > 'title'.
child: Text(value.title)
What am I doing wrong?
class FormDropdown<FormDropdownProtocol> extends StatelessWidget {
This defines a generic class (FormDropdown) with a type parameter named FormDropdownProtocol. That type parameter could be named anything; it might be clearer to understand what's going on if it were:
class FormDropdown<T> extends StatelessWidget {
Dart generics aren't like C++ templates where the class is instantiated with the formal type information before everything is resolved. With Dart, everything is resolved before the generic is instantiated with a type.
In the case of class FormDropdown<T>, nothing is known about T. No constraints are given, so it can only be deduced to be an Object. And indeed, Object has no title member.
If you instead do class FormDropdown<T extends FormDropdownProtocol>, then T is now constrained to be FormDropdownProtocol, which does have a title member.
I have a final and I am trying initializing that in the constructor. It is giving me error & If I don't make it final I get a warning.
This class (or a class which this class inherits from) is marked as '#immutable', but one or more of its instance fields are not final: GenderCard.genderSvg",
My Code:
GenderCard({#required this.genderType}) {
genderSvg = '/assets/' + 'genderType' + '.svg';
}
final String genderType;
final String genderSvg;
#override
Widget build(BuildContext context) {
final instance variables must be initialized in the initializer list. See the language guide.
Instance variables can be final but not const. Final instance
variables must be initialized before the constructor body starts — at
the variable declaration, by a constructor parameter, or in the
constructor’s initializer list.
Change your constructor to:
class GenderCard {
GenderCard({#required this.genderType})
: genderSvg = '/assets/$genderType.svg';
final String genderType;
final String genderSvg;
}