Named Constructor with required parameters and null safety - flutter

The following code does not compile with sound null safety because it is possible to pass null to the constructor which initializes a non-nullable field myString.
class MyClass {
String myString;
MyClass({#required this.myString});
}
I would expect that adding a question mark after this.myString would solve the problem, but it doesnt and another compile error is shown.
A default value is no option for me and it seems that the only other solution is something like this:
class MyClass {
late String myString;
MyClass({#required myString}) {
this.myString = myString;
}
}
Imo this decreases readability if there are a lot of parameters. Is there a more convenient solution which suppports initialization with this. in named constructors?

For non-nullable types, either use required or provide a default value. You can also use late but make sure to provide a value at some later point before using it. It's like a bang operator.
Example:
class Foo {
// All are non-nullable.
final int a;
final int b;
late final int c; // I trust you that you'll provide me a value later.
Foo({
required this.a, // <-- Mark it required
this.b = 0, // <-- or provide a default value.
});
}

If i am not getting the idea wrong, if you want to have a String param which is nullable from the constructor, you can declare a 'String?' type.
class MyClass {
String? myString;
MyClass({required this.myString});
}

Related

Dart generics method not recognizing type

I have an abstract base class validator with a method which takes a generic type as parameter.
I will be passing generic type parameter to base class from the subclass inheriting the base class.
Base Class:
abstract class BaseValidator {
bool isValid<T>(T obj);
}
Child Class:
class IPv4Validator extends BaseValidator{
final IPV4_REGEX = "^((25[0-5]|(2[0-4]|1d|[1-9]|)d).?\b){4}\$";
#override
bool isValid<String>(String obj) {
bool hasMatch = RegExp(IPV4_REGEX).hasMatch(obj);
return hasMatch;
}
}
Here hasMatch takes in non nullable string. When I directly pass some string hasMatch doesn't throw an error.
But when I try to pass the generic value in the method parameter, it shows an error.
The argument type 'String' can't be assigned to the parameter type
'String'.
I couldn't able to understand why generic type is not accepting, even though its compile-time type.
The following code solves this particular problem. But it may be different from what you intended to implement. On the other hand, the code will be cleaner if you create a new concrete class for different data types.
abstract class BaseValidator<T> {
bool isValid(T obj);
}
class IPv4Validator extends BaseValidator<String>{
final IPV4_REGEX = "^((25[0-5]|(2[0-4]|1d|[1-9]|)d).?\b){4}\$";
#override
bool isValid(String obj) {
bool hasMatch = RegExp(IPV4_REGEX).hasMatch(obj);
return hasMatch;
}
}
Explanation.
In the line class IPv4Validator extends BaseValidator<String> we are not declaring a new class BaseValidator, it is already declared as BaseValidator<T>. Here we are inheriting the specialization of the existing generic class BaseValidator. While in the line bool isValid<String>(String obj), we declare a new function, so the compiler understands it as if we were declaring a new generic function with a parameter type named String. So, here bool isValid<String>(String obj) is equivalent to bool isValid<T>(T obj), just instead of name T we used name String, which is not an object String.
another fix that you can do is to use the covariant keyword, to implement that, try this:
abstract class BaseValidator<T> {
bool isValid(T obj);
}
class IPv4Validator extends BaseValidator {
final IPV4_REGEX = "^((25[0-5]|(2[0-4]|1d|[1-9]|)d).?\b){4}\$";
#override
bool isValid(covariant String obj) {
bool hasMatch = RegExp(IPV4_REGEX).hasMatch(obj);
return hasMatch;
}
}

I can't initialize a non nullable variable class Position in flutter [duplicate]

I've created my class in Dart this way, but I'm getting the Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'. I would like to know if there's a way to do it in a 'Python' style where this kind of class creation is possible, thank you in advance.
class Lexer {
String _text;
int _pos;
String _current_char;
Lexer(String text) {
this._text = text;
this._pos = -1;
this._current_char = '';
this.advance();
}
void advance() {
this._pos++;
this._current_char = this._pos < this._text.length ? this._text[this._pos] : '';
}
}
class Lexer {
String _text;
int _pos;
String _current_char;
This declares several members with type String. Since they are declared as String and not as String?, these members are non-nullable; they are not allowed to ever be null. (This is part of the new null-safety feature from Dart 2.12.)
Dart initializes objects in two phases. When the constructor's body runs, Dart expects all member variables to already be initialized. Because your members are non-nullable and haven't been initialized to non-null values yet, this is an error. The error message explains what you can do:
Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Use initializer expressions. This means using an initializer list:
Lexer(String text)
: _text = text,
_pos = -1,
_current_char = '' {
advance();
}
Note that if you're initializing members with a construction parameter of the same name, you can use shorthand:
Lexer(this._text)
: _pos = -1,
_current_char = '' {
advance();
}
Adding field initializers. This means initializing members inline in the class declaration.
class Lexer {
String _text = '';
int _pos = -1,
String _current_char = '';
Marking your members as late. This means that you promise that the variables will be initialized before anything attempts to use them.
class Lexer {
late String _text;
late int _pos,
late String _current_char;
Making your members nullable, which allows them to be implicitly null by default:
class Lexer {
String? _text;
int? _pos,
String? _current_char;
However, that will require that all accesses explicitly check that the members aren't null before using them.
You also might want to read: Dart assigning to variable right away or in constructor?

Unable to make a Dart class object

My Code
class Book {
String title;
String author;
int numOfPages;
Book(String title, String author, int pages) {
this.title = title;
this.author = author;
this.numOfPages = pages;
}
}
void main() {
Book bk = Book("Modern Operating Systems", "S.Tannabeaum", 1250);
print(bk.title);
}
Hey, I'm pretty a newbie to dart and programming. Here actually I wanted to make a class and it's constructor and three instances within it. And when I wanted to make an object from this class, I caught up with this error!
My code's error message!
I think something is wrong with my code, any help would be appreciable:)
There are two problems in your code. First, constructors in Dart has two "phases" where you first initialize the object and then runs the constructor body before returning the object to the caller of the constructor.
That means that you are here creating a Book object first without setting the three variables. Yes, you are setting these variables later in the constructor body but at that time it is too late.
The next problem is that if you are not setting value for a variable in Dart it will always default to the value null. With Dart 2.12, we got non-nullable types by default (NNBD) which mean all types in Dart does not allow the value null unless specified. You specify the validity of the null value by typing a ? after the name of the type. E.g. String? allows a variable to point to a String object, or null.
In this case, we don't need to specify nullable types since the problem is mostly you need to move the initialization of the variables from the constructor body in to initialization phase of the object like this:
class Book {
String title;
String author;
int numOfPages;
Book(String title, String author, int pages)
: this.title = title,
this.author = author,
this.numOfPages = pages;
}
The same can be rewritten as the following which is also the recommended way to do it:
class Book {
String title;
String author;
int numOfPages;
Book(this.title, this.author, this.numOfPages);
}
Since we are here just directly referring to each field we want to give a value. Dart will then automatically assign the values with the parameters from the constructor.
If your constructor takes a lot of arguments, it might be more readable to use named arguments. The required keyword here means that we most provide a given named parameter. If not specified, the named argument is optional (which means we most provide a default value or allow null for our parameter to be valid):
class Book {
String title;
String author;
int numOfPages;
Book({
required this.title,
required this.author,
required this.numOfPages,
});
}
void main() {
final book = Book(
title: "Modern Operating Systems",
author: "S.Tannabeaum",
numOfPages: 1250,
);
print(book.title); // Modern Operating Systems
}

Dart Named constructor vs Static method what to prefer?

So after dart made new keyword optional,
we can initialize an object with exact same syntax but different internal implementation.
class Color {
int r = 0, g = 0, b = 0;
Color({this.r, this.b, this.g});
//Named constructors
Color.red() //Implementation
Color.cyan() //Implementation
// Static Initializers
static Color red() => //Initialze with parameter
static Color cyan() => //Initialze with parameter
}
We can use them like this regardless of being it a named constructor or static method:
Color red = Color.red();
Color cyan = Color.cyan();
What is the place to use each of them?
In practice there is little difference between a factory constructor and a static method.
For a generic class, it changes where you can (and must) write a type parameter:
class Box<T> {
T value;
Box._(this.value);
factory Box.withValue(this.value) => Box<T>._(value);
static Box<T> fromValue<T>(T value) => Box<T>._(value);
}
...
var box1 = Box<int>.withValue(1);
var box2 = Box.fromValue<int>(2);
So, for generic classes, factory constructors are often what you want. They have the most pleasant syntax.
For non-generic classes, there is very little difference, so it's mainly about signaling intent. And deciding which category the name goes into in the DartDoc.
If the main objective of the function is to create a new object, make it a constructor.
If the main objective is to do some computation and eventually return an object (even if it's a new object), make it a static function.
That's why parse methods are generally static functions.
In short, do what feels right for your API.
Constructors and static functions are different. You usually create a named constructor that returns an instance of an object with some predefined values. For example, you have a class called Person which stores Name and Job. You can create this named constructor Person.doctor(name) which you will return a Person object with Job = 'doctor'
class Person{
final name;
final job;
Person(this.name, this.job);
Person.doctor(this.name, {this.job = "doctor"});
}
Static functions or variable persists on all the instance of a class. Let us say, Person has a static variable called count. You increment the count variable whenever an instance of Person is created. You can call Person.count anywhere later in your code to get the value of count (Number of instances of Person)
class Person{
final name;
final job;
static int count;
Person(this.name, this.job){
count++;
}
Person.doctor(this.name, {this.job = "doctor"});
}
Another very useful feature of static class methods is that you can make them asynchronous, i.e. wait for full initialisation in case this depends on some asynchronous operation:
Future<double> getCurrentPrice(String ticker) async {
double price;
// for example, fetch current price from API
price = 582.18;
return price;
}
class Stock {
String ticker;
double currentPrice=0.0;
Stock._(this.ticker);
static Future<Stock> stockWithCurrentPrice(String ticker) async {
Stock stock = Stock._(ticker);
stock.currentPrice = await getCurrentPrice (ticker);
return stock;
}
}
void main() async {
Stock stock = await Stock.stockWithCurrentPrice('AAPL');
print ('${stock.ticker}: ${stock.currentPrice}');
}
Another benefit of the distinction between named constructor and static function is that in the documentation generated the function will be either filed in the construction section or the methods section, which further makes it's intentions clearer to the reader.
A person looking for a constructor in the constructor section of the documentation will easily discover the named constructors as opposed to having to also dig through the static functions section too.

Dart/vscode: enforce function argument types when passing function itself as an argument to constructor

As the subject suggests, how does one enforce function argument types in editor (say VSCode) when the function itself is passed as an argument to a class constructor? This goes for complex types. In the simple example below, vscode linter doesn't hiccup when calling the passed add() function with incorrect (int) argument rather than the correct (String) argument:
class ChildClass {
final Function add;
ChildClass(this.add);
...
add('this is a string'); <---- HOW TO ENFORCE STRING ARGUMENT TYPE?
add(5); <----- EDITOR (VSCODE) should hiccup currently doesn't
}
class ParentClass {
int _add(String text) { <--- ARGUMENT OF TYPE STRING
// some code
}
final childClass = ChildClass(_add); <-- pass add() as argument to ChildClass ctor
}
Not sure if this is a vscode linter question or a dart question or both. Thoughts much appreciated.
You can specify function types inline
class ChildClass {
final int Function(String text) add;
or as typedef
typedef AddFn = int Function(string text);
class ChildClass {
final AddFn add;
See also
https://github.com/dart-lang/sdk/blob/master/docs/language/informal/generic-function-type-alias.md
https://www.dartlang.org/guides/language/language-tour#typedefs