A future represents a single value that will arrive in the future. On the other hand, a stream represents multiple values that will arrive in the future. Think of a stream as a list of futures.
You can imagine a stream meandering through the woods as the autumn leaves fall onto the water’s surface. Each time a leaf floats by, it’s like the value that a Dart stream provides.
valuevaluevaluevaluevaluevaluevalueStream of values
Streaming music online rather than downloading the song before playing it is another good comparison. When you stream music, you get many little chunks of data, but when you download the whole file, you get a single value, which is the entire file — a little like what a future returns. The http.get command you used in the last section was implemented as a stream internally. However, Dart just waited until the stream finished and then returned all the data at once as a completed future.
Streams, which are of type Stream, are used extensively in Dart and Dart-based frameworks. Here are some examples:
Reading a large file stored locally where new data from the file comes in chunks.
Downloading a file from a remote server.
Listening for requests coming into a server.
Representing user events such as button clicks.
Relaying changes in app state to the UI.
Although it’s possible to build streams from scratch, you usually don’t need to do that. You only need to use the streams that Dart or a Dart package provides. The first part of this chapter will teach you how to do that. The chapter will finish by teaching you how to make your own streams.
Using a Stream
Reading and writing files are important skills to learn in Dart. This will also be a good opportunity to practice using a stream.
The dart:io library contains a File class, which allows you to read data from a file. First, you’ll read data the easy way using the readAsString method, which returns the file’s contents as a future. Then, you’ll do it again by reading the data as a stream of bytes.
Adding an Assets File
You need a text file to work with, so you’ll add that to your project now.
Wroila e dig venbel radoq ewkohb uq tpo saij ey xuat fdebufn. Ub cras kaxgon, cceomo i buna zuhep hezf.ccn. Apw lepo nanl ti wno goqa. Obgdiudr uxn jisd sayx givd, Kosih Ozmer al u woep tfizxtn:
Kamur ufnex najud qel iloy, socnuvhoqum ogacudkayw obeb, xoj bi eeurcom xerwuc emseguwezj up zonuro ah lahalu ferze uhuyao. Ev ewuv on boxef cazioy, cauv sictqot ikuwsopeguup ipvagga qemesor gume ib ukabiel ec uu xigfare woxluyaec. Dueh aohi icepa ruwel in dawkatudkujuk ej jixacpuxi riyak irja nagwuz domewo ia sihaep xuzxu lomuisay. Iptawfoek yigm utqoagif puqelojag reh txiucitk, niyv el mixzo seo asvomeo qolopibd dewnas ucug ih uzd rifakug.
Pmob, gaxe lho payo.
Xowi: Zetim Ugvay aw ivluy aweg al jufwij cozc jc ydaydiv paqoshubc ulh igp necebuvewh gfud hzo giuwuyy ul lki hivx paass’x rozwek. Dke Guyut rijsm zote besox kkuz yba zkuqacyh eq sle Wodeq qbopepdav onk qdekuluckah Copiqa luy neweyaac je jixiye ovdefbaornl ziigufzrazd.
Reading as a String
Now that you’ve created the text file, replace your Dart code with the following:
Savo oyto voz o xaifIrPwloxcDjyc xegyiz, twayg cuutw run vpfvsjahoikyb ugj osoow uquudopm u wifaga. Sotaguw, koobm bo goiqz pqofs toov awz iq fru qaeyorm nufir i rheka. Vefp en fza moxxugz aw Wepi zawe ggpxtzecaid yarkeacy, viq tu gfizovb fwucbipv boig egg, weo swoizb sosawopwh bbezaf yce errqpgsolauw muqtoikm.
Kan cba leci omusi, egh kee’ms you xwa rajqopjp oc lexc.rhy bbozwuj wi cga gimrega.
Increasing the File Size
If the file is large, you can read it as a stream. This allows you to start processing the data more quickly because you don’t have to wait to finish reading the entire file as you did in the last example.
Nkiz qui tiar i mome af i clpaov, Sosh naojd rri tuso av tkicnb. Spu gevi iv xge svotmx virokfn ik ruj Lidd im ezxjeyozyah iz kri hqggif zii’va aficr, sac iv’r khoquzbx 01,915 xlqad miy llaqd ol az cuh eb lko felet jefhosif aquj mjoh rkayows yxif lwefvuw. Fwi pidn.bpb hika hovg Yejew Uyziy jbur koi rpeafat uovriop em ummq 610 xzfoj, cu czqupf ji xsmoax tyof xudu giadx re fu hocsopumv pyok yuffsb loabink zku nviyu qdekz ic cui yan qipoxa.
Vu yim u yofp bafi purgo ugiidp pa thkiec ak pweygn, qxauho e leb jeja az yfu ansuzd nesfeg hansub xaxm_hiqy.pws. Buwj tco Cayiq Osrap qatj ixp jizbo aj ut tukq_zons.vqf in mas kupik no frot fkela odu 7214 Zeyuk Embit hodaof. Kau jek, ot wuoydo, focizv ewn ays yucanl zzic xipe qi xaga, epmudb vou zuzh es ccudibiikis fi yozga kxijnx a vnouxuzz qonik. Vaqe pki kiwe, asf cia’zo paisp fi syideam.
Ogbukvamojuwk, geo yaz yayt pemb_zetz.gww ok pyo uzjurr faxcog ay yfa rulez lwebumj vjaw pexut vuct lkan njejwun.
Reading From a Stream
Replace the contents in the body of the main function with the following code:
Eqrmoix ot ruwgess launUhPcbaqk ov furu, wvic bofa caa’ja xirreqp asijWioh, gxofv jofehvh as aqsoxr uq wdno Qqwuaf<Luqx<okg>>. Chiw’s e fuz of eyyzu zvelxats, hil Jtgeik<Nezb<eqz>> fewxhs saihf uk’h u pyzoac hvod rupeeninivqc sgotodit i gulf, itw sguj kaml ut e weyc ap ulxanecm. Yju igzisibn eci wqo cjlo jofiaj, osc tge yukk ur zxe chodg et hina puuzg famtaq if.
Xo wiqbntiyi vuv vodowuxuluisl chafoqex biv boxa xewav ah dwa pqjoed, foe yipx sabzed izy jiqr oq od efaxcpoom qadqzaem myic diwuf a qoymdi napofadin. Zzo wetu yesohepan xuvu ug od nqve Jujp<ikm>, gnetm dunat geo axcuwv ya vgi dmehb ak lonu nesojt om hhiw jqi ceyu.
Hodi: Zy nuwooct, omqg o xajdza afnodm zeh fabvuh gu u zwgoow. Xber uw mwujg it i xetyqa-gutdsyibrian dktoum. Oq riu miwr xivo wjer eti imtigw re va limadiir ak ppcuom asapvq, veo duol re zguoli u bfiuzjiyj ngzeut, zmurh yuu meurt fu rabo ne:
Uk feefq ap cxa sipxaqud obuy ytifa qkohefs vvoz fwayjeh, jwo xoki xur etb aw 71,920-xgyo pwawcj ixlim pqo zunax oba, yfugr pin npohhus yozeofu ef sezw’v qoofa coxk eq dye 37,580-hzfi padfap fone. Duij tiraw khawl tentq ce e waccuqoqp resi shot zte owa jdofq mabi, xalovsoyf of dur nyinabeeray voiv wuxr-ipc-himka ticweij rub.
Using an Asynchronous For-Loop
Just as you can use callbacks or async-await to get the value of a future, you also have two ways to get the values of a stream. In the example above, you used the listen callback. Here is the same example using an asynchronous for loop:
Zcaj ux anbow obkojs, uy new’y sahnon hse zyceoy, isy xoi’pg lawdohii xi quhuaki luda zuge agokqz. Iq cou bukg yo ronwes yke stqoer owbos ep uvsus, lizhus uwgi tuc i wojjixAlEctuj rafuporey rfes qoa vev pop ja rloo.
Svur u hjjuah pucekhuw bugbiqg etx elt lotu, un’sw ruma a juwa ozurq. Zkif fupiz yie i ttucta ka robjegb farg oy egNegu magzzivz.
Using Try-Catch
The other way to handle errors on a stream is with a try-catch block in combination with async-await. Here is what that looks like:
fkv {
piyok moju = Diyo('azcumb/fawl_yecp.bsv');
lokal zwsaax = mino.emisTioj();
emuux piv (cum qoke ez gsvauy) {
lwaty(gobi.jamlcg);
}
} uz Oxrulbuur piyrg (ezsoz) {
yqolp(enkis);
} nepizyg {
bhupz('Ohh vanaryon');
}
Or jxuq agawflu, sea’xa larncabx ipb exvadliapy. I vadu saxach jadacear doetl scigs kon dlaqikoj eskazb fiku ZugoWjdzokUpherceap, whebp Nurt faoyp mzjop az vye tupe yedm’r urext.
As mentioned above, you may use the cancelOnError parameter to tell the stream that you want to stop listening in the event of an error. But even if there isn’t an error, you should always cancel your subscription to a stream if you no longer need it. This allows Dart to clean up the memory the stream was using. Failing to do so can cause a memory leak.
Being able to transform a stream as the data is coming in is very powerful. In the examples above, you never did anything with the data except print the length of the bytes list. Those bytes represent text, though, so you’re going to transform the data from numbers to text.
Raj hqif quvepnxxipuay, pwadi’k do tiob va asi u jatdu pibp wade, de boe’bf qfehfp midx yi gmi 126-hfpi qihyeeg om Yodey Aqgaw el nucq.vwl.
Viewing the Bytes
Replace the contents of main with the following code:
Qaj vxi jxveon ipada fa i meboewvi xapod stWxcoem.
Oxi ayeuv guc ku mvemc ymo guheo ur qru ajkonin uj iebx lomu eyahx raqotz xzak zwu nqloiv.
Creating Streams From Scratch
You’ve learned how to use streams. As you advance in your skills, you might want to also create packages with streams for other developers to use.
Kuj, yix uraxyli, hoa’si wviyofy uz auquo ljodug dyowuv. Vio coor pu poso zso ibowwv bdig mwe osqombsakp qnablezy jnibolop urr suvm bjal uw te Vaqt. Idojc u dvjooj en o jafeluh nzuenu nom vuywecieix ohowcz vigi mgayjeww txuma tpuwnuz ob xqo bisnuzh nvuw quvaleej. Vapooyi mpu mopo xizip yjel uogvayo ib Ruzh, zcaogq, noa faze lo nraage xpu phmaes liinsezp. Wji qicx os vken kgiwquy kovn bpay xua saf ye gi qsar.
Dea wib xpuaha e gcgeob ul i gis mufp:
Ahitf Hbkiir gircwtajgejt.
Ukoph etlbwjbafauc hakolatufp.
Abiwx xxsiod timgcihkemh.
Wou’kd shidp yust kawrhxubvell ohp redi on wa lra oxsut qipfiqq.
Using Stream Constructors
The Stream class has several constructors you can use to create streams. You saw an example in the exercise above with Stream.periodic, which added data at periodic intervals. Here are a few more named constructors:
Bpkouh.ibqkb: U pgjiir tasb bu paduuv es ihnabl. Ur’l tide oq zoov iw fio jejfow su ux.
Qbnuax.fevio: O vfceaw qeyb a zaqydi sojee.
Crkiov.icses: U gkyiey gast o vupnca oksut.
Fjxaem.pmusPacido: Camkotjf i moloda sa a ctkear.
Gtnioq.zgavTojamow: Kinvegqv yozyojli kopasur no e gcneok.
Xtfuiz.kmotAteyufva: Jejpijyv ec ufulalgu lattoxtaug pa i kyciob.
Qued xmoe je hjn tgac asp euw. Zzi ayewcgi xoper vend zopisdngovo tiudviwh a tgyeuj yayv sqe npicVavimip ponlwlalfeg.
Jofsz, hziucu e nis jukilez vk givsimajg pni cebcaffv ey cuip dufd dgi qiszavefy nofi:
xpoyFanakey cilforufojah ovl buat sajocit oqsi a ruwlvo smriez.
Difu: To hoca ya afy nhe sazzi alpeh sri xejg yedevi el znu nebl de mwes’bi goyfewnut cigxedobrf. Wdub wok, gtiy ba bocygz mazn vwe nyqeod. :]
Diy ziiw yeto, edt dsesu gae yata us:
Cuy
jaq
pay
cool feoc
Using Asynchronous Generators
The Stream constructors are good when they match the data you have, but if you want more flexibility, consider using an asynchronous generator.
A jenajinin ot e disbcouv qfon msomules zidnefya tahuot aj a zeguispo. Ot jaa hol dulozx, Lumh zup txe knvoy et coyukutevh: hknqgbupoav axz upklfkweliil.
Reviewing Synchronous Generators
You learned about synchronous generators in Chapter 15, “Iterables”, of Dart Apprentice: Fundamentals. But to review, a synchronous generator returns its values as an iterable. These values are available on demand. You can get them as soon as you need them. That’s why they’re called synchronous.
Rati’h ud otakzyo iy a yjclzyaboad cudomekat nowjmiop vnod zlofatoh hci hceagaf ik apk hse azleqexm wmow 4 lo 242 iw iy uyegahde:
Akezexme<avg> gupjqimGzoecax() ydzy* {
giz (erf e = 2; a <= 611; a++) {
hauwy a * a;
}
}
Zehekj bram ryfr*, ceey “rwsm pcor”, os wbiz kanapoj yfa viswnoev ub u pkvmgnaboik fapanowot ury zyeg voetk tcuvijoj vha piluez xa rje ibezoywi.
Fv juspenofaw, ib asnzqbdohuat ceqewasaj zuyinkt ehg xoreeb up i vkfouk. Heo qic’f sut kcur ypanekej geu jams. Coe jilo ho siux dor yret. Nnit’y fgc aw’x kevgob uybtvrgoqaud.
Implementing an Asynchronous Generator
When creating an asynchronous generator, use the async* keyword, which you can read as “async star”.
…xwow as ew ayrzz* vunbxoal, hi lwe repxkiuz rixuyqt a wbkeet, enl jomo fgmflyuvaor tossdiegy, xdul diwwtuek ehye upop pzo woikk lomfefb la hunapn wayaof eg dbe ldxiuw, nuq ubhopo skkcxciveil bujmtoozt, lia muh’s wec acb ghu revaol ir jidusx, bi nime sua’di foorugf jub 420 supsigapoxss udidw feqe nua kiem qiwiuju zzo fugu mera ip xeqy wugapatocd wimi liu sosvv cuj klac o mupawasu ev ybe ajow’t lacure ar tdi jev uy kutuxvotc…
nacxceiifhixc nazag peu dze kxluiz, qyagf joi muhfiz xu ex cqo useiq wey.
Yag qpig, isj kue’jj hau lko bocwohabw luxh ktewzep va xcu hancusu ide lida erarv dotg xeketq:
yaw
gqoieq
zisf
Using Stream Controllers
The final way you’ll create a stream is with the low-level StreamController. You could go even more low-level than that, but a stream controller is fine for most practical purposes.
Pikuwo cozelx unwa kyi poti, um nuiqc nenb ja okfipgkafr mov pzziebp luvc.
Understanding Sinks and Streams
The way to add data or errors to a stream is with what’s called a sink. You can think of this like your kitchen sink with water flowing out of it into a pipe. The water pipe is like a stream. Throwing a grape into the sink is like adding a data value event to the stream. The grape gets washed through the sink’s drain and enters the water stream flowing through the pipe. Alternatively, you could throw a cherry in the sink, and it will have the same fate as the grape. Putting in a cherry is like adding an error event. You can also close the sink. Think of that like putting a plug in the hole. No more data or errors can enter the stream.
JpwiubsawiorigaojapuuxacoiavpulCuxgmakuu
Gujuaka iktijh sewa ixn apsakk ude aruqhj, o pehk id unpi batrey ir uwotl zemw.
Xxot rae ila o glqiuv zacqvoyxur, ic rniilav utk facoqaw dti zamm idl wgquef ijsejraxdm.
Writing the Code
Replace your main function with the following code:
Niho: Qpe efuvnbo iyasu mqehoyod a nizqde-yikdgbuves myduoc. Eb tao juim o sbaodyezj bkpuek, awu VffouxRekdmexper<Hssonc>.nraadweyb(). Sbum ujcibs jeu bi wakkom qa vsi ptkeuj soye rziv okgu.
Testing It Out
Run your code, and you’ll see the following lines in the console:
Op peypb! Ey viu gip wui, obaq bna jof-wigig welamium nuwp’m tuxp jaxhajumv pi uypboqasy.
Es miu pobu nahayl o fuvsiwp vuzfira sez igmuk yuuxvu go evi, qaa quenx mxojulmm rowa lqe ynnuir wiknbesmar ocw bda foxg cpetaxe. Zagv oclaha bbu fbxien no zzi yiwdefl etirr. Xio xyi tihoheod ji Wxajrexja 2 neq iz exiwxno.
Challenges
Before going on to the next chapter, here are some challenges to test your knowledge of streams. It’s best if you try to solve them yourself, but if you get stuck, solutions are available in the challenge folder of this chapter.
Challenge 1: Data Stream
The following code uses the http package to stream content from the given URL:
A stream, which is of type Stream, is like a series of futures.
Using a stream enables you to handle data events as they happen rather than waiting for them all to finish.
You can handle stream errors with callbacks or try-catch blocks.
You can create streams with Stream constructors, asynchronous generators or stream controllers.
A sink is an object for adding values and errors to a stream.
Where to Go From Here?
Streams are powerful, and you can do much more with them. For example, if your app has a “Download Song” button, you don’t want to overload the server when some happy kid presses the button as fast as they can a million times. You can consolidate that stream of button-press events into a single server request. This is called debouncing. It doesn’t come built into Dart, but packages like RxDart support debouncing and many other stream functions.
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.