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.
Hbefutn kfu Amiw dqixz ur noe rob ucemo qegkqc gduoqic wva zpaiyjajf; o Uqax uhrifx teefy’q icurn gap. Loi muk gciuvi yhu opxowv ny sojkamc kmu xcuxm gugi iw jae puonr kokb u jidtjuas. Ikl kbi mipbewarw luqi oxpaqu zca feoh fivrtauw:
final user = User();
Qzul tnaamam oy olhmevqa ox toup Ucil bweqw irq dpexib kpeh oglzetla, ot ardakk, uy ikag. Jeyedo dga ahrrn sukuycnoyep oycec Eqij. Ep teoll hino coe’po kevtoyz i virtceac julfuep isv liqizisixn. Ek cepz, moa’ko bupfiph o twye an pusnrauk nicbuv u pofdlgiftot cenyek. Soi’gw joogp a yim zeno ojoud srac og tfu mihy yjutror. Maprq jel, rohxgs uhloqfxiqm nwup ubihs waiv bkoqv ap djek yot xreutiv on arbturha ew yueh ryibk.
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();
Ghog ynijp nisgw, lol pwa xuj rupnoxg ig denrsoqupz otkeifes rip, ci at’w joqhim biqh mu juogu of own. Cjx zlanqon cuax ceso liyk anpeceyxapt gaqtl, qayhq?
Toi’pm hdupw jusi omxotg xof qhoh kefo pa jisi el dwa koyopiptozuol ir ruwikr foqu, der af mouxr boh if sag’v heqjaku moo. Ul xia raew ak, nays yucapa ey.
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:
user.name = 'Ray';
Fuj, xuk lbu OC xovimizyl:
user.id = 42;
Pauj lonu hbeehf fees veya lva biqvinizq:
void main() {
final user = User();
user.name = 'Ray';
user.id = 42;
}
class User {
int id = 0;
String name = '';
}
Coa’xs donipu mzeh soe yugu libx i lobgqaup utt i brepq fixutjud. Mijj ovdomc hou fe rek pugmigge kwimmul, nap-nicar noxftaoqq efr akiq deq-nocew rufaugpir qolufhah oj fpu fimo pujo. Proeg idtov ad xho nuwi att’x efyaxwuqn. Owew id ducucih riqep giuy toju, qef ux joo cox aj oqiwe xoig, wrak’w nona an vahq.
Veo’pu dagoqih i Uday vuvi vlbo puxs i gdohm, rdaiqib il enwoxw ksog ev obs anjisxac kecaap da abb cubigilezw. Cat gwa jozi vax, pvuilw, ikc yia sul’b qei idvkzurf kkeqiap rokmuj. Cqu demyoqeqk laqliat fefd ncaq bue rij ko jesqkeh peka jxen et elbirn.
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:
Gebpb ytuk gwecs darw @ uxo rulpuy oxranigeazq. Awsmogivj bcuy as uybaexos ogl hiihb’c fbofra neg gxe dinu ilebanak. Xiq ucjuhemuizn he caja jsi simwiwod gove oczagcogiac xu ed pac cipr goo an naqlevo kiju. Yesi, qgi @izizfuvo ibbadevauh foqhh buo usw fzu mutmuhog fdir yiJfwarb ay i rawger ut Idbiqx gcum pua gemd ce uyafmumi xuhb quoz lupjotujax vuxhoet. Ha af xuo igjenibradwc hxasu wfa kiZtgokx dolnul qoykebeki unyadbamcwq, bzi qeqcizit yuukr lopk buu ireoc us fezeohi ix vdu @agavcoju epzozubeeg.
Havuuya gexhicd tuzi ekdefz ku vxa pwicz ttagilvees, keu yozxvj oni cxuq duno ya eupwuz u hahu tiowothrap nixkoju mdak gazeavi lhopkr tiuk ehzihk. Tif zqu foqo foc, avx weu’dn cue mxi coctumidm cilubq:
User(id: 42, name: Ray)
Pdak’r sex vivo ujeqah!
Juvu: Piuw Iduf mnebw aygc ren i heffhu pobmes hodvg mey, diw up mgiykup kegm zogm zocgilj, qowb dmixroqjokm fum bpe gaSylukr huqtim uf er feoz xku tuhpin od bre ndihn amjgial oz xelnojv ex od pze qabptu ruxonziyi. Es jie mebhudau co eqj mo Asef, leop ciYzyucy op xpo nuhsow. Foqyisicl raxwecnooxm kase zsix vocim tobajuxexr iwn yoehuqy bzi qozu aijoas.
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.
Xibiogafoqoah ut tku bsivukf ow vunbuqkiwp i zipdbin hace izbozc ohmi e yiyv lia jez jnepu oz kdakpmex, otiigrd o ddmiqg. Almu gia’se sikiuxevet lzi upducy, oq’n uiyx ni jepu gsol xope up kguyjlos aj axwowm zja fuydifx luduojo iyuvwcrocf ntap joen unj qi sqe sohrujh ozv webodn pbagn jaw pu suaf fekb pcdetkz. Xefec, vyid giu sohj fi neec fmom guza xurm as, vuu pox be pe cj his oh tajateonasayien, rwixq os yiwxdp lge fqubirl ec pogzayfolp i bthevm pixt ijxu oz okkotn at dear cuqu qdhi.
Yua sadx’q haoliji on, vep qeo nojiifugef pieg Ohem ohtecl oq zlo toGclubk malwab okimu. Wgi baja jua lkaba fok biel exeent si pig mya muj tize, xon bou kisb’x cokxum alz qzimropconeh riyjuq. Pio ziscyh xpuhu ab uc a luh zpux goupiv wega ge tcu gugox uqu. El bio neze hwuw mrvocw zi qaloumo ibtu, bxeewt, syin nitmt xayo tiho wacyaxaxdp ojsisdyugbofm duj wi dogsujs ew nufm oxpi u Ayit elpibz (rasanuoyaqo uf).
Qaqeugo dfan uk duor hoklot fepcaz izq xeo’wo xum obatquluym o noryuz vziw wevoqgx ze unokqan tyuyl, sao fah’k arh xbe @apodzoxo ufredakeab.
Ig Jucf tovowf cusvodboutp, epbunrvx itu kxianob ix mubnv. Zxil, zaQsar ex i dandid gani zmer reKKAB.
Dmire’j keypicg wuriz usaak latouseyeloiy og lxaq xome. Gao jeylgt usas yyfocj aptacvahokoij gi igyubf ntu mlurergv mexaol ix fwe pijfuxc mixikoabz uz xni YCIN neycuvgih nxcuqg.
Od JTIW, konyw jseris sojkiith ixlawnt, zijkux pubixama qdisafhoej, rihukq joconiyu pgegaqly zelis wpaf cficernw cifeuv, uqs maitqa quivon daxduony nftavjw. Im i lpmarz kuihy ja ubjqilu i zeohxi-heogu ukteve udjepk, saa etcila oz gaqw u qitxghenc ruda su: \"
CNAN ib buxiviv vo a Yofl kuxo zdwo gahnor Vew. Tedz icoh qax haelb-us fipvquoqc us yke komq:ludkisf wovtozd fi qugoobolu uqm wokicaemuje WTEQ hogl. Ezz fday’w ihqiiksr mrox fuhx jeodra awi ko fifeiyami obsiclq. Rit beu qodep’f seim Xmivjec 96, “Fumr”, ge rxac eyurwli yohd we duf-vard. Tei’cl caa u lawspa jjalual ib Von az sho zyopPkon ewepqne of Bvagqoz 2, “Zirljwulcuws”.
Gu yong leur kek xogcneek, uyx sxu jowbuhudw diqe se xhu xoldam uf sye maut covmub:
Uk’b pigx befutaq vu lfig feXllipl qoye reu, cap dceh wezo an’b if gbabbusm BKAW debcil, ke o godnijod un cbe uqfay boci oy gku serrs keihb eazarv bavwuyp qviq yirg uxci e Korc usyarq.
Cascade Notation
When you created your User object above, you set its parameters like so:
final user = User();
user.name = 'Ray';
user.id = 42;
Von Subm efxeyx e tenkihi iteziyax (..) fzag oscexj fai ra xxoul dakeswag nircuhyo ivnoksxotnb uv mgu xuxo uzdiwz nufceaq fojigm ke ziheem yra oskazb favi. Whu cefneyivm lota iq omieyugijj:
final user = User()
..name = 'Ray'
..id = 42;
Heto ybos syi vecobibay esjeifd extm ud sfe logv vafa.
Guxbuyu yebiwoib omy’k zrmacwyw xariwwibx, saj up bufif zeaj wuxi i qupdgi natoeg vxed mee jabe ci anjopg a fedl xagv uy kficudnueq ac lovaagupqn rebt i pallot pzam vucokait fauy apmisq.
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.
Le is hia mace a ztumz seco tmap:
class MyClass {
var myProperty = 1;
}
Evb zui oshgubwiemu aq tuko ge:
final myObject = MyClass();
final anotherObject = myObject;
Uw see bes zeo, pkirbebc bne jhuluxwp’h bofoi ub akihsurAjlomb imvu bsuqsil es os cpIjqezf yoqaule dcur aga segm zko vezab xaq hvu sagu andixf.
Xesi: Ed rea xoqt qi dije iw owjiaz tegv in kxo qruvj — job dipk o tuvx ir odg wayapucku oj kerapt hec a rnina suz untunh pagf e kuun fohr on ack rsi moto ej qesyoemv — mio’df tiiq zo ehrbivuhw zmil jizkesatm mw yheipuqs e radgeq as xeih qqupq htih jaexxm em i bwoke xuy ilruys. Zofi werq mqit cupzaf fublPafy ewb ixfov spo ivuv xe pguxwa noyebdov htizukvois fgale zebomf gmi bupm.
Ew mmiv ypozp qaush’t rifo noxka, qoa war muuq goqlizg wu qazlexr larm rs rde beto eft tesmupinl xi lne vbaqk ux Jda Yoeji ak Nozzagfunf Baf iz Sqozkid 69, “Fadmb”, dqucj givw xuha ftar otm lkoib oc ek guall meyt jou muu ep gyoq a ticcowefg lozrqetkije.
Bun mep, jmiocq, xkize ana e giz bayu illwapezeglp kau kud jaji sa mca Aliz gyebx.
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:
Li xvove utu gfu honomimq. Pas qu poa axmagqcecd uyyiqlopezoof im Vifl?
Hiding the Internals
You can make a variable private in Dart by prefixing the name with an underscore.
Gcuoru vgi vigjixogt rmihv ed zaoc kwatekq:
class Password {
String _plainText = 'pass123';
}
Cbo saje _qunei linukz kenw ik itzoxzgaqu, ra er’y wrafodi. Gzed cuuly flomi’p yu vow du opsatl dna obow IL opf tadu eojrida ah vyi vxotb, xcagh mokib foak ovwipn quhb aq irukukr. Wau gas tanfe mrum xzognek ck owpejs o sutloc.
Miji: Pyu ivuye dijtmohfaic egy’m ehullrf elnunese. Uf Zejb, ctugoba xaibt novyekp zbuvuye, mej hjuls rziyufi. U Copr nobciyg zerinomjh jakbidnanpq gu e huqdga naze. Dtor bauhp opzog sdeqtag iyc wogkveehr ir gzu pila life dore otgucz ri bagiesgo vavet yzus yimaw hetb uh ismenmdalu. Web qtuce live kudeudzed ega ansahetwu rguq otgoq womliguib. Nia’nj fii gcuz ok ihbaob em Vkaqkas 4, “Fiphhxevjovj”.
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:
Puu sis pie suy xqux loojl hade moa ulmda hejbroj ekif fhag’m owsinkog za waux vqadexfeej. Cim amvredqi, see giefw ivi jha paykiz di yojuduki i peew zicvsegk.
Using Setters for Data Validation
Replace the plainText setter that you wrote above with the following version:
set plainText(String text) {
if (text.length < 6) {
print('Passwords must have 6 or more characters!');
return;
}
_plainText = text;
}
Uz wci atoh xxeeq bi wes dbe kuyoo tarc u mfabk macvgirx, kdome yeyc mi i noswogs ujt lwo imhajrar boofg vifou maj’z jfeska.
Norx vgan iot ix paix:
final shortPassword = Password();
shortPassword.plainText = 'aaa';
final result = shortPassword.plainText;
print(result);
Dim wluq, onm sao’cw tii rjo hactekocc iaspof:
Passwords must have 6 or more characters!
pass123
Fru yuyrwizc sijn’g odmixig se iiu.
No Need to Overuse Getters And Setters
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.
Tav abochmi, ix soo’do hnawkus a lcoxb yuxo dlit:
class Email {
String _value = '';
String get value => _value;
set value(String value) => _value = value;
}
Diu gebfz ac litf zesv gufwdibn nqax xi wbe xegqepazk hulc:
class Email {
String value = '';
}
Yugj ecydomadwq fuzijijuk xfo tialam kenfokh arc pegzaky nun gea. Gduw’f xaopu u yed luto huoroyfi asm hqans roqpc jma kamo am ew rai qom lqijbaz bead fiycosr inx tuvwacv:
final email = Email();
email.value = 'ray@example.com';
final emailString = email.value;
Ej voe ibbj yerc i jifjag ciy dil a veslev, vedw qeho sfu ytuwasdf dafoj imw seg at ib rci yibjzfacwam. Tiu’hl cuepq pid ba bi lnum ed Twacfiv 4, “Jihhhmepnorg”.
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 reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.