Flutter ignore field using Freezed - flutter

I'm using Freezed to generate my models. I want to ignore a specific key, so I'm using #JsonKey(ignore: true).
#freezed
class ObjectA with _$ObjectA {
const factory ObjectA({
#JsonKey(ignore: true)
List<ObjectB> ObjectBList,
}) = _ObjectA;
factory ObjectA.fromJson(Map<String, dynamic> json) => _$ObjectA(json);
}
When I try to run the build_runner, I'm getting the above exception:
The parameter ObjectBList of ObjectA. is non-nullable but is neither required nor marked with #Default
Why doesn't it been ignored?

The issue you are facing is with Dart syntax. When you declare parameters in constructors.
You can have a nullable parameter or a non-nullable parameter; this also apply to variables.
If you want the parameter you declared in the constructor to be non-nullable then it must either have a default value or the declared parameter must be prefixed with the required keyword.
You have three options:
Add the required keyword
required List<ObjectB> ObjectBList
Make it nullable
List<ObjectB>? ObjectBList
Give it a default value. With freezed this can be done with the #Default annotation
#Default([]) List<ObjectB> ObjectBList

Related

How to stub getter or a method of freezed dataclass in Flutter / Dart

I have a freezed data class with some fields. A getter method returns nested elements of one attribute for easier access.
#freezed
class Airport with _$Airport {
const Airport._();
const factory Airport({
required String identifier
required String type,
required List<Runway> runways,
}) = _Airport;
List<Ils> get allIls => runways
.map((runway) => runway.allIls)
.expand((ils) => ils)
.toList();
}
I use the Airport class inside a test where the getter allIls is called. I don't want to fill runways with legit data, instead I directly want to stub the getter method allIls and have it return a list of objects.
What I tried:
Mock the Airport class:
class MockAirport extends Mock implements Airport {}
My test:
test('',
() async {
final airport = MockAirport();
final ilsList = [ils1, il2];
when(airport.allIls).thenReturn(ilsList);
expect(...);
});
However, this gives me the following error:
type 'Null' is not a subtype of type 'List<Ils>'MockAirport.allIls
I have also tried a "normal" method instead of a getter, with the same results:
List<Ils> allIls2() => runways
.map((runway) => runway.allIls)
.expand((ils) => ils)
.toList();
...
when(airport.allIls2.call()).thenReturn(ilsList);
Any idea what I could do?
It looks like you missed a step in setting up your Mocks.
You need to add the GenerateMocks or GenerateNiceMocks attribute somewhere in your library to auto-generate your Airport mocks. I like to have the attribute in the same file as my test, though that can lead to repeated mocks throughout your test files. After you have the attribute, you generate the mock using build_runner.
Lastly, in your second example with the "normal" method, you don't need to add the .call() to your when statement. In fact, adding that will cause the call to fail. It should instead just be when(airport.allIls2()).thenReturn(ilsList).

Why does it keep saying initialize variable?

Below is the code I wrote. I need to initialize a variable called verificationID for later use. But I keep getting a red squiggly line with the text -
Final variable verificationID must be initialized
Non-nullable instance field vdi must be initialized.
Is this not how you initialize - final [datatype] [name]
I am brand new to flutter and could use any help!
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
enum NumberVerification {
SHOW_MOBILE_FORM_STATE,
SHOW_OTP_FORM_STATE,
}
class LoginScreen extends StatefulWidget {
final String verificationID;
String vdi;
#override
_LoginScreenState createState() => _LoginScreenState();
}
All variables in Dart are getting the value null if nothing else are specified. This is a problem in your case since both verificationID and vdi are specified as non-nullable types (no ? after the type name). So Dart complains about this problem.
Another problem is your final variable which also should be provided a value since this is a read-only variable which can only be assigned a value when initialized.
You therefore need to do:
Change the types to allow null.
Or, provide default value other than null.
Or, make a constructor of your class which gives values to your variables. These values can come from parameters to the constructor.
Because flutter and dart language are null safety. It means you should initialize your variables, and there will be no error regarding this in runtime. So when you write dart codes you must initialize them as follows:
1- In some cases you can init value directly as follows:
final String verificationID = 'value';
2- Or you can get the value from constructor of class.
final String verificationID;
LoginScreen(this.verificationID);
3- And also, you can declare that you will initialize the value later. This way you guarantee that you will initialize the value, so you should use it wisely.
late String verificationID;
verificationID = 'value';
4- Lastly, you may declare a value as nullable. This way, you don't need to initialize the variable directly.
String? verificationID;

Assigning subclass to a factory constructor in Dart

The Key class in Flutter has a factory constructor which works more like a variable.
abstract class Key {
const factory Key(String value) = ValueKey<String>;
// ...
}
But when I do something like that, I get an error:
class Foo {
Foo.empty();
const factory Foo(int i) = Bar; // Error
}
class Bar extends Foo {
Bar() : super.empty();
}
Actually I didn't quite get what is the use of this factory constructor cum variable. Can anyone please explain.
A constructor like:
const factory Key(String value) = ValueKey<String>;
is called a redirecting factory constructor. They're not well-known (even within the Dart and Flutter teams) since they aren't mentioned in the Dart Language Tour, but they're mentioned in the Dart Language Specification in (as of version 2.10) section 10.6.2:
A redirecting factory constructor specifies a call to a constructor of another
class that is to be used whenever the redirecting constructor is called.
Your attempt to use them:
const factory Foo(int i) = Bar; // Error
doesn't work for two reasons:
You declared the Foo factory constructor as const, but the default Bar constructor is not const. Either remove const from the Foo factory constructor or make the Bar default constructor const (which also would require making the Foo.empty constructor const).
Note that when you use a redirecting factory constructor with =, there's no opportunity for you to specify how to pass arguments. That's because a redirecting factory constructor requires that both constructors have the same parameters. Either remove the unused parameter from the Foo factory constructor or make Bar's constructor take an int argument too.
You should pay attention to the errors that you get from static analysis; they explain the above two issues. In DartPad, I get:
A constant redirecting constructor can't redirect to a non-constant constructor.
and
The redirected constructor 'Bar Function()' has incompatible parameters with 'Foo Function(int)'.

