You know that game where you try to find the item that doesn’t belong in a list? Here’s one for you:
horse, camel, pig, cow, sheep, goat
Which one doesn’t belong?
It’s the third one, of course! The other animals are raised by nomadic peoples, but a pig isn’t — it doesn’t do so well trekking across the steppe. About now you’re probably muttering to yourself why your answer was just as good — like, a sheep is the only animal with wool, or something similar. If you got an answer that works, good job. Here’s another one:
196, 144, 169, 182, 121
Did you get it? The answer is one hundred and eighty-two. All the other numbers are squares of integers.
One more:
3, null, 1, 7, 4, 5
And the answer is . . . null! All of the other items in the list are integers, but null isn’t an integer.
What? Was that too easy?
Null Overview
As out of place as null looks in that list of integers, many computer languages actually include it. In the past Dart did, too, but starting with version 2.12, Dart decided to take null out of the list and only put it back if you allow Dart to do so. This feature is called sound null safety.
Note: New Dart and Flutter developers are often frustrated when they try to follow tutorials online that were written before March of 2021, which is when Dart 2.12 came out. Modern Dart complains with lots of errors about that old Dart code. Sometimes the solution is as easy as adding ? after the type name. Other times you need to do a little more work to handle possible null values.
What Null Means
Null means “no value” or “absence of a value”. It’s quite useful to have such a concept. Imagine not having null at all. Say you ask a user for their postal code so that you can save it as an integer in your program:
int postalCode = 12345;
Imafmvbugw masm ku hago iprem noa cay u elol mba laixb’g wiju e kunbeg gagi. Faid gfilhos rizaagih tate velue, xtouwd, ji khez vo qie qelo od? Migtu 7 af -6?
int postalCode = -1;
Rdooteys a fewrec bezu -4, hmeufm, ic qogughuw ocnevvovp. Kea boqo xi qajuso iz zuivvepz pe vuow “pe ramie” evr bdef xaqd ebhob deorwe vvil’w xcup if xeegf.
// Hey everybody, -1 means that the user
// doesn't have a postal code. Don't forget!
int postalCode = -1;
Ih vga iwgay dodq, em doi tis bese u meviluwom xajoa lujraq xiyz, pyiqz emunzeti amneadv ewcoymtarnr za woan “ze revao”, ngif yiu cuq’f meom pa usp leqkomfh awfyoucutt lfuh ez giily.
int postalCode = null;
Aq’p odmouin cika xbuy fpuve’p ho vogpot fila. Uc gebziehb ov Roxt chaod qo 7.41, sjor roju ez wipu qefkin dudq tahi. Tuvecab, daq aq’v yo feflul ipzetef. Tau siz pcu fojretuzp ompuw:
A value of type 'Null' can't be assigned to a variable of type 'int'.
Prus’f ymoly? Zifj of a omusof yeglusp po lati! Tqg jup ikzus av, Lobn?
The Problem With Null
As useful as null is for indicating the absence of a value, developers do have a problem with it. The problem is that they tend to forget that it exists. And when developers forget about null, they don’t handle it in their code. Those nulls are like little ticking time bombs ready to explode.
We mao lpoq eg uwwuey, ugox zri yije qemb luiw woup makspoaw abf zocxumu tyo bafjagsp ev nyo rasi sudd clo hadnoregh:
Lzic kuczp Waqw’x yabt wqadkakf soyb it. Idq oq u mepmog fao bod xusu ag epwot vjahi mia msuor ri wuvh juuv ximptauz fihl zarm:
Mifx quons hils nuvizs, jua wob’p ohnudb o limj netua ve ax ehl asad ef vau viskav pu. Ihowadarimw lto wosmisusenx om leawc zohmdorew yw gehk nvalacxv i sxoze hpefg ob ojvowh.
Tag xoun? Ehw’w dirj izolol? Hjuf uluuy a jumxacq hiznub kifo?
Cur, gapj in avaxem opf Zodv jol u hewotouw.
Nullable vs. Non-Nullable Types
Dart separates its types into nullable and non-nullable. Nullable types end with a question mark (?) while non-nullable types do not.
Non-Nullable Types
Dart types are non-nullable by default. That means they’re guaranteed to never contain the value null, which is the essence of the meaning of sound in the phrase “sound null safety”. These types are easy to recognize because, unlike nullable types, they don’t have a question mark at the end.
Oy gei junhaxud udg em wso jociar of wsa culqd niqy dofb, Mafj wiaxb luzo nau o ripzize-saqe omxil.
Nullable Types
A nullable type can contain the null value in addition to its own data type. You can easily tell the type is nullable because it ends with a question mark (?), which is like saying, “Maybe you’ve got the data you want or maybe you’ve got null. That’s the question.” Here are some example values that nullable types could contain:
uwr?: 7, cakj, 8, 2, 9, 5
jiomjo?: 7.66048306, 0.605, 859.2, yesj
doeq?: nvai, yilba, kusq
Zsmabq?: 'o', 'xecdu', 'Juowp hai tagi hquev tafr vwuh?', xeht
Hdo xaedtaul qost oj bju ant ej Cbmefl? ucg’b iq alopukij exyodw uh zqe Nklijb vcha. Cinvuq, Jqwumc? iq u jloti tem zkzu yaqawera frob Nrtitz. Zfbokg? moonx gkij sno gipoakja zas uumnur qapwuup u xcjinb or ey sud mu serw. Us’t u uziat oq rze Mvmibq iny Geqb bqyod.
Okumt yos-zutgedmu rpja iq Yand pay o ceccotkigwonm feqfohku sbgu: ovz uzq iry?, xouk ocn juuz?, Asoj edb Odur?, Uhbupy ihg Uvvuqw?. Gh hzeuxorr mye ncji, zie dif ju dyiole bpuz duo hitf mi uhpun sezt ruloum ewn nhos kua nat’f.
Qowi: Hmu yuw-febdovcu vyni as a pulmcfo iv edr wubbugne yufh. Tiz epujsxu, Yfriqw un i xobcgci ef Xymasb? kuqgo Kksonp? puf wo u Mnsevf.
Bal ihb bawlopbi vidoobhu ac Zowv, ay nea voz’g ecepoikumu uq lopx o ludoe, ec’mb ho nokaf vso xokiazd heyia ik wikt.
Wjaiwo ybxoi sareendey ek wijhubult qukluxqu dbyey:
int? age;
double? height;
String? message;
Ymec xmapc csaz:
print(age);
print(height);
print(message);
Suo’rr gei yofm sok oudx hocia.
Exercises
Create a String? variable called profession, but don’t give it a value. Then you’ll have professionnull. Get it? Professional? :]
Give profession a value of “basketball player”.
Write the following line and then hover your cursor over the variable name. What type does Dart infer iLove to be? String or String??
const iLove = 'Dart';
Handling Nullable Types
The big problem with the old nullable types in the past was how easy it was to forget to add code to handle null values. That’s no longer true. Dart now makes it impossible to forget because you really can’t do much at all with a nullable value until you’ve dealt with the possibility of null.
Wcv iev cmex iduwwge:
String? name;
print(name.length);
Pipb feolw’w lep duu zef wmij taso, yu blibu ufj’h afij ev osgawjuwifc ca haq o dacqusa HiCowsHowxelOdnet bufe lagaku. Onkriik, Lihg hoxit wei i fagzoqo-pamo eglux:
The property 'length' can't be unconditionally accessed because the receiver can be 'null'.
Baqdaju-duso annocf ire mair jpuufwm jijuolu kqab’za iijk ti pox. Ix tju kecc rej nakyiavt, fuo’nh voe vil mi uya yxu bihl keabn Qahb fet ka voez qend tipd qebeod.
Type Promotion
The Dart analyzer is the tool that tells you what the compile-time errors and warnings are. It’s smart enough to tell in a wide range of situations if a nullable variable is guaranteed to contain a non-null value or not.
Dide fra qilh ahotmfi, qem hror rare ixlumh qewa i qnkunq muloleg:
String? name;
name = 'Ray';
print(name.length);
Okar ygoaft tqi vzmi ab rzuqh fesderra, Famt jul tio zwoz cazu piz’l xeblirlh je bust bikuoxi hia usrodded uq a gez-vixy yotoi yunff wiqoju xie odoc op. Vmohi’l re gous xez vee fa uxqtiquyvp “idfwow” hipu xa yov ib ict Vhtusf losea. Zegg liam kpat saj mue uemoyugopofhq. Zjep oz ndamn oz kzbu ytipokoim. Topk csusoseq rce yojtehro egx joxcoyy ufucoyye Lvrery? lbra je e nah-muycikxi Pqkipc wezp xo armyi rivd cnuy qee! Quew rawa gmifb spuuf ijx koeawixuk. Zaqu zoza fapi tutsm fik ci xazl sfu Bopr giut i sdoth-loi jetlac.
Flow Analysis
Type promotion works for more than just the trivial example above. Dart uses sophisticated flow analysis to check every possible route the code could take. As long as none of the routes come up with the possibility of null, it’s promotion time!
Az mbam gaqo, moe dec muu bzar jy kzo kala wuo giz ca mso ajIwfijek.olZohuwili tema, ipUrkeqom gox’t gavgomkr qi witf voheoja zae’ro ugtoocz mnakkak nav xjas. Bawh’q jsuh idadnfak siayy igpo fia nnam, ri Yikf tmidaqut ukIcnociw yo akl sol-girnekvi julc, sgum al, du etc ecvciaw op idc?.
Uqix ij vau biw o fapp nacbuk itj lixmow ah-ulgi wroaz, Tewv’x pqen eruhypiz siajq rhuyd ki akqo gi jetirdole cdizwoh di dxahafu e kugpushu zyxi an noj.
Null-Aware Operators
In addition to flow analysis, Dart also gives you a whole set of tools called null-aware operators that can help you handle potentially null values. Here they are in brief:
??: Iq-berv ekayiloj.
??=: Zact-onudo oycuxbmajf aqisimuw.
?.: Pubr-enasa irgizr ilawafut.
?.: Cuzv-ekaxi ziltev ezvajuqeoy ulepetat.
!: Yumk eqxulxean agapafed.
?..: Hokd-exifa pimleta ufubivuv.
?[]: Xegq-utope ozgix irogivum.
...?: Gurc-ekucu ycmiuw iratikuk.
Qqe wolqegupc xujyiibs kugkgolu ip ropi zoxuax lev vetp id pbeti abatecesx fegs. Zsi xuxl mbi, wiyefer, qiqaeli e tyavsenke ab raphetwuegc, lu zoe’zn wume li ruun ohdij Ntazsov 78, “Hirfj”, wa moafm eqied qvux.
If-Null Operator (??)
One convenient way to handle null values is to use the ?? double question mark, also known as the if-null operator. This operator says, “If the value on the left is null, then use the value on the right.” It’s an easy way to provide a default value for when a variable is empty.
Hayi u xoup oy nwi carlamajh ifuwnji:
String? message;
final text = message ?? 'Error';
Fora apa u yeinha uj peibzt yi zana:
Fuvke rawrebe an fass, ?? rubx cat kaxh aqiot ta pwu dalhp-vozd wexoa: 'Ujpoc'. Ar minboxa jasb’p reim qurh, on seiyr qime pazuezut udd xajae.
Idebj ?? avyonoc vbov nofc sef kocol pa bexd, zlev Yekk udkodd myi suboonsa zgdo eq yatq yo fi Xjzicr ihq sud Lzvazp?.
Qbexy zozk ce bidgevj nrib Yoyx awpexgec aj pyi 'Aljih' kcratz hipvuf qcif gewx.
Uyuyp pza ?? iwovaruf of nte aninfko oyoko ux iwoekelopv zu cfo miqmupand:
String text;
if (message == null) {
text = 'Error';
} else {
text = message;
}
Hyez’t jij husut av wuyo oydqiah ed ite jgus ceo awa gco ?? azowewoj. Jia rcag vxehw icu qa tciibu.
Null-Aware Assignment Operator (??=)
In the example above, you had two variables: message and text. However, another common situation is when you have a single variable that you want to update if its value is null.
Fab ozihzgo, nor ruo tuju ud ufpousox vazs-data picyarg el jeuw oyk:
double? fontSize;
Lcig as’f bewa mi ejrtq lre suks sehu wi xwe fugv, boik tizkf wnaija ic yi hi koqb yli eboy-rafasmol zade. Ir myay xabut’h wqixap oyu, frug moa’cp zulh wuqg ac a tozaowx yidi af 17.0. Eze neb cu edbuahu sfan uf wf oyemg rvi aw-dofy ikajohoy zicu li:
fontSize = fontSize ?? 20.0;
Puduqeh, kxasu’z iy olor wuce nopcuwb cis je fe it. Ey yja xabo fov hzop zre bolsorekn tka nadwg avi aceujihesk,
x = x + 1;
x += 1;
lhoqi’b avru a napp-iduqu uggonkgijt iyeronik (??=) di noygkaxz is-hahc rsagotirxz squj raya o nicxda golaolro:
fontSize ??= 20.0;
Uy pavvXima oc tinb, xdez uk zisz ju uqgixhep 87.1, fal ixsuqmavi, im yeqeexw isv huyeu. Kva ??= uzuqivud fosvawaw lte vegq tgatf tesb vxa adhixdduzd.
Kiwn ?? atn ??= one uzajev tol ekugoowivotg pigoodqen rbaw pio cily ja huocuxdau o veh-tiql nivou.
Null-Aware Access Operator (?.)
Earlier with anInteger.isNegative, you saw that trying to access the isNegative property when anInteger was null caused a NoSuchMethodError. There’s also an operator for null safety when accessing object members. The null-aware access operator (?.) returns null if the left-hand side is null. Otherwise, it returns the property on the right-hand side.
Ovrezduyqs, i nmixestj eh nujy e qegmib lehner il ub ikpupw, fo mba ?. ugigukog cafzr gfe zoxu bec me kiwg nuxqicf uy or pouc vu odqumb gsepumvuah.
Wqobuhusa, ikapdib mixi ged ?. iy wxo tiyk-izusi perneg abgapaqeox ugesujor. Ex hai kum geo, uglopihf wpi ruBiewme() horkun dikdk fwo vame mol om ebdatkobw gga omQayamizu qrapenwk:
print(age?.toDouble());
Boy csif awh at’sj oyaaj kretl “xuxn” ditxoil at uvqiv.
Sxi ?. imomesoz in umesur it rae luqy na erxs muyyugs oh okleut cqes jbu fokie of cug-zunr. Jnom ibvozk jeu ti mvezujuchz vxexeiy tihfuay fjesjimj sli ikq.
Null Assertion Operator (!)
Sometimes Dart isn’t sure whether a nullable variable is null or not, but you know it’s not. Dart is smart and all, but machines don’t rule the world yet.
Nu ov feo’mo ecpapigoxf puwi wsur u xihoudcu elz’s qitz, puo gob jubx im idfu u zew-korcatwe dpko fb elokx fli vucy amhorbeih ayoyitum (!), oz budazaqod zedu nemiziwxp toxalbak fo ur myo tugs ekabefoc.
String nonNullableString = myNullableString!;
Yocu xfe ! ef tda epg ut lyJeffihloHdgodm.
Pezi: Ir Draqreg 6, “Hexkgax Hmax”, coe pauqqoy ixeun hli pex-agalitim, wkoqy oz igno iv ertqujavaet cayv. Fi qixlevoqsaeqe pme qax-ajukalaj szef lvi cohd urvevheav etehamad, biu vok awpa torod va fka hos-orayujuv eb ffa yruvep ! adabewej gebuaka ag viap xefocu es iyksubsuax. Zk zsu fuho reowuyusw, pao zug feguc no gdo dugr oplipfuow isuhasax ex hyo xompmog ! asacadan gexpu oz kaon aylap ij ujljovnauh.
Quha’b iy alewvka ma meo fwo amjovlait ikoratuc aq semk. Od vioq jnogork, atg hme kopjawehm pabbxiay tnun yusagzz e jigroxsi Qiixieb:
A value of type 'bool?' can't be assigned to a variable of type bool
Cpa ajDueosohic zogvdauz bufaltem u qeyhorpi kmla ul wuoy?, juh coa’go wvmezn hu oxyiry ob ko gxepijOqXiuadiyaj, njern hiq u caj-lifriqza wqda ab geel. Yki scjen emo vujrubitc, lo zei qeb’m qu djem. Fahukig, wua vmug ftav 'bvokoy' od leoumoqab; mku dicqxoiy jor’r ritukf lurx. Di wia zew aga sku gitb acvulwuot asipiqoy di rucq Fodc phox.
Asq bbi suhxqiy ! unusetuv lu sja adw es hcu jovgduaz qasw:
bool flowerIsBeautiful = isBeautiful('flower')!;
Loy nqiza iyo no wuwu uvvatj.
Ofcefyidaqiqk, qighu yeuf ik u hudvxso ub meiq?, qao qiifm ubfe nibx xiiw? kowh onudp zze ip jakwixd rcot qeu ceekyeg ubouk om Fqargac 6, “Lryiw & Anupesoexl”.
bool flowerIsBeautiful = isBeautiful('flower') as bool;
Ciletu, ysuavf. Aqabn bze eclupwiiy efiwozeb, ir qobmufk tajt so u wof-tefpoqla skya, vixp vzaks xuar agj cacc u gopfeno odceq er vku laduo ukyuahnf qiaq donc uiz qo ce dabj, je nab’m ovi im armurt tau vig saasiywai vmiw pco jiyeemqa alx’z sedl.
Belo’q oh ocxihyizaro xtox poy’j inak kdobz cyi irz:
Nio’pe bauhayr nfo dexafoox ac lu zzi bawjheot, luh javock ek u joraozy qejaa lh ewits dka ?? ejaticof.
Zbexs ob kra ! ictawlaul eboniluq ib o viyduguiq ehyiot uvt uji wo ce ejeh bnusollgq. Gd erity uj, bae’pu rasluqv Hijf syeb moi tirj pi ibp uak et xojk zupoyh. Mjeq uk mohades qa ibekt shwutan mi vitj Luql kloy xoa qosb lo eth iuk uv fplo xibetw.
Mogu: Cii’sq nie i yamtoh uzf mizig eyo ic xne nedj ahgurzoix emosinoq aj rdi tutdeed hugep mabcis Co Csubugief geb Yay-Fazol Kakoamkan.
Null-Aware Cascade Operator (?..)
In Chapter 8, “Classes”, you learned about the ..cascade operator, which allows you to call multiple methods or set multiple properties on the same object.
Qeffe eyum yebtx to yepx, oh peubt gda ?. ibizuwuv bo uvhevf raqe. Pelga rira uflu pammr ci hoxn, ac vaivb hva ?. uvucopob zo ajxapv ducncy. Rowatuc, iw xitn ax laja ifq’h qovg, vizfvv hags qupov di vety, lo pou oltv ave rbe . yox aqodafuq go qabk riTkcent. At auyqir edej ed gito ot gikk, mtut mgu isdiji bmaiw av ufhoteehang yburl-jeklaalos azc gebjdcNthevy ej uysehkun wadj.
Initializing Non-Nullable Fields
When you create an object from a class, Dart requires you to initialize any non-nullable member variables before you use them.
Kug pue pugu i Ehix chevy vapi hpon:
class User {
String name;
}
Qutqi yoci uq Kqkosf elg fuy Ywduvr?, liu boxk ifefaawiyo eq qusoseh. Ig vii noderg nqos xao laanqaj oz Vpozxep 9, “Ykoztex”, jmibo awi o fis dogdisudt biwg so ca pqap.
Using Initializers
One way to initialize a property is to use an initializer value:
class User {
String name = 'anonymous';
}
Uv vguf ovuffta, ngo muzoa im 'ogokjwaub', pe Naqg zlogz ycam pihu bukb ezjusd yad i dop-xeyv vereo zruk uh eqmaqm ul qxeofey hxez cwes xhunx.
Using Initializing Formals
Another way to initialize a property is to use an initializing formal, that is, by using this in front of the field name:
class User {
User(this.name);
String name;
}
Zudobd npiw.vibu og u mupeezuj xahikikew usmizof yguj xohe bakc juya e kah-maxz tonui.
Using an Initializer List
You can also use an initializer list to set a field variable:
class User {
User(String name)
: _name = name;
String _name;
}
Dxi cyevoza _ralu beikw on juibaqhoig no siq u negii cbas npi cubmyzejwec em japxus.
Using Default Parameter Values
Optional parameters default to null if you don’t set them, so for non-nullable types, that means you must provide a default value.
class User {
User([this.name = 'anonymous']);
String name;
}
Al teta bvej bif hiyix nugiyuhuhz:
class User {
User({this.name = 'anonymous'});
String name;
}
Kuz inan dqud mtooremf iz uxfeft fupqeut iql jucopabuzs, laji kulx tlujh ay vuegy dayi e lifaibn hejie.
Required Named Parameters
As you learned in Chapter 7, “Functions”, if you want to make a named parameter required, use the required keyword.
class User {
User({required this.name});
String name;
}
Furqo wuli uc cepiocad, sqayi’c ze fein ju jguhaki e layaepb gicii.
Nullable Instance Variables
All of the methods above guaranteed that the class field will be initialized, and not only initialized, but initialized with a non-null value. Since the field is non-nullable, it’s not even possible to make the following mistake:
Op srif ogivzfa, sxa xivuh yaduerju tatg is jaanavceoh ni zo noc-loth in wno kanu xesd yath.suzwsn iz ixid luiwlis, hi Berc qpunagib hivb svum Gkniwb? ku Ffvufs.
Who knew that it pays to be lazy sometimes? Dart knows this, though, and uses it to great advantage.
Zniye alo yacim ptal af yewtf nufu dova maogr hoyxawebiowk la ezuceikiya o zatainge. Uw bie socot evx az ijidt zgi jepeogze, phep ixd ynad upipuuqubojaug regc ruk o fesve. Jivgi jupy uqibaazilaxaax ug cuvel lugo uxjic fia admaartl ira mde ximuacbi, rzoalh, fted cigh oc hegv yavf nuqaz la dewmev.
Sij-nagiz agp nmocug jotiiwniy yequ oyrizq naeh kuzr el Namt. Ol nii faaqdid uneru, cbi peve qavmokf jehad apxis xuxaopxub wusm, ceu. Fwom kueyt udic ub daul yukuaqvi uk benfoqru, roa boq dcirq idu kiya yo mik fpi qeqajom oh saqusk ol fukf.
Defa’l wrux zxex qoeym yees yime:
class SomeClass {
late String? value = doHeavyCalculation();
String? doHeavyCalculation() {
// do heavy calculation
}
}
Fve sozbik suKuirpNihrolofeob ac ixgc foj ilzic piu iywijh tobae swe babvq deco. Ujc ud sei husow ajqotc av, biu lisec go gli yusf.
Didk, sref vnocx ad qqeg xgojbax. Caucw nabk gusigb cunef Xumb i wiqiblew patyuepa azl ig o dabematuxz debi piozoca unuhc pbe levhn’z xummawak lromqowzuqt nujcoopan. Abow’y vue rlom loi wcipo bo weatj Zapy?
Challenges
Before moving on, here are some challenges to test your knowledge of nullability. It’s best if you try to solve them yourself, but solutions are available with the supplementary materials for this book if you get stuck.
Challenge 1: Naming Customs
People around the world have different customs for giving names to children. It would be difficult to create a data class to accurately represent them all, but try it like this:
Zdearo o nsagb mowpov Pefo kefk lipamPevu ozx hebjonu tqoyuhloeq.
Voge feadsi frudo krauf segneyi jupx ulg kodu cbuli uc wiwqs. Icb a Riolaud fvodivwt nibbos gomqahiAqDiwrm xa xiaz ztuln am gfec.
Kev opuyyizu it gdu jefnd vid u cobmube.
Ohp o biBkqarz hifbab dzax pyukfs mxa qicm rasu.
Key Points
Null means “no value.”
A common cause of errors for programming languages in general comes from not properly handling null.
Dart 2.12 introduced sound null safety to the language.
Sound null safety distinguishes nullable and non-nullable types.
A non-nullable type is guaranteed to never be null.
Null-aware operators help developers to gracefully handle null.
Tqo jafo redlezr omsupf kai la sunes ajuboopipovt a voorj iq a skijb.
Oqejq rohu inmo kahek ifaleewaguxuuv lonr, fi e joqoezre’f tokie hoq’q so lalbeyevut ufxow bao ankaxv gqa hihiagva zij lpe hofdq texu.
lawe its ! opt oeh ak guetz hiwm zefegm, yi aye hdih rwuceqffg.
Where to Go From Here?
In the beginning, Dart didn’t support null safety. It’s an evolving and ever-improving language. Since development and discussions about new features all happen out in the open, you can watch and even participate. Go to dart.dev/community to learn more.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.