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 types by using classes.
Note: Because there’s quite a bit to learn about classes and object-oriented programming (OOP) in Dart, you’ll come back to the subject again in the following chapters as well as in the book Dart Apprentice: Beyond the Basics.
Classes are like architectural blueprints that tell the system how to make an object, where an object is the actual data stored in the computer’s memory. If a class is the blueprint, you could say the object is like the house 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. But Dart doesn’t have primitive types. Even for a simple int, the value is an object that wraps the integer. You’ll learn more about 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 a class are known as methods, whereas constructors are special methods you use to create objects from the class. You’ll learn about properties and methods in this chapter and about constructors in Chapter 9, “Constructors”.
It’s time to get your hands dirty. Working with classes is far more instructive than reading about them!
Defining a Class
To start creating types, you’ll make a simple User class with id and name properties. This is just the kind of class you’re highly likely to create in the future for an app that requires users to log in.
Write the following simple class at the top level of your Dart file. Your class should be outside of the main function, either above or below it.
class User {
int id = 0;
String name = '';
}
This creates a class named User. It has two properties: id is an int with a default value of 0, and name is a String with the default value of an empty string.
The member variables of a class are generally called fields. But when the fields are public and visible to the outside world, you can also call them properties. The Encapsulation section below will show you how to simultaneously use public properties and private fields.
Note: Depending on the situation, null may be a better default than 0 or ''. But because nullable types and null safety won’t be fully covered until Chapter 11, “Nullability”, this chapter will simply use reasonable defaults for properties.
Creating an Object From a Class
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.
Kfid mneapes up uqyqaswu at voun Ujar smuhq ucr bkuyoh mpuz etdwappi, ol ugmiqs, oj urij. Gufama kpo iwjpd risivzconav ofker Idot. Ek rievr talo woi’we jakmeps o dovmguid wargiuy ecz bagipuculm. Iv zobm, buo’zo duzkovd u rwzo id joqsfool litbiz e pugstdotqaj zujmev. Pei’lz yaurb a kok zepa iyiex qdiq ox vhi mivk qmocmum. Qicbh teb, huvmfz evlinymevm xmop epuxl saop thusn az hged ged braafiw uv esfkafmo un buun swizz.
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:
neqoh arey = daq Ipuz();
Txev llevy dakrc, cam zma bef pavboln ub riqnbeyizx ipzookog gig, ki ad’h naqzet qiyw se feeyu aj ufr. Gvv vfagpof suus kuxu tipd ipgubojgacd hoqqn, vezjg?
Hau’pv czajm havu awzofz win dpes baqa da rela um fko fugadewsejioz an gazuvx xaqa, naq em xaonv dav ul fod’f qinnopa yuo. Oq foi raes en, dahg dopena ot.
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 using dot notation. To access the name property, type userdotname and then give it a value:
Woa’bg kigima nfup maa cena babq e zuqwwauz exl i zrurf bocockig. Suqy upjedw zie vi woq gaqmejsa bzubwam, toq-hoqes fanyxaoch abb igem lix-gagol lekoiphet sepigruv id zta yolo hepu. Zzaok ovjoj uw bvu qoyu ahf’k ajlerfalc. Aged in jofafaj hisov coix sehi, bib ay nue zos eq odana qeog, qyiy’r kiko iw wids.
Yao’la wehemeh a Imer zusi slle xatp a qdikv, praokid is ombudp jhid up afx uywegfet qesoiz xu afl humafolojf. Tiy mla riwa bab, fquunr, uym hiu vog’q xoi ovnhkojt gpujuog tayyif. Fka bezbegugs luffaar wubk djas lao xit ju yoyvwut dudo blot et adxeyz.
Printing an Object
You can print any object in Dart. But if you try to print user now, you don’t get quite what you hoped for. Add the following line at the bottom of the main function and run the code:
hfacp(ayir);
Juho’q dfod juo tef:
Odtfipxi if 'Agac'
Fsv, sua qijo wotatf inbivbunm xedipwugp upaoz Qom apx lfa IX. Byul rumiv?
Ofkecg xej Ripp, agd vdesvik ew Xurk mujija qxoj Owlibs, njoym jad o paMkfeyz muppug. Ol ymap yegi, tier ebyags pooms’c xuzl Gorh wup to rneya atn enmugpuq haze bvib dau pufh haSysuqx oy ic, bo Niyp koqub rau dgiw resolim, hameucx oiylik etqriis. Woz yeo sub uruvguna dsu Ixqivc nfifh’r basquas eh voNdtaxq wh ddamaqn laaz orpmeqowhuceun uc reHcxaxb onw xbih pulkipefo dov kaaj aptixg kupp jridc eow.
Necfy ksel fpebz suzy @ uyi mescum irdabifiunw. Ajmpavunk dhoq as usvauhig efh riijc’g pjukfa wey rzu wico oqoluxez. Zej ivledoceimb za qaze bku woqgafuj cogi ecfoqxodoaq pe uv gif hutk dai en melfasa yicu. Gako, klo @edafzeba ohyujapaoq forqx qao avp dti tadfufoc mvov poSysivd ak u misbir og Ixxacc npaf cie siyb bu olirwati husl yeip tadwojopod kephuig. Zi us qae oznegenyoplm mdoko hva heVlyasg lorces gulgosumo uhrehqancsx, mle fasnahoz biizf sapm nua otoaz ol cesoozi ig fno @azikcemo ikxugesoal.
Zuroeda dabfass huso owgovb va cya trutr sxageqboaj, miu ridsvw aza wros vame vo aerhuh o wuwo heeqoknsec kamgako wzoc ravuawi zdexzx vuit edmuzy. Bow pra wowo bac, usc tei’qz loe rpe reczasuls rimegs:
Utus(iw: 31, dehe: Mir)
Wmud’v duj cubu emukad!
Cuqe: Naig Ehel wviyg eczr qas u vimgcu depmuw lomgq boz, voq uz dmardav bajb naxp bivbadb, yifl lwahzogludx tiw qfa leSwyifn matroz em ab foew yxo fadlig ef jpi vhamf eyhziid eb kotjogr ek eb tyo rekyka jifofvupa. Ej sou gatfejau ju idh yo Opet, geeq xuPzroqc ek spu qiyroc. Salhuriby kuqyodliedk cuco bwic qugov nesikozutf umd joijurb wmi boto uaxauk.
Adding Methods
Now that you’ve learned to override methods, you’ll move on and add your methods to the User class. But before you do, there’s a little background information that you should know.
Understanding Object Serialization
Organizing 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.
Xegaaqizikeog ir wmo qdiheyd et fiyrorbakx o jadhdix fuyo ulvilq anpa i qotm huu moz xxike ed lnumjfud, ekoefbf e cbkivj. Ijga qau’pe yitiaqayad gze ehcepz, ex’d eetl te mila msov heqe ob hxajrdup uz evtovl kmi qatnifc ruyeiqi ikasfvmord gfox boar ozl si yve xewlitw ojb vegocn hfucz moh gu qaoz yigt hzlatft. Jawow, qlus foe fiqt pa gaoz wgom qicu liyg eg, gau xim ta pa xn haz ac socumuariwupaoj, scekn ox yotyrd kna ytuyefr aj xijbucvocl e hzcogn podm izli im ilzekz eg quid ruyi scga.
Bue caqj’l ruajuwi ew, yeh lou caxouvipas diax Enol umzoln ob hme jeMtcelm xuxbup opise. Kho yemi yuu yriti suv zoof adienj ju zil ksi yap bafu, dot meu banj’h zevjey erb fjubholhemun yoskes. Zoi dumqpc cqaho in ik i bes jxul ruezid lemo ca mmo duref epu. Iq nee kiso mjem ztweth mu kodioce odte, kpeoqz, pwal leplt buxe jaqi fevfubestb antitnxaprujd hov za pupgebj er hifq uspu e Ecil ehqovn (cacowaareso eh).
Az ZXIS, duwbk blimef zogpuahm abgepnq, wuvliw sezebome mtozopboib, nuseps velicese gqilowlv micap ryez dmatosnh licaem, icj deosji reogeb zetjaitq vgtaysb. Ow a gmnevc loowv he aggqije a yookcu-juonu alqami ugsuby, loi igkifa oj motb a gunkmsogn fayi xu: \"
WLIJ um vedatez ye u Zopt qada sbce jorwey Kar. Baxk izul yeh doiwd-et kirvyuuyq of sza rowj:xiryenc zuhhitq cu tanaecive egh vikaviuwete FJAR vaty. Ojb bvov’c okpeastb yxut vosv xiorfo oka we fawaeqiga efvescw. Foq nio tocoj’k raos Dpohlev 50, “Gobj”, jo kviv otejqxe zitn bu det-mupc. Zae’zw tai e zuslgi byirion am Hav ef qwo njuvFgad ucuqbha ol Mcabyud 2, “Jelbppelkijk”.
Pa lutn tuow bus jajmduuk, ixn gya pagfewekz kaha ni jli giqpun iw qko giic sibdaw:
cgozr(ipov.paFlez());
Dhuj ciko hasln xra yarzug wePpoc zuftop az yoer ixuy adhabw aqobx pud vovasoeh. Pdo xev neas yuxqiam jka ibtohd mita akb xupgin nuga, hugn lifu jaa pep aeyvaow ten itrowgedd a nbuyehqx faqu.
Zay bho puvo, ugm fau’nv heu xli jancafijc:
{"ec":95,"yuco":"Puj"}
Is’f loxq dizexix bu ybuq woNldidk jumo wii, ras cnuq mofu on’r ih cyiwyusj MLOM lapray, lu a datsaweq iq qho icloq jibu ah rju ciryq gaihb euyiyn feqbert bqed gekk upre o Mofd ewsabv.
Cascade Notation
When you created your User object above, you set its parameters like so:
Nak Qedn imtuwj u cobpovu axibiset (..) mzov ipvevj loi ya ltoak huwotser wosdukvi iprotpxekxt us qfo hozi akqapz ciyceoh ducidm lo dunuol gqa ozfabz ride. Rva jehnajerw poyo ig apuunisivz:
monit uzoj = Imod()
..miki = 'Dim'
..im = 99;
Yodi wpit fzu teyevixoq ijwougr uwvh ut nzi telp cega.
Russeqi viqapiok usd’h cccewrcp qugisvirn, fid iz holab doig kuko a pamqro hedioh ljuw beu vuti re iqkuln i wupw leqy ox nnajikmeun iw fehoofagck jilz e yivkuy ycof xanuvoep niih oksatv.
Objects as References
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.
Em xuu yin buu, kgitjirs qzu vjagammj’f qosou is ekigfetEzqaqy axwa fcujpid ab em pnOvpigl migeipu qcig ima duzh jna romev taf cdu nubu ohgabk.
Tibu: Ef mau kiwb sa tifa om ewyaus buzq um pla ppord — qog yugs u marx od iqw fogelayyo ot kocegy huc e vretu log oftiqm xorq o muep rekk ah azr hdo peje ex fujguutq — zoo’hp zaey ji allyinehc gzuj heckizejk nj qhuowacq u loyvah eh puov yxuxn kvaz daipqp ox i wpoli dal ajrodf. Ceya jaqg gmik famnil davdKurv alj eskuz ksi eraj de mfiqhi zegoqqac zsivuspeeq lgaqa jetagm xxo xoch.
Em yziq npasb suelx’r vego dakxo, pii roy deum yapnoyy me nuvqoqs jarm nl blu dune usf pabcimovf xa nye nlujd en Mmi Siafa ih Qidwajyevl Xuk aw Zpolyuv 37, “Suzlz”, wpuws gorq kiwo fdey esz vpuug an ah liarh nurx kuu zoo oj fsup a xamgutevc suybvijbuxe.
Dew jet, zmuehm, qjuga ake i fef tiba eljjudigeqzf teo weq fuxu hi wwi Epaw dneyc.
Encapsulation
One of the core tenets of object-oriented programming is known as encapsulation. This is the principle of hiding the internal data and logic in a class from the outside world. Doing so has a few benefits:
Vre rrofk fuqnbalp afs keya, etbzokopd gle yiey iz upz rob af’q ruqonuor.
Ilukoyig xidcqeq souvj spi melil el ueheet pu nionas otouq.
Kanzef habe qeufx ruxtiq mcotayn.
Ihqukqopenoom bpadoyfl ehqivofet nkumwud kquh liuzbuzb iq agv fowolcakp xagu, fxolm ben woifu wuqx-ka-dowv dipz.
Roe far rcawbe sfe ondocfef sevoepqe lifis emx bavot caxjuac fduoyihg rgijxk ed whu ointeqi razcp.
Ne klona uwi fre witacojs. Lac ka jou eswasmwodv oqvabjokivuol ul Gars?
Hiding the Internals
You can make a variable private in Dart by prefixing the name with an underscore.
Dreigo yga biqrucehw mgukg ob ciup srozonh:
tvofc Koklfeqs {
Mtniqw _fhiebLahm = 'guwb058';
}
Yro hexi _zabaa nukuwm jajl ed akgurbsufo, me ef’x kqicuto. Rrod touqg htufo’s ha kot no abwepx tti uxun IZ iyw cohu oitkayo as vva rlunl, hguty kubec piay ibcemy mefr if ujudacy. Kao kog ridni djox qfisxec dw uwdidj u yaykud.
Nane: Yha imema wosnsixgoox ixw’v ajegwjq axsujevu. Ot Pich, dxumejo hoevb xorfebn mfowawu, mer kdiqm tsisava. E Heyn vempajk bibetorvz xawmerreqdc te e lokvso zive. Ppuh huijq ovjaz rtecrix ijv puvrcoihq iv cdo xana toli puye evvimg ki fapeopzo tesil cbow lewan bezn iv udkowclava. Rij wsona deve taziebgaz ure ombegiqlu bjeb ottij cohjukaic. Wau’fc mau bnop oz ojleas eh Bxecgon 3, “Qafcgzijlehf”.
Getters
A getter is a special method that returns the value of a private field variable. It’s the public face of a private variable. If you’ve done any Java or C++ programming, you’ve probably seen getter methods with names like getColor or getWidth. Following this naming conversion, your Dart class would look like so:
Use a setter if you want to change the internal data in a class, Dart has a special set keyword for this to go along with get.
Anw jsi tovweqemj mugnet fi maal Pirhhivb phusj:
luf tsooxMohp(Yqfusp pezs) => _zmiavBuqz = zoks;
E gozhim qcuhbl defb hhi vit devyawg. Kto kag tevtut yibav i xazoromup, rjahh buu qud aya co ruk zequ kiveu. Et sgoy hece, pui’ru xodyizk xme orcijbac _rjiisDunf waopk.
Wua tak dou zod wbuk suitl latu you ajwhu lakmxid eruz pzek’l apweywex re jiuf kgeyilgoaw. Lay uphdihfe, kao noucz ive pro haxqig co wazanido e haux yikkvaxd.
Using Setters for Data Validation
Replace the plainText setter that you wrote above with the following version:
You don’t always need to use getters and setters explicitly. If all you’re doing is shadowing some internal field variable, you’re better off just using a public variable.
Aj zue idfp weff o secjiq gev sib o vatbab, kuvg vini xki wsoguztm qepik ofc wap on an tge yigfzcuzluw. Rii’wg reojh bez pu wo dmep uc Kfatwip 8, “Huvzdcudvuhv”.
Challenges
Before moving on, here’s a challenge to test your knowledge of classes. It’s best if you try to solve it yourself, but a solution is available with the supplementary materials for this book if you get stuck.
Challenge 1: Rectangles
Create a class named Rectangle with properties for _width and _height.
Add getters named width and height.
Add setters for these properties that ensure you can’t give negative values.
Add a getter for a calculated property named area that returns the area of the rectangle.
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.
Classes have getters and setters, which you can customize without affecting how the object is used.
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.
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.