dart freezed #Default doesn't apply defaultValue on JsonKey

I have a class called Player (player.dart) that has a field called isCaptain:
#JsonKey(name: "is_captain") #Default(false) bool isCaptain,
The line above produces the following in player.g.dart:
isCaptain: json['is_captain'] as bool,
When I create a Player object using Player.fromJson(playerJson) that's missing the is_captain key, isCaptain is set to null on the Player object instead of false as provided by #Default.
When I add defaultValue: false to the #JsonKey(...) as follows:
#JsonKey(name: "is_captain", defaultValue: false) #Default(false) bool isCaptain,
...the implementation in player.g.dart changes to:
isCaptain: json['is_captain'] as bool? ?? false,
Now everything works as expected and if is_captain is not present in the API response, isCaptain gets the default value of false.
I'm confused because freezed's documentation says that adding #Default automatically adds defaultValue to the #JsonKey(...) too but that doesn't seem to be the case here. What am I missing?
Freezed adds an implicit JsonKey with the default value.
Your issue is that you passed a custom JsonKey. In which case, your annotation takes over what Freezed auto-generates.
So by specifying a JsonKey, you basically removed the implicit default value added by Freezed.

Flutter required keyword

I don't really understand how required works. For example I've seen this code:
class Test{
final String x;
Test({
required this.x
});
factory Test.initial(){
return Test(x: "");
}
}
But what should required do here? Seems like it makes an optional parameter a non optional parameter.
Update
As of Dart 2.12, the required keyword replaces the #required meta annotation. For detailed info look into the official FAQ. The following answer has been updated to reflect both this and null safety.
Parameters required by default
The parameters of a class constructor or function are required by default.
class Test {
final String x;
Test(this.x);
}
You're not allowed to do this:
final value = Test();
// 1 positional argument(s) expected, but 0 found.
You must do this:
final value = Test('hello');
Optional named parameters
If you surround a parameter with curly braces, though, in addition to becoming a named parameter, it also becomes optional.
Since it's optional, the property must either be nullable like this:
class Test {
final String? x;
Test({this.x});
}
Or it has to have a default value like this:
class Test {
final String? x;
Test({this.x = ''});
}
So now this is ok:
final value = Test();
And so is this:
final value = Test(x: 'hello');
Required named parameters
Sometimes you don't want to allow a parameter to be null and there is no natural default variable. In that case you can add the required keyword in front of the parameter name:
class Test {
final String x;
Test({required this.x});
}
This is not ok anymore:
final value = Test();
// The named parameter 'x' is required, but there's no corresponding argument.
But this is still fine:
final value = Test(x: 'hello');
Dart 2.12 (null safety):
Beginning with Dart 2.12, the #required annotation is now replaced by the required keyword. You should mark your field required if it is mandatory for others to pass some value to it.
For example:
class Foo {
final int a; // Mandatory? Use 'required'
final int b; // Not mandatory? Don't use 'required'
Foo({
required this.a, // Marked 'required'
this.b = 1,
});
}
Usage:
Foo(); // Error: 'a' is required
Foo(a: 0); // Good
Foo(a: 0, b: 1); // Good
#required is an annotation that will create a warning for you to remember that the named parameter is necessary for the class to work as expected.
It will not create compile errors, at least for what I know.
#required bounds you to pass #required marked arguments while creating object of Class. For example, while showing a dialog, you'd mark context as required since, you cannot show dialog without having a valid context. But, you should not overuse it.
Short answer: Named parameters are optional by default in Dart. We prefer them to positional params for ease of use. In this case, the named parameters also might be expected to hold some value all the time (non-nullable) - from initialization itself. Hence, the double effort.
He could use default value initialization of the parameters instead of 'required', if the values were compile-time constants, and that doesn't seem to be the case here.
Positional parameters can be required or optional, which we pass in order when calling. The following is an example of required positional parameters' usage:
class Object{
String name;
int value;
Object(this.name, this.value=100); //auto type inference
}
final one = Object("Name here", 50); // All parameters are needed to call.
Named parameters are another type of optional parameters. Flutter APIs use named parameters and in our UI code, it is preferred to use named parameters instead of positional parameters. Reason being readability and clarity when reading code or calling the constructors later on at several parts of the code. You would have seen this as the case with all Widgets, Styles. For if it were to be positional it would be difficult to keep track of them upon calling with the sheer amount of methods that would be in use, and dynamic type inference could also be at work.
void display({required String name, int value1, int value2=100}) {...;} //named params
display(value1: 50, name: "Calculated name");
NOTE:
If exists, required positional parameters have to come first. Either named or optional positional params can follow(NOT BOTH).
String say(String from, String msg, [String? device]) { //req. pos params and opt pos params.
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
Remove
required
in constructor.
Instead write
final String? x;
So, it becomes as:
class Test{
final String? x;
Test({
this.x
});
factory Test.initial(){
return Test(x: "");
}
}