Using Dart 2.17 Super Initializer for Widget key - flutter

Now that Dart 2.17 allows for the super initializer feature it seems that I can now replace my default boilerplate
class WidgetName extends StatelessWidget {
const WidgetName({Key? key}) : super(key: key);
with
class WidgetName extends StatelessWidget {
const WidgetName({super.key});
What are the ramifications of this? I assume this change in code isn't 100% equivalent given the possible nullable value of key in the 'old' syntax vs the 'new'. Or maybe I don't fully comprehend the underlying process of super initialization.

It is 100% equivalent. In both cases key is of type Key?.
You can confirm what the type of key is when you hover over the constructor in your editor.
The type of a super initializer will match what the type is in the super constructor. The super constructor in this case is the default constructor for StatelessWidget which is defined as const StatelessWidget({Key? key}).

Related

Why do Flutter StatefulWidget subclasses need to override the default constructor?

Why do the Flutter code examples always override the default contructor but don't add anything new to it?
If the overridden constructor code just calls super, can't they NOT override the constructor and inherit the default constructor's behavior? The default constructor can handle the key parameter anyway.
e.g., https://pub.dev/packages/shared_preferences/example
class Demo extends StatefulWidget {
Demo({Key? key}) : super(key: key);
DemoState createState() => DemoState();
}
In other words, the above code should function the same as this code:
class Demo extends StatefulWidget {
DemoState createState() => DemoState();
}
because the default implementation of StatefulWidget already has the constructor:
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key? key }) : super(key: key);
No, it is important to override the constructor, here is why:
In your example, try doing this:
Demo demo = Demo(key: myKey);
If you didn't declare a constructor, this will throw an error, because you never told Demo that you wanted to be able to pass a key into it. Worse yet, look at this example
class MyParentClass {
MyParentClass({required this.value});
int value;
}
class MyChildClass extends MyParentClass {
}
If you try to make a new object of type MyChildClass, what will value be? You will get an error, in fact you won't even be able to compile this example, dart will complain that MyParentClass doesn't have a default constructor before you even compile. Because you told MyChildClass that it was ok to receive no arguments, but you told MyParentClassthat it should always receive at least one.
What if you had a class of nullable values and you wanted an inherited class to always start with null on those values? if the constructor was automatically inherited, it would be impossible to do this.
This sadly means that if you have a class with say, 10 variables, and you want to inherit from it, you will have to declare a constructor for all of those values again.
Back to StatefulWidget key terms, it is not a huge deal if you don't override your stateful widgets constructor, as key is nullable, if you don't intend to ever assign a key to your widget, it is ok to ignore creating a constructor, specially if you don't have any other value to pass to it. But remember that the reason why this is the case is because StatefulWidget understands that key will sometimes be null.

CallBack Function with null safety

