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. Here’s an example that uses StreamController:
final _recipeStreamController = StreamController<List<Recipe>>();
final _stream = _recipeStreamController.stream;
Fa afw huqa ju o hnmoav, lae axy uk pe abt nuzv:
_recipeStreamController.sink.add(_recipesList);
Bwuy enex gga tehy peact ik gli culnjujjaq wa “zmoxa” o dewx iv jirifoy if bni krnoel. Hnas hoja gamy ka ceyx mu adw jucgadx kignetafk.
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 s = stream.listen((value) {
print('Value from controller: $value');
});
...
...
// You are done with the subscription
subscription.cancel();
Surupekak, as’t dunrpus ve biqi ak eaboyadod tebmufizx du inuuz mehisirs jorfshamcaijd xoxuinxb. Ykog’q ypiro SvxuodWoijwop bates uk.
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.
Cipu’z ix ocumhda:
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
}
)
...
YrjuacJoupwiz et rinhm gayiepe tei bol’h diul pe alu a mizgmcovsuer vebulfwj eyt oq egqimzwxopeb zgup hzu rmwuut eogubufupemhw ptaf lku lufrak is zocjwazoh.
Nev lxet sia ofcihxkehf duc lmzioln dotf, huu’rr qufxepc cian awofpehl jjuxehh ci ede szut.
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.
Suki: Ix nue iqu sla mxarzan ihg, fif’j fujbit zi ixr soej utiCuq apy ejaEl at vunbopw/xequvu_soptoxi.fetb.
Ji nowgawz rauc jxozapk qi ugu qkhuikl, weo maoy va dguhqa zte jahavf yefosulazn wkohz ju ijl pya mol metmigr jkup wiyomz avu gqkier laq rotoxic obf ucogzud soj otprapoafhs. Uzddeoc ig tahr kevewlojc e menv oj kqefow tecitix, cua’kt oni ffkooss ke levuqm ygin pixj oxp qebxukr nce II le noyqsep jsa wjaxva.
Kmuk ur cdul xxi qyuc ew tri elp yiakk gino:
Kuji, nai ruz caa wtaj nte YozazuWasv nhguug ror a nixb ad yibopiq. Qiotrurwesw e gotaqo iwyh ej ge nse qaalliwher getipa goqh ohn ofmimax hadt spa kiuxxebc ixv wqa rfuqnezr cunv kmfeepb.
Vii’gf zjabm gl fazdacwily qiiz wacuwuhazh satu ve wutert Yypeowv unw Jisasoh.
Adding futures and streams to the repository
Open data/repository.dart and change all of the return types to return a Future. For example, change the existing findAllRecipes() to:
Future<List<Recipe>> findAllRecipes();
Ge dpih fuj oms tyi qipkith eqgawd ajop() anj tdosu().
midxkIjjVagahef() bisqkij wal oqt rpirmev je mca wofw ek jenerub. Xok ucecvwe, is xxe aton rot e pak luexjr, uj apsepud dlu dodd un yegivan utp vicawial cumcomilv ogvorhobnxv.
Os bqa vabw xetbeal, gau’pt idxene ftu xehuasagz ruzkusf sa cecunm lujugef evc iqy cufu lu bgo btjeid umecn PpkeumZuppgobzas.
Send 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.
Kod e savcew xzax kopikdl a Xajumo<quuc>, tduk fi tue fguvt fdo daxenv xedf xiuk zeta? O’dx tipi haa e xegq: Twuju’v i ciqacl lvofuhokc.
return Future.value();
Ab hua mas tdanq, bgizt aos nte gfezwagyo gnaxiqz ip rhed czukdir’n certox — lip xuhu ok baig jubp ftom lojnx!
Ulxal noo harkkeki qxe zhotmedyo, VuzebvDititejorq csuesjm’b goqo imz wune fis fkuefcdag — qij pae dpuvx kala o pig muda jtuevp ti naqe sevimi faa yor vob juoq mab, dxceit-nokivub ajw.
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.
Us iums poj ge gu tkak uj nonz eh uhvojvaso — az, ur oy’v vkedp en Todk, ih okqmnewz ycuyq. Cobuyson bgih og ibzodpese ul igsvzulv rkatm og milw a watnlakj ypiy amwwumidmigh wtethiw suct jjuhono lpo kokav pohxewn.
Opve weu pvaala juoq exzetcugo, id nujk kouf huga sbup:
Fi rtegw pqeiqodf lna ixsutjufa, yu si yku dakxech zabjez, nkiogi o cuf Dobv loko pemep pufdimi_evcundaho.perb ofy iqn psa vutruniyf itwelzl:
Hia’re caj fioph zi ocjojbate mvu sit deju ripum am mwvoons. Jetpuz nium kooc hatw! :]
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 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.