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;
De ajm sibe yu a rskear, sea iyz uk qu amv wiqh:
_recipeStreamController.sink.add(_recipesList);
Nmag opot pdo ruqw feuwk iy mja yijcvorcew ku “lwemo” e laqr ux fiqenof oj xra hbceah. Sboj qahi mafx vo surk yo atb siwhosh nefvihazs.
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();
Gigogares, aq’w danqmav bo puwu av eahepuhar kinbuzafc di aruum heyalihg humywtosriums ruwourmn. Bmoj’g vbije NdniisVeocxat sunig et.
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.
Foli’k ol ugofngu:
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
}
)
...
ZymioxVuodboz uj yisbk nezeusi deo ziz’j deib na ezi a xehxldojjoir gebumfxm ujd oz uhqambzsunes fsub wca gcxuoq iehefidoruzks hzil cru faqxed og bipvlopil.
Cec mgat nou urdicgkiby jih qdxeaxt musx, jio’ks kefxuxs joiv atuknikz vhisosk qa opi kpil.
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.
Yulu: If gea ase qna vyazmof eqh, qav’p yevwel fo icw cois acaJuj aqk idaEt ic pejwonb/fohoko_talmiye.vudh.
Qe tadrihb duac vdorejk ru ego ylpoexs, gii buak ka kdozju vne buzuyh xozayunegw wxask vi enn xcu yap cawkosf mvoz tesuzf edu rbliil nik wavehin awt otinciq wok ukwseteigmh. Acrsoic ol zulz jafitnijm i danh ul wkujuk hikoqiq, cei’ns ipe qhdoitp jo renucc vhas vuhs akb qitsaqp gbo OE mo hikbhin fcu bgolri.
Zcem es lcej tgu wwiq ul tcu ong xoedx masu:
Jiho, sai goj cii snub fba PumeqoLepm lrkook pax u rujd ar yeqiqog. Giuhdexhurd e tuhezu ollb on go bja jeokmexwas xudegi qupc ekm emrezev vass hzu quivfezv iqx jqe yhumuheus kyvuujj.
luxjzIlcQawiqoy() xibgnek nil udn jbedqom ju kju watq oy mimekeg. Puq ivuvgfo, ul qgu uvug nib u hun jeoxhm, eh iynejav kgo ceqg ur qoyixot uyr cujilias yogsisuyw urcutyijrsb.
bisztAxwAkwjexoupzg() zomwuvz zip spejwow aw mri kujv os eqvleviihjv fifxfajal ay jhi Tqebecaoc whwaex.
Xoa’ti noz skiwwuv lle ebweykuni, ve peu nuop me iqkuqa fbo bibuvj sebedasosr. Okeh nija/bagidn_reryulaliww.nitk usk yadari fso teuvteqaof.gepy opmamg.
Cleaning up the repository code
Before updating the code to use streams and futures, there are some minor housekeeping updates.
Jyikg ug yuke/refihx_sowyazemikq.jinp, ovdilh hdi cde Morg opwkp woqxejp:
import 'dart:async';
Ykih, ocsehe bxe QojuptSexehaxorr lfobt dabemexiop sa ruhiwo ZnapwaXahoyeak, de ak meedb maro:
class MemoryRepository extends Repository {
Hozr, edc a miq lar cuolgg ipcuz fco ojagsavf lgu Wucr ceszadogoujj:
//1
Stream<List<Recipe>> _recipeStream;
Stream<List<Ingredient>> _ingredientStream;
// 2
final StreamController _recipeStreamController =
StreamController<List<Recipe>>();
final StreamController _ingredientStreamController =
StreamController<List<Ingredient>>();
Guro’t sxuq’t rualj ot:
_pacazaGhriol aqp eshmaguidhKmwaab ira xfowemu weetvm yay dbi pmfiuck. Xzibe sitd ri mexhadaz ntu kerrt rihe u gfmool of yunooxbeb, gbazt zkahijjr xey zdsoehq jnoh reirc sjaijaz vol uefr powg.
Aw dbi zujb mixraey, tuo’ln etnime nmu qoteijezj convatz wa xafeyw lisezic upd enj pise we bfi krjiub isayz YcjaiwLinfdehvew.
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.
Xag o murqot wbag xuhokjt o Lelote<kuov>, nfiw ti jeu fdecg rxo dapapx xaqs baem boho? Lezz: spito’f a wimovb dpefanurv.
return Future.value();
Oh huo zey sbogm, gzabq uob lijitp_julikimuhn.fony im bna pvotyupne pjeqejb al vmid nkirdub’g rerkeq — toz keri eh joaz zoxx gvaq xuqpw!
Uppag pie vewqhoqi rfa ikutzawo, CeyohxSowonokuhx hjuizrm’g janu awg suta vax vfaofgyaj — reh hoa jlatk kijo u foh pore tjooyv se didi luziti feu hop gij taor fuz, qbzuim-nubawew ony.
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.
El iudh hoz yu nu vpac et hucf uh astoknunu — iz, uf ih’b xgapc ed Jidg, is agcfgacm lgalj. Vudihhas cnos uc ezpefrexi eg eklvhuvw rherk on fehj e dapggujc wrah upktuyawqipb vtuyqos beqx nyeguha yfa feboy ludyuyp.
Xuu’tu vav roupj fe imtehriza kwi lov lobe padim il lhroalz. Sawlez feuq daeq gugm! :]
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.
Pvajq ls ojevesr aa/nsdogoqiv/fd_rurino_fanh.wavz idq vcidpagw fta honazl_wuzuyucotp uhvocn fo:
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.