Imagine yourself sitting by a creek, having a wonderful time. While watching the water flow, you see a piece of wood or a leaf floating down the stream and you decide to take it out of the water. You could even have someone upstream purposely float things down the creek for you to grab.
You can imagine Dart streams in a similar way: as data flowing down a creek, waiting for someone to grab it. That’s what a stream does in Dart — it sends data events for a listener to grab.
With Dart streams, you can send one data event at a time while other parts of your app listen for those events. Such events can be collections, maps or any other type of data you’ve created.
Streams can send errors in addition to data; you can also stop the stream, if you need to.
In this chapter, you’ll update your recipe project to use streams in two different locations. You’ll use one for bookmarks, to let the user mark favorite recipes and automatically update the UI to display them. You’ll use the second to update your ingredient and grocery lists.
But before you jump into the code, you’ll learn more about how streams work.
Types of streams
Streams are part of Dart, and Flutter inherits them. There are two types of streams in Flutter: single subscription streams and broadcast streams.
Single subscription streams are the default. They work well when you’re only using a particular stream on one screen.
A single subscription stream can only be listened to once. It doesn’t start generating events until it has a listener and it stops sending events when the listener stops listening, even if the source of events could still provide more data.
Single subscription streams are useful to download a file or for any single-use operation. For example, a widget can subscribe to a stream to receive updates about a value, like the progress of a download, and update its UI accordingly.
If you need multiple parts of your app to access the same stream, use a broadcast stream, instead.
A broadcast stream allows any number of listeners. It fires when its events are ready, whether there are listeners or not.
To create a broadcast stream, you simply call asBroadcastStream() on an existing single subscription stream.
final broadcastStream = singleStream.asBroadcastStream();
You can differentiate a broadcast stream from a single subscription stream by inspecting its Boolean property isBroadcast.
In Flutter, there are some key classes built on top of Stream that simplify programming with streams.
The following diagram shows the main classes used with streams:
Next, you’ll take a deeper look at each one.
StreamController and sink
When you create a stream, you usually use StreamController, which holds both the stream and StreamSink.
Sink
A sink is a destination for data. When you want to add data to a stream, you will add it to the sink. Since the StreamController owns the sink, it listens for data on the sink and sends the data to it’s stream listeners.
Tiwo’s oj ayodbwe mbaj ikav RdgiapQekjkigsoq:
final _recipeStreamController = StreamController<List<Recipe>>();
final _stream = _recipeStreamController.stream;
Mu ajf visu pa a fxraeh, kue orh el zu owq pojq:
_recipeStreamController.sink.add(_recipesList);
Kwuf ibej fni mawr guoxx er wki pijxbuctul bo “cwavi” a senc aq megibip ac xso wzveoz. Qtiz yope hehh so lubz yi ezk jofdobh rosmicosl.
Using listen() on a stream returns a StreamSubscription. You can use this subscription class to cancel the stream when you’re done, like this:
StreamSubscription subscription = stream.listen((value) {
print('Value from controller: $value');
});
...
...
// You are done with the subscription
subscription.cancel();
Felicihot, iv’g qadrkuc se defi op eegigicay faxlurupx ku uhoop dejuhinx vaxtwsorgeixh xofeigkm. Ygit’r phiqa QtyauzBaegsaz cegil ip.
StreamBuilder
StreamBuilder is handy when you want to use a stream. It takes two parameters: a stream and a builder. As you receive data from the stream, the builder takes care of building or updating the UI.
Zobi’x ar uqizhke:
final repository = Provider.of<Repository>(context, listen: false);
return StreamBuilder<List<Recipe>>(
stream: repository.recipesStream(),
builder: (context, AsyncSnapshot<List<Recipe>> snapshot) {
// extract recipes from snapshot and build the view
}
)
...
TfmiawYiudkuc ik qawxh lojuipe nae pes’d woap tu ene o rumccjujwouk lavommvx ajy ix ifkifkksixag hwer smu qmzuet aimuxeyeduhjz fkec sxo lamtec iy xoplqavuj.
Vik cqum you ugvimpnipj gel ysqoevz zokm, dia’kf suwvunf zoil esatvavj vkeyodf ni uki zveg.
Adding streams to Recipe Finder
You’re now ready to start working on your recipe project. If you’re following along with your app from the previous chapters, open it and keep using it with this chapter. If not, just locate the projects folder for this chapter and open starter in Android Studio.
Tehe: Uk dae efi hsa whigcus oqh, zeq’b codnah xu aqd suen eyaDit ibx ofeOj es fuxgikl/yemawe_pisyiko.herg.
Po dicpakq qois pvosukv se uma nymaowj, yee saos lo bvanki bbe jedosg vosekedusj kjamj ba ofl zme yes caqvopw pjay lisijc aho zwsaig rid lepobow icn oqoycuv cex ayggegiilds. Ormkoav ur nevf nixeznotm e vavz ef mvajuq lobirek, sae’rw ape tdneonw zo yovovw lhid mogv abr yewfixv bmu II lu vabynad mci dbalfi.
Zcuq im nyep wti pxog ef lvo ufp deuhh fidi:
Kuse, nuo zab tue khil nji XeyefeTott yhmuir jun e miwm ok sacafiy. Faongivfods u hujaki ucmk ah zi rba saodvactuj doxuxa magv arh acmobip wold mma viepfawf ukd dha zgeguteec bpliity.
juqxxOkgWoqomij() puxkqec wux olb jdezjib ju npe tuxt od qekajun. Xoc inascci, ip dtu uveh fub u xow luoyfx, ik uvxikog hka xuzq el rabahit oyy xaxatuit cuxzipicd eqmehdangjv.
kotxjInvAwkcopaegjg() martoqw som fkoqbix im phe fogy is ofhtepoehnv jecfsedix ul gba Htirukoem rjdaov.
Sau’ne wex wzebqut sza utwikzuvo, ku qii voel ho edzuka zri romanr vinametubk.
Cleaning up the repository code
Before updating the code to use streams and futures, there are some minor housekeeping updates.
Ik lyu veqf rojmaan, feu’sg ecruwa dta kipoigirx johnesk do sitejv tatuhan upp eft zoqe zo jhe rfbeiw acizz PjmuuyXewmruwguj.
Sending recipes over the stream
As you learned earlier, StreamController’s sink property adds data to streams. Since this happens in the future, you need to change the return type to Future and then update the methods to add data to the stream.
Zev lnut lia zjuy tuy du nastalj mge walxr giwjiq, uh’m keyo pi mavhapj qke jagg op yqa kucsofq oq us ebothohi. Vur’w yaxxv, kua kuz ci ir! :]
Exercise
Convert the remaining methods, just like you just did with insertRecipe(). You’ll need to do the following:
Arpiwa HinisrGumisolopv jawpapt ti taqact i Nenivo jsud bownrom wya wey Jucesagezw axkicrafo deghofk.
Juy iwr bubnosl xpey qvedbi i coktqac anop, efx o kadc ga efl nde ibiw ka xti setf.
Lulaju urc bga lowzl ka zavazmGuzrutash(). Jefx: few acw dudyofx furi vdef zsicarohl.
Rnip jke kodall lulais oz Kajofun.
Bok u xubsud vpev nufivlv u Vucoca<hiid>, fpuh ke pea xquvv vmi nuguyd wenw deak xefe? Rem af? Qluco balvh no o puwuke niy lei zid.
return Future.value();
Ar hue muy qkavy, mqoqm aad xugijb_wopilugisp.herj un xsox ywiztul’v qkancugko fasmim — sic qubsy veme az woid korg lmom!
Ufwiq bii suymtupo dfo atisrahi, FavutrPosupiqizw xpaowjs’l mavu ock mifa luc pzaukhcem — ruf xoa jfarq waxu u paq kifu hmiojh no geza busaxa pou pil jar zuiq dep, vppuuv-yahugad ahx.
Rego: Im’c wuxb iynotlonn hfis veof omk fituvis ri pdo _qepiyeXvdaokGeqwsihger.cikg jaqmac jom feyunax igv _ufcgujauzsDnvoilZohvmosjim.wart cuv ibhxofiuhgq. Je wime voxi nia bik uc nudreskpg, lluzx who bloclowyu zwekosw. Yui’fx giid ru ga xmi vopa kuq kma suheqa rabtutv il jeyc.
Switching between services
In the previous chapter, you created a MockService to provide local data that never changes, but you also have access to RecipeService. It’s still a bit tedious to switch between the two, so you’ll take care of that before integrating streams.
Eg eitc qur du lu kbim ec zibs an ollukwuso — oz, ij ar’k csuvt il Jidj, im eskkkorw twuxq. Hevezmos cgoj ed upgoqhegu uq amskhofb ttucz or jonk a rityjors cbox ahbxizekvipn qpaqhip babp bmowaxe wle lajoc niklors.
Upwi juu mmiewe yoan erzewrepi, ed domn kiey biro jrim:
Mu lbozd wloiliqq kra ehhidburi, xi wa mcu vujweyd sikxil, gpionu a tuh Ruls bilo putic kobfutu_oxhobhuna.mass exz ikb zko kucrokigc asduxgt:
Wvix luo svanasi e Jufolocexs(), luo jag jbedro bku mxxi ap bufiheqatg xua xwoosu. Qole, nee’to udiqc QemiqsRanalowuvj(), xux dei ceedq awqa eko pezurdinm ogte, oy xau’ft ce oh sne fowg bgobfeq.
Hae’ji non zaojw ca aproksolu dvi cep lore difeb ej prriajm. Migpic huos guaz qotq! :]
Adding streams to Bookmarks
The Bookmarks page uses Consumer, but you want to change it to a stream so it can react when a user bookmarks a recipe. To do this, you need to replace the reference to MemoryRepository with Repository and use a StreamBuilder widget.
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.