Non-Nullable Dart: Understanding Null Safety
Learn how to use null safety in Dart. Get to know Dart’s type system and how to utilize language features in production code. By Sardor Islomov.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Non-Nullable Dart: Understanding Null Safety
30 mins
- Getting Started
- Getting to Know the App
- Understanding Sound Null Safety
- Exploring the Dart Type System
- Enabling Null Safety
- Migrating to Null-Safe Dart
- Creating Model Classes
- Reviewing the Person Class
- Creating the User Class
- Creating Friend and FamilyMember Classes With Nullable Types
- Declaring Nullable Types
- Creating FamilyMember With a Nullable Property
- Using Late Variables and Lazy Initialization
- Retrieving Data From Widgets
- Understanding the Never Type
- Understanding Flow Analysis
- Testing the Never Type
- Using Type Promotion
- Displaying Member Names on the Home Screen
- Displaying User Details in the Dialog
- Retrieving User Input From the TextFields
- Displaying the Dialog With the User Information
- Displaying the User Relationship
- Clearing the UI
- Sound Null Safety In a Nutshell
- Where to Go From Here?
Creating Model Classes
For this project, you’ll create three models: Friend
, FamilyMember
and User
. These model classes will help you hold and manage user-entered data.
User
‘s main responsibility is to hold Friend
, FamilyMember
and primitive information about the user.
All three extend an abstract class, Person
, which defines fields common to these models. This class is already defined for you in the starter project.
Reviewing the Person Class
Inside lib/model/abstract, open person.dart. Person
‘s constructor takes a set of required
properties, namely: name, surname, birth date and gender:
abstract class Person {
String name;
String surname;
String birthDate;
String gender;
//1
Person({
required this.name,
required this.surname,
required this.birthDate,
required this.gender});
//2
abstract String whoAmI;
}
Here’s how it works:
- You need to initialize every class property in Dart. If you can’t initialize the property via a class constructor, you must declare it as a nullable type. By using the
required
keyword, you make the property required so you don’t have to declare it as nullable. - Dart treats abstract classes differently. It gives you a compile-time error if you don’t initialize fields or make them nullable. To allow the fields to be implemented and to prevent compile-time errors, you mark the fields as abstract, which lets the child class implement them.
Creating the User Class
Create user.dart under lib/model and extend it from Person
:
class User extends Person {
User() : super();
@override
String whoAmI;
}
Because Person
requires a set of parameters, you pass these parameters from User
to Person
using super()
. The final class should look like this:
class User extends Person {
User({required String name,
required String surname,
required String birthDate,
required String gender})
: super(name: name, surname: surname, birthDate: birthDate, gender: gender);
@override
String whoAmI = ' a user';
}
Creating Friend and FamilyMember Classes With Nullable Types
Add a new file friend.dart under lib/model. Then create a class called Friend
extending it from Person
:
import 'abstract/person.dart';
class Friend extends Person {
//1
Friend(
{required String name,
required String surname,
required String birthDate,
required String gender})
:super(name: name, surname: surname, birthDate: birthDate, gender: gender);
//2
@override
String whoAmI = 'a friend';
}
In the code above, you:
- Declare
Friend
with all required arguments, then callsuper
with the arguments. - Implement an abstract field based on
Friend
.
Declaring Nullable Types
In real life, a friend has a relationship with the user. They could be a high school friend, a colleague, or a next-door neighbor. To define the relationship, add a nullable called relation
to Friend
:
String? relation;
Dart uses the nullable operator ?
, to declare nullable types. Thus, you just need to append ?
to the variable type and it becomes nullable.
?
symbol from relation
, Dart complains that it isn’t initialized and forces you to make it nullable or initialize it with a value.Now, add relation
to Friend
as an optional argument:
String? relation;
Friend(
{required String name,
required String surname,
required String birthDate,
required String gender, this.relation})
:super(name: name, surname: surname, birthDate: birthDate, gender: gender);
You didn’t mark relation
as required
in the constructor because you want to be able to create an instance of a Friend
without declaring the relationship. Dart will assign null
to it at runtime.
Creating FamilyMember With a Nullable Property
Create family_member.dart under lib/model and extend it from Person
. Then declare a nullable profession
field.
import 'abstract/person.dart';
class FamilyMember extends Person {
String? profession;
FamilyMember(
{required String name,
required String surname,
required String birthDate,
required String gender, this.profession})
: super(name: name, surname: surname, birthDate: birthDate, gender: gender);
@override
String whoAmI = 'a family member';
}
Here, you’re extending Person
and implementing its abstract variable, whoAmi
. You also added profession
. This property is nullable because a person may or may not have a profession.
Using Late Variables and Lazy Initialization
To display friends and family members on the home screen, you need to create them in _AddMemberPageState
. This class is in lib/add_member_page.dart.
Add late Person _person
inside _addMember()
in _AddMemberPageState
:
void _addMember() {
late Person _person;
}
This object will hold a Friend
or FamilyMember
.
Use late
on variables when you’re sure you’ll initialize them before using them. Use late
with class properties.
Sometimes, you can’t initialize properties in the constructor, but you’ll define them in other methods of your class. In that case, you mark those properties with late
.
Another advantage of late
is lazy initialization. Dart will not initialize late
properties until they’re used for the first time. This can be useful during app initialization, when an expression is costly or might not be needed.
Retrieving Data From Widgets
To create a _person
object, you need to retrieve data from the widgets. To do that, update _addMember()
to the following:
void _addMember() {
//1
late final Person _person;
final name = _nameController.text;
final surname = _surnameController.text;
final birthDate = _birthDateController.text;
final gender = _dropDownGender;
final profession = _professionController.text;
final friendRelation = _friendController.text;
//2
if (_dropDownMember.contains(ProjectConst.FAMILY_MEMBER)) {
_person = FamilyMember(
name: name,
surname: surname,
birthDate: birthDate,
gender: gender,
profession: profession.isEmpty ? null : profession,
);
} else {
_person = Friend(
name: name,
surname: surname,
birthDate: birthDate,
gender: gender,
relation: friendRelation.isEmpty ? null : friendRelation);
}
//3
DataManager.addPerson(_person);
Navigator.pop(context);
}
Here’s what’s happening:
- You retrieve user-entered information from the text fields.
- Based on the type of relationship the user selected, you create a
Friend
orFamilyMember
. Pay attention to the last property of each object. Ifrelation
orprofession
is empty, it passesnull
because you defined these properties as nullable. -
DataManager
is already defined in lib/utils/data_manager.dart. It adds a_person
into a static list so you can access it from the home screen.Navigator.pop()
closes the current screen.
Build and run. You can now add members to the list:
But on the home screen, you can’t see members in the Friends or Family members sections:
You’ll see how to fix that shortly. Before that, take a look at Dart’s Never
type.