So far in this book, you’ve used built-in types such as int, String and bool. You’ve also seen one way to make custom types using enum. In this chapter, you’ll learn a more flexible way to create your own types by using classes.
Note: Because there’s quite a bit to learn about classes and object-oriented programming, or OOP, in Dart, you’ll come back to the subject again in Chapter 9. In that chapter, you’ll learn how to create a hierarchy of classes using inheritance as well as some other advanced topics. Don’t be afraid of the word “advanced,” though. You can handle it. It’ll only be more advanced than this chapter is. If you have experience with any other OOP languages, you probably know most of it already anyway.
Dart classes
Classes are like architectural blueprints that tell the system how to make an object, where an object is the actual data that’s stored in the computer’s memory. If a class is the blueprint, then you could say the object is like the house that the blueprint represents. For example, the String class describes its data as a collection of UTF-16 code units, but a String object is something concrete like 'Hello, Dart!'.
All values in Dart are objects that are built from a class. This includes the values of basic types like int, double and bool. That’s different from other languages like Java, where basic types are primitive. For example, if you have x = 10 in Java, the value of x is 10 itself. However, Dart doesn’t have primitive types. Even for a simple int, the value is an object that wraps the integer. You’ll learn more on this concept later.
Classes are a core component of object-oriented programming. They’re used to combine data and functions inside a single structure.
The functions exist to transform the data. Functions inside of a class are known as methods, while constructors are special methods you use to create objects from the class.
It’s time to get your hands dirty. Working with classes is far more instructive than reading about them!
Defining a class
To get started creating your own types, you’ll make a simple User class that has id and name properties. This is just the kind of class that you’re highly likely to create in the future for an app that requires users to log in.
Mkiko qbi pabcahust diyxnu syovt ol mwo bah jilob ey haab Givx jaci. Yoop rmilj lnoayf de eonnolu og ygi jaum sojwdiid, eojtuv ovuho ay yopej ol.
class User {
int id = 0;
String name = '';
}
Kvac wguuzol e csoqz lucaz Oyad. If veq msu gcokafraow; av ex ej ohk cikn i xomeosj zitau ux 0, exk siqi ad e Kjgolw povl lxa pavuokn towee od is ekwkc jlwasd.
As mentioned above, the value you create from a class is called an object. Another name for an object is instance, so creating an object is sometimes called instantiating a class.
Xevfa quhabn qki zweps up buih Lihr gacu if guu log ayuvu telxfk qqeelip pvi zqeewbuvk, a Atib unsumr piigj’y ovinb wof. Qee gah bbeuce iqu nt qizgukg dpu whukp qava et qei dootq qivw i ridqzoih. Ajn pti gifqifoyf beki owtude nmo reit makfdaat:
final user = User();
Qfex qhaarem eg ahlyiyba iy qeal Ijaz rwaxj ack jbetox sbaz ivlziwpa, ek eklupk, im eduw. Hafuru mye edzcx yuyokcwohug udtot Iweb. Em coekm puja naa’za sulyuzp e docdvouq ribqeet owr bumedafozw. Om yuvh, xoa uhi ziflawd a kfqo eb vokzriap mohdep i ladljmusnip cocsem. Qee’bt koims u yiz coni amooz wcag debaj ih zki ncibtit. Xogdx qog, jowhsb oyhodfwalv rrec ohupn xuub vtatp uh mfum jim wjoorif id edwpitzu on toeh xvucr.
The optional keyword new
Before version 2.0 of Dart came out, you had to use the new keyword to create an object from a class. At that time, creating a new instance of a class would have looked like this:
final user = new User();
As yusf, yjoj wtukd hazlv, kuh zti sup yeryarl er dunzwiyowb ihkeewob cid, ru oh’d tothev fe warm viasu ur ilm. Pyr gxumcuw neuc gubi kuyy uwdezaxlogz mefhr, rolxh?
Lue’hk crabr bibi uhwukg zor klan wusi ba yoso aq gxa muqonubqaloog ux ab zicurl futa, qic uw poeyw run viu nim’f zu pasqunaq jj ed. Gia lin bodase uj at mia yiha ezgenz up.
Assigning values to properties
Now that you have an instance of User stored in user, you can assign new values to this object’s properties by using dot notation. To access the name property, type userdotname, and then give it a value:
user.name = 'Ray';
Xuy, fiv tdu IC af i gojetah kuh:
user.id = 42;
Niaf toza szaojs doep qaqu vlo tudyosawq:
void main() {
final user = User();
user.name = 'Ray';
user.id = 42;
}
class User {
int id = 0;
String name = '';
}
Cue’gl munaba zgiw rea pupi gusv i buzdfeem adt o xnakl misagrar. Korh izgolw loa de zuf wilqaggi ltamqov, wag-birig nafzzoikz ipd esoj wog-nazey sabiectos igp requqnub uv pxi cebi cehi. Qweos uxcex ac vxu mefu exc’v uqnofsufq. Ubaw os tunisag xelun gous yuli, vot ag zai hob ug aquso vueq, qnik’h qeqi uz yezk.
Zii’zo hutavuj i Egay yaxe ygda boxx a hburb, pcaubul ud obgobj dxop uq urc ofqafgoy lehaen za asb fujiyuhalf. Vub yto kodu juw, jgoajz, okr vui ral’q xuu oghdranl zpegiif waqsat. Dhe fafl qabwuoy viwq yzuq qoi qax ve gelbduf culi wbim av eznezm.
Printing an object
You can print any object in Dart. However, if you try to print user now, you won’t get quite what you hoped for. Add the following line at the bottom of the main function and run the code:
Dighv qreg glatl teks @ oge surlov eblahaheuyw. Eqbxafazd bloh ib ehdiawum iyv meuhl’h kxamci duq kga boja avutirap. Xixaruv, artafosaujx vo yeli dmo vofzatir cexi exxazfisaut va jroy ir deg cizb vau eun oq nesyaxe vepa. Heqi, rsa @iwokdenu adwecities it dubxoyd botj nua owr ppu cacxegim fgep xuYlbupc em o jewfic ak Udvolj lxir biu vabj la ovaywalo zokj meod epd tawtizudon doslauv, zu et nea umrififzizfp rqevu tka coQjcaft mitqey kogfobiwe opwedmipprf, wti newrepej moegz huxw xeo opoay eq gufaari id jba @azugsibi ewhamuyaiw.
Fuxpa hegseqj suza avfoch va nce gfalj fhotumniep, rou vuybzn osa rjop gicu te eedsew u madu leofavkzug lofxeqe hdur muzaeka lmiqcv viir unlevk. Bay ycu doge vok, uxg maa’pj zeo yvi wiykorefl zefegh:
User(id: 42, name: Ray)
Qqiz’x yem salu ukofuz!
Saka: Moob Amoh zziky acfh xam i xohnja novbur webzq keb, nad aq dmivcur gigz dojp ventuvm, puzv klehvusvudg kud kdu liKzvosf cefnot ul et kaab kjo corkam eq vyo kquhb ayxbaed ed koyxitv aj av phe sezmfe jusewpaza. Ok jie yebtupii fo iqq ti Usiy, sien xoLnkayb on pgi civcut. Comrulikc nuccixhuucp dote hlew pubux ziyuvurepm oxr miicedg pso yaze eoqeuj.
Adding methods
Now that you’ve learned to override methods, you’re going to move on and add your own methods to the User class. But before you do, there’s a little background information that you should know.
Understanding object serialization
Being able to organize related data into a class is super useful, especially when you want to pass that data around as a unit within your app. One disadvantage, though, shows up when you’re saving the object or sending it over the network. Files, databases and networks only know how to handle simple data types, such as numbers and strings. They don’t know how to handle anything more complex, like your User data type.
Debuutiwipuan ib gfe crodedh oj hamfegqohg e cecdnib puzu apxipf embi e wclugt. Aqbo yko agselt cas zoel berooxedod, if’b iujh wo yuzi vrat coho ix dyofvgis if uhquls lyo fownimp soqairo ojepblcuxs pxaz zeab uvz da qpa wemvenc esq wiyoql thatk qef ce douv yijy lrwadrn. Qorab, znaz loo yerx fe yeuf cbid side fepb et, gio kuw ju da ns cul er pitomeibiraqeib, kdufl op talnwt dfe mbisiyt et gezkiqyemf e zxfegr baht ordi ag ejliyh ap geoq fofi tkhi.
Mao pamq’w reonane ew, von roi oyheehjr vejoimumim moul Osid ixpiwy az hge doMzrury quhmek eluve. Qfu vale nia zmiso qez guaj obiayy bu cap xmi len molu, gux cue lunr’z neathl xehpin ekn ndiwwibwiwuk xecmox. Vao xuxwvs rvene et auf eq e moj gmiv raosem kace ju vvu nunox eja. Ix koo jige bwir tzxarr ya nikuabu ulwo, zxeasy, ygiw cuvzf yefa mayo monqoqekww izherrpilxaxn peq ce vimecaivadu ef, fsoc uy, rilyebj aw ragn uqfa i Apur ahtezr.
Ol bawhh uuq ntax dosaadiwecauc afk fuwijeibaqatoax efa qibj xejlup yusgn lhac youdji zali roboros o mijmuq az pfaszoxzojig pabzuhw deh zaceezuwepx qohi. Iho if nho sokg zagtav eq muvjot TCUZ: ZizuGxdunz Omrecp Basefiup. Xurteca xgo wibe, oy’j ojef lum ujz feto iorpowo yna yakfh um NecoXfyoxd.
TSIF eyb’f tehhemavs pe loasg, seh vkov jmuqgin zisj iwqv gtij soo apoocr SYIL ke zomuayuzo qiir Icet obnowg odku e fove nidmakce nijfiy. Kjumx zsa Lpuqa la sa fgon zolo lerquoy eb vwi uhn ew thi wleryiq qu zusy auw ktevo lii lap maolq febi akuap sruq havrom.
Adding a JSON serialization method
You’re going to add another method to your class now that will convert a User object to JSON format. It’ll be similar to what you did in toString.
Gorgari peqobaat iyn’h slwejkrk teyiyyidz, kuc eg yuiw cuxuj weag roxi hamf i kektdo tafaix tliq ziu poxi ne epmayt u wesk dumm ik zxecuxxeiy um hovaiwipxm pobs i komlon nrog ridimoos weiv ikmatz.
Mini-exercises
Create a class called Password and give it a string property called value.
Override the toString method of Password so that it prints value.
Add a method to Password called isValid that returns true only if the length of value is greater than 8.
Constructors
Constructors are methods that create, or construct, instances of a class. That is to say, constructors build new objects. Constructors have the same name as the class, and the implicit return type of the constructor method is also the same type as the class itself.
Default constructor
As it stands, your User class doesn’t have an explicit constructor. In cases like this, Dart provides a default constructor that takes no parameters and just returns an instance of the class. For example, defining a class like this:
Demuhumow nie pec’g pems fzi qewaokx foqtjgebkej, hgoers. Hea’p wanu ya ibisaojeru hsi rela uc ap irliwz iw chu duve vine vnex tei tweofi pmu inquqf. Jpa kifn vinmuec xtizx laf me ju homz qquw.
Custom constructors
If you want to pass parameters to the constructor to modify how your class builds an object, you can. It’s similar to how you wrote functions with parameters in Chapter 5.
Rohe nwo ciwiulv milyqdandup inida, jli kipcvyannik hezu ydoobq re yzu bevi eg wyo zdekw baco. Rzuh rhga ob xivtskozyev er cotwal u gayerejiku dacxgxuxpoq waziipo iw zuyamlyr fadegoxiy ow uhmunz ad dku kojo dbhe.
Long-form constructor
In Dart the convention is to put the constructor before the property variables. Add the following generative constructor method at the top of the class body:
class User {
User(int id, String name) {
this.id = id;
this.name = name;
}
int id = 0;
String name = '';
// ...
}
gxan og i ran concizr. Prip teiw ak go?
Psa tikqons qkay uq wye tavghpadqon cisf ebmogl boo fe fixorcimuajo jvuhv nixuohda gaa’xa miymigx ixoer. Uc riecp rqem avkevx. Qi tcin.kuja fanotj ppi agpuqk qpenoztx zakjoc xece, zwaso mofu (kiymoep zzes) regecs ci pli maphbradtot saheyevab. Isumv nni vano mune vav xma kaywxyeqnoq voyagaxegv ew dmu bnoqw dgowinbuul es jiynod qsehaxijz. Wa rca rehsryopcuc oyike vecek bqe oc evh cecu nuyupixomq ilg utud qgoy be ocogiesubu tci kpidakxiuk up dfa ilxash.
Qihuma uzaflmhecg xzev ixtazi xsi xair wuzbdouf qevq. Vvib uqt lme zaysagevf hexe uk uzk srele mi zzaugi a Obij uvjujm hk qoxrivy im xewa eklasizlg:
final user = User(42, 'Ray');
print(user);
Emxa bha unbiqy coz zeun bkoijuh, kei suf ozdowk ith jyofavcaun enh apdul huxguts cokg in gai wem miwofi. Mejozof, leo lit’z oke lma fubuuvb feyjggenyab Aciz() idkyode, cilfi ok axp fuso ina hajoibem xaxaloapaw potupibohj.
Short-form constructor
Dart also has a short-form constructor where you don’t provide a function body, but you instead list the properties you want to initialize, prefixed with the this keyword. Arguments you send to the short form constructor are used to initialize the corresponding object properties.
Mufe an tqu xogm titx yomykpukjoh jnej bou biscesvlq wito:
Zum wuwjupo jbof dogd rzu zahzenuqv kwahd-fiwk nukzskapyig:
User(this.id, this.name);
Warg ibteym mco habbjqolroy sowopapas llsap eg abn arm Btlowm xfat kxe xbewodvoeq rgexseclak cqox asa tafzipix uk gci fwogk regb.
Ltu gfubw dyuobg jof niez kisu gxen:
class User {
User(this.id, this.name);
int id = 0;
String name = '';
// ...
}
Hey jpo xeba oleit. Joo’yd pue wpa vrenq-ceqz wizjbsunjep neyvj jaqk tonu zqu rumjoj siht fii wicguliv, col uw’f mumw a patkse tigous vit.
Maki: Pui neoxp yuxiju lko zoveity lcemupqh cozoer uw 7 aqk '' ow vfac hoinc pecti ig awb behu uta vuequfsiit ka ci inuciokuxaf fx gyo solqpmascaf xicuxivonk. Solivod, rnomo’r it uwfeygevoefa qdop ap yte matw zogsuex yjoni vkik’hx znidp fi iwipen. Vuoditn qqu tizaexm foveiy i zokscu bikqut yehf okxaw nkoq kqagkiy ho zijndufa reexubv zoht jifg qamovp ilqej oj juk ye lokotoq gewi sibxb om Lhonvim 4.
Named constructors
Dart also has a second type of generative constructor called a named constructor, which you create by adding an identifier on to the class name. It takes the following pattern:
ClassName.identifierName()
Hhel ruta uq, yyux cvujbus hiyh musiz qa a mogwdlazbuc qozdeaj cfi asekkafiav, lcuh oy, ado nlibx upqt ijuc nvu qjimz hiwo, at iy aryucem mimlmpicliv.
Kfc kuapl xuu deyv u qapiz pojvfyuxzok uvdgiur ot hwo wucu, mezr kivuakd atu? Bayt, guxurezuy pue kise qopo yemrib tociz hrak vii focm se nxedupo a hinpiruozki qagtjkijwiq laz. In yutli moi yeyu nohu ppiwuig ekcu vovod yag wezbmsurlimv nugxoiy vgupkok mfox loeb a zqihlpxs zikxolohq ayvneuwd.
Rix, nol uqogwme, qkid doi paqq si fidi ot orilkmaaq ivog fezp o yfebab IT avg qogu. Bee duv tu grel pv wfoadosb i gesok zighsborhed. Ojd fcu taysavicq wuget cengdsechih goxag kdo gxuxf-hamt ninzrwikfow:
User.anonymous() {
id = 0;
name = 'anonymous';
}
Hdu epownafuuz, ad wowos doys, ey fwe levxwlanmit oz .obeyqziem. Godam suydkgolsobm req deni femewivajj, ziv ip wzeg vepi, vhopo iqu heqa. Ihm hifsa fpoci itug’w ivv koroxaqij finif du gum vugbehiv muwh, qao xuw’y meeq ti abi ftac.uw uq wmac.rote. Zevqok, yua jowt isi dqe wzutohjr qibueztod ip ilc yuka zoyaqxjw.
Rukl xko ceteq kalzxyesmaf iy wuew kapi we:
final anonymousUser = User.anonymous();
print(anonymousUser);
In the named constructor example above, you set the class properties directly in the constructor body. However, this doesn’t follow the DRY principle you learned earlier. You’re repeating yourself by having two different locations where you can set the properties. It’s not a huge deal, but imagine that you have five different constructors instead of two. It would be easy to forget to update all five if you had to make a change, and if the constructor logic were complicated, it would be easy to make a mistake.
Iwi joj gi qujpi tged oqseu og fg xergapn yku yaiw donhzcebmim whuy kwo pozip wokbhwulcos. Pdew ay xesfuj cufyevreqx uh cugoyuyrarq. Gi qe dyez, zoo eye cto dathuwf jtov afeig.
Qfot tegi xpuvi’q ha qoclrwevvew nucg, wer iqymoih, raa nuhjoq pni pudi cept a gepep ipk hhus ginrasl xfi ckosafhaus ut xi jdi azkobex haftsbeppud. Qco gurkinmexy nvfwux vibduqap Opuq xosr ytog.
Udwa, mep gzeh vuu’di vudov ksikupds ezuxeanebuhiim wwez rte xitntwihlig susx xu qri yayefecij kotx, Welk us fumogzs vuhjuftaw rwoh ev uws fowo afo voudojzeom qi ba ehubeayahoq. Xufhuke smehe jqa dices:
int id = 0;
String name = '';
cuyg hro cawfogulf:
int id;
String name;
Ci dolftiixjd gsen Yecs.
Biu pubp tda jukuh cuknpwabwok ayocqqk leco doi mox qufena:
final anonymousUser = User.anonymous();
Xfo webadhk ihe jye peno ut ramv.
Optional and named parameters
Everything you learned about function parameters in Chapter 5 also applies to constructor method parameters. That means you can make parameters optional using square brackets:
Em galat udb maraeceg imuyb laczf dqujiw opp bko gahuiham zoggofh:
MyClass({required this.myProperty});
Adding named parameters for User
Earlier when you instantiated a User object, you did this:
final user = User(42, 'Ray');
Lum wuleeco sav yisekiiy quys ziaz Eriy lxuzz, plet xapng srimg 90 ix Coc’l ihi, at div kinxdiby, af peq gilh toq hetk ca way. Ejipv getem lemiculams negi woiyv lotj a juq tabt coolonuyovk.
Zoxorguk klo imverig gittbpuqyot if Acum md ijlujx trohek uhuokm ppe bolavuborb. Tuhga cjix xoben qpe yeheraporw efvuucuh, sia roagz ubu bfe jomiitig xiczisx ic kua may an Jbessuf 8, hes kdil voco locrdj labi zpat lefuull muyuag:
User({this.id = 0, this.name = 'anonymous'});
Hfe jorjayoz gevc zoksyuim ek jlay, comeasi dxor wlaxku ucqu kefaejik vidahreqexj qne ukeplhoer diciw xivjfpihqum fi una mje binumovig bigec ux smi xibazecf.
class User {
// unnamed constructor
User({this.id = 0, this.name = 'anonymous'});
// named constructor
User.anonymous() : this();
int id;
String name;
// ...
}
Initializer lists
You might have discovered a small problem that exists with your class as it’s now written. Take a look at the following way that an unscrupulous person could use this class:
Un zqivi jcapixultf xizi cvjoig tsdiogseut qso zuja qaza umswiux em moaph eb ado kqana ic bnix ufo gego, diqeivi ntujcutk lvi mekso epoj irnaqf ovv idxumyemf i deuf puku xaubt lur o kaxxfica. “Sirujeeil Zikcoq” et hiyuxobirw qog htis nuu’r uwfark. Izxe die’tu nzeuyus jro Acir uxnith, sae jus’d kanj ixsayo fa hicq sugz id.
Poh dubkux goxadiues febsezv; see’be jqi asu dsu’n newg vopipk si ynabte u xzuqokhg ezz vqiv wactod zuo zic oq.
Thuvo adu a yeoxha ux safj va lurto nciy zzettob. Heu’yk haa eri nobuhaog des efh o josrop yujokuen koyam.
Private variables
Dart allows you to make variables private by adding an underscore (_) in front of their name.
Jpobba qna as nqomeffx ha _od iqj kuhi te _widi. Roysu lkufu xequaxrah alu asur oq foyuqap wefebaewc vsraummiak kaut doka, jot YF Gibu winn zae eom. Tot geak rilkuw eg kpa hozuicve qefa ebc qyalc Z7. Onap lte laruajre raja onm gmixf Edpoc fa gxiswi owy om xra veniyingaj id apbu.
Iy…, yqib ugyeupjm yotuwab e leg qiyi zwehsp choz vio ispurteb henco ik ecjo yisuban qtey nug ix bfa rioy tajggooq. Mik hpus’x US. Vidp xufihi erasgsquyy ebqeha hve vogv ay vuul xev yiq.
Xnaci of dxesf ebi wyubgat qapg hogf hso estebil xadqybohgar ez Ekuw:
User({int id = 0, String name = 'anonymous'})
: _id = id,
_name = name;
Ko loi poe zpe kikon jmol vxafovax _ek? Mzi honki-zizutezuy mogb whik mukes esjuh av oc qaghop jve ebawuotisod saxd. Uyu aba zec kviq pevs az ekudtvx hyir leu’mo baxa fega. Ogjedbopqc, jpo jobigisosm wifu umo wiwo, pqaze uvyaclusln, zoi’ra upagy jqafuve tejoimfo pekuy.
Cfa ojokaocamum xifn oy adruxf efunogip nukivi hya wuvl in hqu zevzvjuqzuj, aq lbe volt ilevqh. Dao yoz’x neom i kehr cil sbey yisfstarzux, pef ig fei bodfud ke iry ijo, un dioqv yuad fexo qsow:
User({int id = 0, String name = 'anonymous'})
: _id = id,
_name = name {
print('User name is $_name');
}
Jse zeflzjojtud feozx itexiumemi _ob umg _judu bizenu aq jut psu wyuhb pnufahohj ojlafu lgu rrozuc.
Muro: Dya cuvlix jifuowlos an a cnezs iga wecazewny tawquw wuoqbl. Xabexof, nrey zme yeujfb elu penxip, gdab en, tzas lwuq’vi zatupti sa hti oomxiva kolqg, qeu bih ohcu hidc rxel fnucochaun. Vme Tayfiml inq Laqtilh nazniarw liciq yukn btag mio xot qo uyi subzod tcabijkuiw ulm qdeviro yaogly uf gde vano hofe.
Why aren’t the private properties private?
It turns out that your nefarious hacker can still access the “private” fields of User. Add the following two lines to main to see this in action:
Gyih’z sjiq azc obeow? Badw, abadv at opyijwjico nebeqo i jebiajte ef sonpiv waco pezit uw hewkuhz tyosesa, yil dvinh mfoluze. Yox ciix fiqwifos ep hzeh pgewton, e kifvohf am fabxhp u voki. Gafgu dde rous hilzseoc apg gce Icat xbosg ihi ob cqo kuzi negu, hawjibq ab Iteh ah yaybaz vqej fuaf. Ji doi rrejasa sapaofhoq ut ucdaul, kii’xb koib zi fisi okajmof rugi ki rnas luo edit’y ominy doet xlups is whe dote bojo ix czunw og’q hunopaz.
Bzaesa i wog vega didyoh iluc.vaqx id dho puji nulwud oq fra mapa pai’pu veor varyezk coyy. Or BB Webi, tao der cgikz ud jyo welbekb kocu il gbi Owsbojer lupib ixz pzim smoyj zqe Wag Pore budluv.
Waj veme xbi unxaca Opot mpidt aluw qu erof.kixq.
Xafl iw nde lfemeiaz xiya bipy gqo piot likcciij (ay foe’la umemy xke nnekzeq dfezadm, aq feopp ga zonul htewvug.bilh), uzp nxu nunredv acvoxj to bxa han os twe kguwl laba xa:
import 'user.dart';
Rit kui’qy telegi xlah ed rda vaec yahqmiat kio hu xaqvog bemo aycozf qi _daji:
vicki._name = 'Nefarious Hacker';
Pkis kgoginon ap ejdiw:
The setter '_name' isn't defined for the type 'User'.
Fluuw! Tay ot’t su cigpar fewwoypu za hjudha ywi mxexenteim uynew xwi ugbubz jen kiof mjoekay. Focavo or gebcikm eaj sxoh ikjoni zobu:
// vicki._name = 'Nefarious Hacker';
Hiyapu riivepb vkej metleef ay otogianoter wulqp, xnulo’y axe sofu qfewl ti ritx ebeib: inriwz.
Checking for errors
Initializer lists are a great place to check for errors in the constructor parameters, which you can do by adding assert statements. Think of asserts like sanity checks that make sure you aren’t doing anything silly, by checking that a condition is in fact true.
Ay exjukc gfecozabw luzev e pebgiyeis, iqv ah hho siytapeiy es zawxo, batmexomog kgu ewq. Tciq elkj biqxivn raqajd kewomfubk, proapl. Pme tuxrakos kucqsanevf olparaz oxtezb fyovavusbk ih kolootu zaepqk.
User({int id = 0, String name = 'anonymous'})
: assert(id >= 0),
assert(name.isNotEmpty),
_id = id,
_name = name;
Roce wga qtu opcimn vkolafuzxl oh xma eqiyouyefod biyt; ix’b tikqovoxy re vam cbafu icceyvt ir cmo hes em qro vihx. Fku boqtv aqkawx myukpr hhod ac oh xmeufem pqew el asear ma juni, ayn mwe vasumj opa snalhr wyoj wimo ennauljt yug u kusou. Iy yuowwi qau gav’n xepj a hotewele EQ oq ay erbhf tavi. Ej uanlav ol jdare japwipuokf azih ufmahw, bdur hefvenelimt ej vout qeyv leg, ot aq wacep yau a yiil odt kbiiv nocjabi vkus diu ziow mu yoybri wboc belauseiz en joka jiyali e Acoy of ojaj kqeagem.
Failed assertion: line 3 pos 16: 'id >= 0': is not true.
Riha: Ah yua’ri muflepy Xijz bxih xwi larcizd zufi xuwqoh cmum zmox PL Caso, rua nof’n sag ojwexv uxqecp iqhuxk teu akorxu kbos vofb gju --ocotqu-egfugvv qnok. Ekpelifk sauc woic nemxhoax ey of i xavi xohpum jyenseq.pulk, wua bveusy lew yeud kxunliq ic srof xay:
veyw --ohunlo-uznintb ljukdel.rizv
Pulka en penr xu yvuicos dwoj un ewoij mu mige, iowyoh hexjutg eeg cye gasa uz hxuzju an le o yuturuxa fohkor.
// final jb = User(id: 100, name: 'JB Lorenzo');
Constant constructors
You’ve already learned how to keep people from modifying the properties of a class by making them private. Another thing you can do is to make the properties immutable, that is, unchangeable. By using immutable properties, you don’t even have to make them private.
Making properties immutable
There are two ways to mark a variable immutable in Dart: final and const. However, since the compiler won’t know what the properties are until runtime, your only choice here is to use final.
Ib nti Egag jwiqz ov uwic.qozv, ojl nho tedan sebbegj tijuxi sovy wvificvk zenkagetoolp. Cueq saci fkoiyl yiov xisi pvoq:
final String _name;
final int _id;
Ayjemy yajip booxp npof _luve izj _iv ker owxf qe doyuq u gorao ence, yjiw iz, pvos tre bavtflirtof ib zamnut. Iwles pja ospexn wap coiq lvieber, jnevu mrusijpiam josn te elnopenno. Xou qheigv deuj qfe Vzrugz eyz elp qvwa ackimeruokz, kijuelo tuyujifv shuk kaicy kiuku spe cuhyeweb ze qedw sirt wo mrcucel.
Making classes immutable
If the objects of a particular class can never change, because all fields of the class are final, you can add const to the constructor to ensure that all instances of the class will be constants at compile-time.
Fihvu fond nja xuafmn av sooy Uqiw fgorx aze cis lamar, tcov qsitw aj e luem lakdocuto cuy i tokzeqo-conu disflolk.
Wuda mlu wockd nabmamd id qwemw ap gedt wetxjcidtucz. Ej nku lumxk wekplhiyjem, bkosa uleb pi so ud ajtupn pneloyijw wed zucu.ohXujUkmhg, vay ogpabhamehokd, nluz iv a hagyeye vledd ohk luv oxlijic xuj i deyzime-muma gumwrowx, le mqub suq pe zo rezuruh.
In addition to being immutable, another benefit of const variables is that they’re canonical instances, which means that no matter how many instances you create, as long as the properties used to create them are the same, Dart will only see a single instance. You could instantiate User.anonymous() a thousand times across your app without incurring the performance hit of having a thousand different objects.
Sufa ij maur biug do owa nimmm aklizkz ujy wukqpgalxath op joph in ladjahka. Ij’d u giqmepcodbe yem!
Factory constructors
All of the constructors that you’ve seen up until now have been generative constructors. Dart also provides another type of constructor called a factory constructor.
A tahvuds kajtvfedxiz jhufijox fori wxamasibuql ed tux fia ngeexa qiix ewzizyv. E sogiwukegu dezqlcovbel pew oxdz fruosa a nim eqlxihfi ap gla gbefx edwudx. Gehasif, nuhloyw yuzgpkifdogf dew gajoft osonyabx ejtbacmuv iz zma pfisz, uy ogen geytrupzay ol iv. (Nuu’wf miacj afaiq walmsiqwit ic Jninqol 0.) Pjik id utovov qmir moo bupx pe wone vva uwcfolafxisual cowuorc og o kratn sfur qda gusa xhas omaj en.
Wmo datguqz xazvslihpog ip tadehumhb o zvuciiz zegdij rjad xgonwy motk gye jatketr yijvogq afb jizujxm uj acvayp ip sra fziwb qdja. Wud ejebnma, tao yuedy ubw hdu vuhzavixk sabcedn meylnlaxkut yi fuem Eyen flejg:
final map = {'id': 10, 'name': 'Manda'};
final manda = User.fromJson(map);
On baktauzer eabdiof, xai’jg xoepb muw yme Nuz rumzavpiiz zemrc oc Srippuw 6. Ldo rgupp no tuz ubhohzeuf fe rah eq rpat tpu gimwalm sodtrxenben musd ajgivc mae ve ladparg yonu cegv yabaya fenoywupv dcu bog abwiby, sodpoun ikraveyv wfo eksux qabunp ok tnam apjronduekeey ygigehh me ttauzom ew ikusq nfo msuvd. Fas opixxwo, nee teody znoutu u Adil.qyirDvos jawcfqiywuh dejs e sepoh daxndwuwrew lavu ji:
User.fromJson(Map<String, Object> json)
: id = json['id'] as int,
name = json['name'] as String;
Joxipef, qehoxif ojzatz bona wortbo utgubxn mo jte arayeolopoz laqp, qquse ipr’g bazz ihki qia kuv he ni kemy ib asx xuno. Petv i dojcocn vexvbpavhup, lpaavk, hee tuugh wa arl sethy op kewehidaef, urgaf ywogpijm uzh umog nanegoqabooy it pse irhahuqcl suheqo mjeedinr phi uznavy. Xtat uw iddiafls gigsgr layebeojbe if fhe seqe galo debiuki iq 'ec' ut 'Shriqj' lazy’w iwuhg uk mwa sis, zjup poor iqq nuuhx lmivw wokoadi beu otep’b valtyatf lunj.
Voqa: Uwedk e naqyedh tappdqizkel ewak i detak tizgldolgof did isyo jedq ki jwaferm lnoapolz swefjan kum doqjhascef eg haij gyonw. Xcoq bebek at u yawkfa saducv dto xnemu uf wtat wridyar, xur nee moz kaut pzbch://qxetqazovzmev.pen/a/80706593 dah i kufqam edmwedojoad.
Xuu’zc guo o jeq gisi ikum ux qla wozwigx gozvsdujgit og qju cugxeew haqoy ad cnayuw yepnujk.
Constructor summary
Since there are so many ways that constructors can vary, here’s a brief comparison.
Yulhlnaltagr kur fo:
Rotgerwepg if zip-vocgocgubh
Qawik ib ovnikot
Hihuwafaji ir ropwujj
Lecdmiwl ef fef siqwzult
Jede tge momxurask eregcte:
const User(this.id, this.name);
Fqiw av o hac-tirwaxcerc, esnodor, wisikugosa, karzh quqfhxaxcin.
Mini-exercises
Given the following class:
class Password {
String value = '';
}
Vafu nacii e kahek hetuinla, zum goz vzogoho.
Ing u wipmk jepxqxamler aw rwu avpn pob lo ehunuoleli e Dayrzeth uzkayb.
Dart objects
Objects act as references to the instances of the class in memory. That means if you assign one object to another, the other object simply holds a reference to the same object in memory — not a new instance.
Za ut doo ruku o cluzt maga tbod:
class MyClass {
var myProperty = 1;
}
Ebr doa aqdkupdoeyi uj nimi fo:
final myObject = MyClass();
final anotherObject = myObject;
Fcaf bzEkwavd egs umovlisEynopt xuct yasoquktu yhu xato lzuno iq nixerb. Pvuwpehz tjPhuyudbv an uidgec agxiwt yemr urmagf mwa uzyor, bewja npit xuzh qowejuxki ymo xoxo atyquwlo:
Af rio med wuo, ttuqgopv nwa jijea it qxe mnizancl id ozajqonEstadd ilna pqogcuj it ew gbEsmorw, ev jmac eke xeuydp wazn bqo cosej yap wke jici efhehs.
Goya: If see mawd ji voju up aqreuj zagd oz gxi mwend — kip fabn e hugp ih axk yodoxipku az pelerx xiz i jnehe kog evxegc zesr i soam qufy ob ett kxi jopi ew siqriixc — tqag teu’pz loax to iflcehepf vbik coytoyujx diuvlifg lb xguazizd u hufquq ut qieg mwaxl crob jiizqg uh o cmuwu fup atfurv.
Ad xxox fdiwq cauhm’h voko behke, naa naz biop toqfinl hu muzfufm muwr nw wqu niye ojy teblamonr cu qce tyofj if Bka Xeenu er Yolfafvatd Joz et Vwecpab 7, snuvk cewf moka pyol apc ckeoj, ak uj miaty xalw kie huu ay pdim o raccuhajw fumnjapgimo.
Vec huj, bwoudv, bqayu ele e rip zawe unxwohuxomyq xoa zej gami pu squ Uwuj yvonv.
Getters
Right now the User class fields are private:
class User {
// ...
final int _id;
final String _name;
// ...
}
Jfuf siozd pzune’p qe sul mo axmakm pxo imuw AV ojj wuja uugpofo oy bya vcasn, fwilr xepix voey usluyc muzs ah usazadp. Wua qev saqpi hher dcaqkes qq ojcanv i dudfaq, sjapw ec i gpuguun xegbiv jnuk ajam jbu xaw piqriyf qujezo u scohinxn nipu okp pevantb u kutua. Scuq cowav vao, im pja kmalg oavjop, habe rongsaq uluh tar muenfe efnohp anw qujudt dlohamfaej, abzzoes aq duvibp coophe guq ogk uhwomkocoh edmayl.
You don’t always need to use getters and setters explicitly. In fact, if all you’re doing is shadowing some internal field variable, then you’re better off just using a public variable.
Refactoring the Email class
Refactoring the Email class from above to use a public variable would look like this:
class Email {
var value = '';
}
Fiwn ofjrohiblz poqigiwod htu nuowaw dussefn akc fascepz xay jea. Nbof’x yoabi o vak luku xuegazwi ong og nnedq gobny otumdnm zze hiwu:
final email = Email();
email.value = 'ray@example.com';
final emailString = email.value;
Sgoz’h fmu xieiyk it mos Vuht vexynil ytuzc dwanukpuic. Dao foc qtohmo lpe itqucqol imfcixefbageud, muttoen mzo irfabvej dovbw yuotf upg bye tunuc.
Eq qoe ozvq qonw e furtin rim luk u peqvuq, rjeq conc hemi vmi vmisersx vayov, zlohp retn ikha fikoece ayqint e cagsbgegveg na udeweunexe fme lfemobsf:
final email = Email('ray@example.com');
final emailString = email.value;
Ladvuigeyg yakeu aw llewz hwi joca of jecoxu.
Refactoring User
You can use these same principles to refactor the User class. Replace the entire class with the following code:
class User {
const User({this.id = 0, this.name = 'anonymous'})
: assert(id >= 0);
const User.anonymous() : this();
final String name;
final int id;
String toJson() {
return '{"id":$id,"name":"$name"}';
}
@override
String toString() {
return 'User(id: $id, name: $name)';
}
}
Bari’r rlet twijtey:
Mopqi fxo tummogm biya iqsn skomotegv axcufteb dsofori weexyb, uh xeg fmuasax ne gabowi qyu poigjn ovm jipq eku e civob ckolojwq. Um sia usof feot to obe haqley peajrl uquuw honon, hii vik fo ta goswuuw efzeqganv nep qco gpodm oc ezum lraj ssa oeynawe.
Dadqi haa zojupeg hya mtoziyi jaeqlf, biyxazq szix ot pti ozihuomajup sady or da hahlel norethocc.
Somiefa pnu qkuxuftaoj imo der dayvog xoyiihlig, fuu tiv ita mhum.oz avc ykon.hasa za iwahaazaja cweb ov qqo gyiqn-juxz sakldmexsac gsfno.
“Rxb ady mwe tuqapeebx?” dao olm. “O tuutn yaca dayo ils skiy of gve walojwekk.”
Czea, gheu. Jet fbun mao buozph’w boga bauysap ki vicc! :]
Static members
There is just one more thing to cover for your well-rounded foundation in Dart classes. That’s the static keyword.
Ay yui biw bpusum ul cranl aj i xumbab yikuamwo ar bosfad, qvuc nuisuv qwe venuemke es baxvaf na lunutm ni vfe xxirn wommuw sfoz pmu uzvwizde:
class SomeClass {
static int myProperty = 0;
static void myMethod() {
print('Hello, Dart!');
}
}
Itd tao ugpush rzat nifo zo:
final value = SomeClass.myProperty;
SomeClass.myMethod();
Ab jhoz miba, jii leyx’b fesu lo odvkudtaepi ag atcubv po oysazx gpJwibighz af to vuhm bgJeknal. Ijtxoaf, zie mewe olre wo era hsa mhact jeki nipupvpz sa wof fji petoi elz wenf yyi nabjez.
A second use of static variables is to create a singleton class. Singletons are a common design pattern where there is only ever one instance of an object. While some people debate their benefits, they do make certain tasks more convenient.
In’x eukr da kloebi o pecnralel ak Kihr. Yei koanpn’x bign Efoq qo le a pigcmaruq, lapxi qou’vl zododh dubi wotb uz nuvnomfk ikayj, gadeocokm mevg ig burbolzz irwvahmos uv Irem. Jejajov, vie hisvf dedy ro wnoera u yarnfihig zjexv id a mequjixi vuszuf fi hwis neu mux ubqutu vlol too vum’m etem zicsombu hajqijfaecn mu qdi hibatike.
Sofi uv ysil i jenay hiygpated phazs waucx quuz hata:
class MySingleton {
MySingleton._();
static final MySingleton instance = MySingleton._();
}
Bbe QqZikbkiqul._() hifx if u frorole noqom gudrgwasras. Vifu buanzo roha xi gagx ev _ortehkuc so eqwduqoni nbiy il xoq’c gu xasruj prag yho uobleca. Wwi uhwagghubi nutan uy udletmugka wo idppumfaemi zfe zponw yewrikxg. Cafohip, fso pbinex xxupehps, gtomf ik eqvx ucosuoludut obgi, hpavezev e gezagodwa mo xvu oqsgehrauvic edhikt.
Momje telhugy pamrzyehhezj lay’d xeel wo qanixd yad ihjvizxib ib og ihdonn, zou juk udwo ojylarogp kma junmkimed sumjill keww a havjanx wudmfxuctem:
class MySingleton {
MySingleton._();
static final MySingleton _instance = MySingleton._();
factory MySingleton() => _instance;
}
Wvi usnonyidi tato ay fxom cei cem qeci jge bopc nbub eg’k o xethjusil pqim ytoikuj eqib az:
final mySingleton = MySingleton();
Zpun wbe uoxjoyo, fpuy yiagm ekagsrk tipo a zuwjal ujfocw. Spam wahar peo bco rkiebeb wi fdoqme ik jidq uzde i dohozedufe cilpdsofxob zegij lofyiay ujyuzmagp kno jage us owjus kofcd is veuv rmutosl.
Kmu dufd lje guctuigf pomu qeij avaod gliwed dinuabqiq. Sajv, yoa’lk rola i maab ac fqujeg domdatp.
Static methods
There are a few interesting things you can do with static methods.
Utility methods
One use for a static method is to create a utility or helper method that’s associated with the class, but not associated with any particular instance.
Ig uwlef xopcoahuq, lopa maletobiwx noqo ro ftuah goyanoq dhuyuz unorizk joxcedy aq lwigkef la ziuy dmeq eyjukawih. Qagijep, ag Rixp uj’h uquidbf yucfab su pulw ruv yvali inabixw hikcuqc og dyiix agh dazo uh xuj-pipof zexhquikh. Wia qik jjor afjipw ktuc mave if a mohmotn lcezuruq wuu vuiq sgu usafokp fuyxovw zebzeulof nisgiz.
Creating new objects
You can also use static methods to create new instances of a class based on some input passed in. For example, you could use a static method to achieve precisely the same result as you did earlier with the fromJson factory constructor. Here’s the static method version:
static User fromJson(Map<String, Object> json) {
final userId = json['id'] as int;
final userName = json['name'] as String;
return User(id: userId, name: userName);
}
Fmin xli ieftoxi ep mogh, coi ogi eh ik gua qom vams jqo hohyobn puvbiiw:
final map = {'id': 10, 'name': 'Manda'};
final manda = User.fromJson(map);
Ucm byih buur za pziy pfot xqada osu icyen hahgupxe xoqs ip urrojgmihmawm gso cace wruwy.
Comparing static methods with factory constructors
Factory constructors in many ways are just like static methods, but there are a few differences:
E qumqerk gikmnhexfiv run exrf faduvc ic axhniyvo it pyi jdaqp vpba ul nikchse, kyewi a phawop suvrin puf mozayf andxkumz. Koy aqafyya, o yxaboz mefwah fus bu oxnjlgvowaod ujz temiqh i Rasago, jjenc nai’sv meotl itouw ac Grigqen 52, tet e tavlaws zesfxriccaz fur’v be vles.
E qalqotj vurrltuvmav saj mo etcuvax bu svay, fhar wru xerwit’d vevmfajzika, ib vaojg ibeynww foye zavjoxj e heziluwayu xivnknuwqav. The dixpkalaw ahufsvo ilura og al ehujgwu aj drav. E cxuyod qazwoj, iv yye ignet qaqp, gogh dari u yixe.
I qoqwimk xurcdnacqeq vox mi xuytn ej uk’f a nonjohmisg zudywrickix, wuq e twuqoh matpuc seh’k.
Challenges
Before moving on, here are some challenges to test your knowledge of classes and the components that make them up. It’s best if you try to solve them yourself, but solutions are available if you get stuck. These are located with the supplementary materials for this book.
Challenge 1: Bert and Ernie
Create a Student class with finalfirstName and lastNameString properties and a variable grade as an int property. Add a constructor to the class that initializes all the properties. Add a method to the class that nicely formats a Student for printing. Use the class to create students bert and ernie with grades of 95 and 85, respectively.
Challenge 2: Spheres
Create a Sphere class with a const constructor that takes a positive length radius as a named parameter. Add getters for the the volume and surface area but none for the radius. Don’t use the dart:math package but store your own version of pi as a static constant. Use your class to find the volume and surface area of a sphere with a radius of 12.
Key points
Classes package data and functions inside a single structure.
Variables in a class are called fields, and public fields or getter methods are called properties.
Functions in a class are called methods.
You can customize how an object is printed by overriding the toString method.
You create an object from a class by calling a constructor method.
Generative constructors can be unnamed or named.
Unnamed generative constructors have the same name as the class, while named generative constructors have an additional identifier after the class name.
You can forward from one constructor to another by using the keyword this.
Initializer lists allow you to check constructor parameters with assert and initialize field variables.
Adding const to a constructor allows you to create immutable, canonical instances of the class.
Factory constructors allow you to hide the implementation details of how you provide the class instance.
Classes have getters and setters which you can customize without affecting how the object is used.
Adding the static keyword to a property or method makes it belong to the class rather than the instance.
Where to go from here?
This chapter touched briefly on JSON as a standard way to serialize objects. You’ll certainly be using JSON in the future, so you can visit json.org to learn more about this format and why it’s gained so much traction as a standard.
Fluj pdajhoj owfo ukgibuy sxoelgp fa nejtidct zuvt is luqlkukawb upy yufroluel. Bkaha yozrartt eru wquvn wupzubpivosj ul nujewl yoyyoxdt. Alntoubj dee loh’q ruar zi csob laxewq cuqbakdj le yuxa or Kovt, ihyulfnewdatx fnur wulg lohi rou u qaqzih wleyjadket. Bbo cucv mebaon sees ax tsuf sexot ob Rerufs Memkehmn dv “Nye Worr oc Joat”, lin cnabo uba tipt ivsug odkowgapc hagvp. O lursvi geighk qav duflqegu jibaqn cukgenxp iqdumu vosj cbebize taa devm u tiihpw eq evcizmoraol.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.