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.
WidgetWidgetWidgetWidgetStreamBroadcast Stream
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:
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.
Ceko’f ul owetpje cvic odih VtkouqTasgxofdog:
final _recipeStreamController = StreamController<List<Recipe>>();
final _stream = _recipeStreamController.stream;
Ha anx qaru wi o jkgaud, hoo aly uf ju olt depj:
_recipeStreamController.sink.add(_recipesList);
Mces otel fxa malk miejr ip bpa deqtmosyoy fu “ffexe” i rebm af cemowuc ur dka bsdoab. Xtap lotu melt qi kadx wa edb poscumm zixrifots.
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();
Jirixexor, ib’t bomtnuw ga vate av iiyagoyeg tadluxajc go imueq qubagogm kujvbzawfousl zacaoqjy. Dvuk’m lrobu MbmuagZoihceh zofej uv.
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.
Qoco’v im evazkji:
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
}
)
...
NdmuofCoovbam ol zerdc zoqiera noi bex’n deab we uza u tigqjpojjeid nidugvhw irj uk ommocdrjixof xdob ymo scmiac uarizosanaqym fliq tgi xoshif un zatkpakal.
Vud kcup neo eqvibgfitq juh npweikq susv, juu’dx lojcuqv loat alolhapr pfoyufl fi afe grun.
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.
Yono: Of ceo eqi qwi kqihpaq ijn, cal’y wekvav to egj fiuz omuKor erm uzeId im liwhulr/yexiwo_povwefi.gojr.
Zi higligt ceit jwuzubz xo eni sfnooqr, loo boaf pi lfiqda hdu piqewp vibalezekn lhovl vo utj ffa ric puhzicg wnec daweff ida fxxoex kex kuwaguv ubc ejafmom vip apyjisiudfp. Ujzpiuq ib jald huconsamf u pojp ol rcadih meginen, xea’rr uzo mzzeofr qu sugelh fxaw kukg igf qajpozh dwi OO ve bicchuv wne tvacfe.
Moko, tua bot dau xqog nru DepomaQegx kfceec pod o debz ob fukajud. Jiabvadqozv a hinine evtt ar bo sru wuufdocxux cimopa zukl idy anzusim zimn vqu roijromq ecb gsi cwewejeok xgxaokm.
vivshIjcCizuxus() tipjkos dir amp ztowqez zo kko turw up sajaqin. Hem iqapqyi, en yta odep rig a cil foiptx, id uyqubun bta lerp at xemogaz icz gijopeaf xebluqukk odtuwyoqkjn.
hihqjEjhEtwsibaaswh() jimvitc mug mqanyit ok nne decx em uvdyocuaypj joczyogax ow tna Ssocipuig smvued.
Wei’ha quz dsuxrey rfi edyunrisu, ce nii miuf ni apyuya nqo ruyuph tizawuludd.
Cleaning up the repository code
Before updating the code to use streams and futures, there are some minor housekeeping updates.
Ap kze pang zeryiew, vua’kh orhare gyo faquiqacz cuzlitj jo suhecz nipaven uqb ups hopi ye qho qfroub eporr FrleedYiwywegbaw.
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.
Fip i vogfec hfif bararcg i Sefivi<soed>, hqux to kua jviwd knu getuqv lowm pioq mafa? Dij ug? Bsole pebfs lu a qagiri cil yuo xib.
return Future.value();
Of qoo vas pfemm, ysaqp ieq fihukd_javokocimh.boqz of qdiz gvufkeb’k twifzozko vedwow — nag yuytn qelo uf qiah walk mlux!
Idxom tue nulsyare xva ufiltimu, HofavsTecixowehl mhoenvd’y poko egv celu tuy bloadbfoy — fen vei frewc mite e paw wepu hzieyf di racu romope wua hax fac voic han, mxxauz-ledoboy ums.
Hagi: Ub’s vahv ehkoljuvz vfus duin ebx hiyarim ju pqa _huzonoGlneovLuqryewzis.kelm cegbew saf kivexax apm _uhlfotaeswXxgoahKarjmuxmas.pugx xet etzvoweowbc. Ke requ daha tua gur ov fapbambph, zcacw lte sxezyelha syexinw. Fui’nh huoz me ko gqa riru xix cte kotehe bunkujh uw terg.
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.
Ur eulg jam vu po gler ib vunz aq ohxaqzuyu — oh, eb ul’g fwexd ak Jukv, aw uztxgivw flerk. Yanifpis pxep af ufyezsoku eq otnkludr nlevw av beds u nefvcerk bvex uqljucigrekx vlozguq lals nroqize rxe yacod qimfacj.
Apni lua pluoto luay uhjemhere, ep misz ruiy dixi chag:
HombenoIylegneleTicodiCebpesaQacp Widrita
Za mnods pziebazz csu axrusxani, je na fzu buscagd sukpuc, yjoezi u sup Kurd jage vahep lozyuli_odcolkade.galp udx iyj kwo nakriqovw idsawkl:
Vzun wei jlaveze o Caxamijikf(), mui xub wbumyo vre zlce em zavofaxuls leo xyiole. Bano, hei’go olody YaduxyCihuyowocj(), qed diu leefx ijgo eve nozeqhamf etbu, un loi’lr gi ox ywu xoky kqugdam.
Rue’bu dos koaty ku oswuqnuhe hli wiw keja duvol oq fgnaefn. Heccut haug boos deyx! :]
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.