I have a widget like this
class googleMapComponent extends StatefulWidget {
Function(LatLng pos) OnPositionChangeCallback;
googleMapComponent({
this.OnPositionChangeCallback,})
using nullsafety i get this error
The parameter 'OnPositionChangeCallback' can't have a value of 'null' because of its type, but the implicit default value is 'null'.
I know using required will fix it but I don't want it to be required
If this function is required just make the parameter required and the language will enforce you to always pass a function to the constructor of googleMapComponent
class googleMapComponent extends StatefulWidget {
Function(LatLng pos) OnPositionChangeCallback;
googleMapComponent({required this.OnPositionChangeCallback,})
Otherwise make the function nullable with the ? keyword.
this is the final Answer to this question
final Function(LatLng pos)? OnPositionChangeCallback;

The parameter can't have a value of 'null' because of its type in Dart

Dart function
I have the following Dart function and I am now using null safety:
void calculate({int factor}) {
// ...
}
The analyzer complains that:
The parameter 'factor' can't have a value of 'null' because of its type, and no non-null default value is provided.
Flutter widget
This is also the case for my StatelessWidget in Flutter:
class Foo extends StatelessWidget {
const Foo({Key key}): super(key: key);
// ...
}
I get the following error:
The parameter 'key' can't have a value of 'null' because of its type, and no non-null default value is provided.
How can I resolve this issue?
Why
The reason this happens is because with null safety enabled, your non-nullable parameter factor or key cannot be null.
In the function and the constructor, these values might be null when the function is called without the named parameter: calculate() or Foo(). However because the types (int and Key) are non-nullable, this is invalid code - they must never be null.
Solutions
There are essentially three ways of solving this:
required
This is probably the most common solution to this problem and it indicates that a variable has to be set. This means that if we have (notice the required keyword):
void calculate({required int factor}) {
// ...
}
We indicate that the factor parameter must always be specified, which solves the problem because only calculate(factor: 42) et al. will be valid calls of the function.
Default value
Another solution is providing a default value. If our parameter has a default value, we can safely not specify the parameter when calling the function because the default value will be used instead:
void calculate({int factor = 42}) {
// ...
}
Now, a calculate() call will use 42 as the factor, which is obviously non-null.
Nullable parameter
The third solution is something that you really want to consider, i.e. do you want to have a nullable parameter? If so, you will have to null check the parameter when using it in your function.
However, it is the way you would most commonly want to solve the Key key issue because you do not always want to provide a key to your widget in Flutter (note the nullable Key? type):
class Foo extends StatelessWidget {
const Foo({Key? key}): super(key: key);
// ...
}
Now, you can safely construct Foo() without providing a key.
Positional parameters
Note that the same applies to positional parameters, i.e. they can be made nullable or non-nullable, however, they cannot be annotated with required and cannot have default values as they are always required to be passed.
void foo(int param1) {} // bar(null) is invalid.
void bar(int? param1) {} // bar(null) is valid.
It's the main reason why non-nullable feature is added to Dart. Since, you're passing Key to super class, which could be null, so you want to make sure it's non null. What you can do is either not use the key at all or provide a default value to it or make it required. Like:
MyPage({Key key = const Key("any_key")}) : super(key: key);
or make key required like this:
MyPage({required Key key}) : super(key: key);
If i get this error from the constructor of a class pointing key, i add a '?' mark infront of Key like this:
const ClassName({Key? key}) : super(key: key);
'?' means that can be nullable
Add requuired function for Eg
required Key key,
required this.id,
required this.name,
required this.code,
required this.img,
required this.price,
required this.promotionPrice,
required this.size,
required this.color,
As additional information to the previous #creativecreatorormaybenot's answer, you can also use positional parameters (no curly brackets) that are mandatory by default, so not nullable.
void calculate(int factor) {
// ...
}
and is called without naming the parameter:
calculate(12);
These kind of parameters can be used on constructors this way:
class Foo extends StatelessWidget {
final String myVar;
const Foo(this.myVar, {Key? key}): super(key: key);
// ...
}
and "can be followed either by named parameters OR by optional positional parameters (but not both)", see doc here: dart parameters
Interesting answer on difference between named and positional parameters: What is the difference between named and positional parameters in Dart?
Add a required keyword before the variable in the constructor and also add '?' next to Key.
MyHomePage({Key? key, required this.title}) : super(key: key);
change sdk version in pubspec
environment:
sdk: ">=2.7.0 <3.0.0"
Changing the SDK version in pubspec.yaml solves the issues:
environment:
sdk: ">=2.1.0 <3.0.0"

Dart Lanuage Question - what is the meaning of <RandomWords>

what does <RandomWords> mean? what is this grammar of Dart language?
class RandomWords extends StatefulWidget {
#override
State createState() {
return new RandomWordsState();
}
}
class RandomWordsState extends State<RandomWords>{
#override
Widget build(BuildContext context) {
return new Scaffold(
);
}
}
what does <RandomWords> mean? what is this grammar of Dart language?
You are specifying what State this state is about, in the same way that when you say something like:
List<String> a;
You are saying what this List is made of.
In another words, it means the State class is a Generic class.
In the documentation of List class you will see they actually refer to it as:
List<E> class.
So, whatever type you put in the List, all of the methods related that contains E (as a argument or as a return) will have the signature adjusted accordingly, i.e.:
last ↔ E
Returns the last element.
As you can see, the last method returns the same type that the List contains, which is the expected.
The use of this is mostly for type safety, so if you try to insert a number into a List< String>, the compiler will know and will detect the mistake.
Also, if you're the one developing a Generic class, it will help avoid code duplication, because you won't have to write a implementation for each possible List type in the world.
When defining your class, you can do something as follows (Example from dart Language Tour):
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
And, in their words:
In this code, T is the stand-in type. It’s a placeholder that you can think of as a type that a developer will define later.
Also, as a last tip, we can see on the documentation of the State class linked above that the generic type needs to be something that inherits from Stateful Widget, so you don't try to create a State< Int>, for example.
State < T extends StatefulWidget > class

Smart cast (automatic type promotion) using 'is' is not working

I'm using the Bloc pattern and have the following code to define my states:
import 'package:meta/meta.dart'
#immutable
abstract class UiState {}
class Loading extends UiState {}
class Success extends UiState {
Success(this.message);
final String message;
}
class Failure extends UiState {}
I try to use a UiState as follows:
class MyWidget extends StatelessWidget {
const MyWidget({
Key key,
#required this.uiState,
}) : super(key: key);
final UiState uiState;
Widget build(BuildContext context) {
if (uiState is Success) {
return Text(uiState.message);
}
...
}
}
But VSCode tells me that "The getter 'message' isn't defined for the class 'UiState'".
I've used smart casts before and they did work. But in this instance, I'm not able to figure out why it's not working.
My pubspec has the following:
environment:
sdk: ">=2.1.0 <3.0.0"
So, I assume my dart version is atleast 2.1.0.
is performs implicit type promotion only for local variables.
For a local variable, the compiler can deduce that the type of the local variable will not be change between the time that its type is checked with is and before the variable is used.
For a non-local variable, the compiler cannot easily make that guarantee. Non-local variables implicitly provide getter functions, which could be overridden by derived class and which could return different values from one access to the next.
Also see:
https://github.com/dart-lang/sdk/issues/21530
https://github.com/dart-lang/sdk/issues/34480
As an alternative to an explicit cast, you of course could store the non-local variable in a local variable first. For example:
void memberFun() {
final emp = _emp;
if (emp is Person) {
emp.firstName = 'Bob';
}
}
Note that this also applies to type promotion from nullable to non-nullable types, such as when doing if (someNullableVariable != null).
Also see https://dart.dev/tools/non-promotion-reasons for some other reasons why automatic type promotion might not occur.