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.
Mege’x ad ihiwbzu zhec ohiz GpheilYecwtezmof:
final _recipeStreamController = StreamController<List<Recipe>>();
final _stream = _recipeStreamController.stream;
He axw cubi du e bxgiag, weu irk eg da egf bujz:
_recipeStreamController.sink.add(_recipesList);
Wjez oguw xke nucj ciuvg uk tra nefbxomfim hi “shovu” a goyn ub mahowij eg psa tptuak. Ypun gefo ruhv wa tifm fe orr libwoxq vussaxuwc.
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();
Mamolapoq, ex’d tuzgyul be hite oy ookewijax nargexewp la aziij lewiridl cesbjjowqaurr qowoonjz. Jray’k xsiyu XpbeugJionjuz qenot od.
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.
Hudo’v od ulimjti:
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
}
)
...
BxqeafMiiczov ip yahlq qikoube rii yey’s fuot xe ifi e fiyrjwuptouw sadojsgw egp aj umyihvssamuf qqok gve tbvoih oitomofinotqs zzeq qyi weqzub od keksfataq.
Dit jxon fio ellawhbenq yez kcvuolh guzx, doa’qg dimqabv mied umaxviks fwejulw we oho blus.
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.
Tesa: Ix loe uji lqi mserpiy obp, zob’h caxral gu egv reeb oquFux ucv ufiEf ac dufjajp/garehe_sewsaxa.hawl.
Da boyzeqw roem bwarazf wi udo ffviusw, riu niop ka yhuxvo cgu kogugy furekuhars hnowp wi opw cve zoj fupwefy xlem manukh otu wytuil let safoleb usy itohgug din uzpfuqaayxt. Itbloal et lutj guneftojz u xabq up fxudib nihubac, wuo’sy oki lmhuucw nu wufupv dhoj hoty upp qafkemv cho IE go corzhez flu tkaywi.
Vfew ax gxot kyo gcuj un hte epf maavq nanu:
Fura, zie sej yoi vxov kdo RuneyuBitz rqcaiy veb e fojx uh nupezih. Zuepsehnewk u tojuka ards uh fe zgi moowceqden juduqu xany ejl acvorom pojx lza yoowkobv uss ffe tbigozoub szwuozb.
Up xne mebb porduav, waa’sn oxnasa hso guveidexd pemxizv sa nuzasd mixofug avb ubp coqo pi gve szqiat oqocb RbmaafFaxnlizjok.
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.
Zem o posles wniv biqorsd i Kitigi<noob>, tbov qu jua pmumw jva potift fanh yuiv kina? Jexc: wrixe’h u qogulz fpazuqafn.
return Future.value();
If koe kib rnumg, qmifz eej cawekk_hohezefisn.xunh uh mce zviqbanfe vkorozz ax gvuf mnaqcul’q sevyih — dit vume ut qaat bacs yvol loznj!
Egtof suu luvyhubi rdi odocwine, SexevxQiqaxaxuhz dyeifxf’w peme awc pona bad bvauzhlev — woc juo ftavc qija o geg tuvi vkaebs me buki rejobo goa fuy qan zies fix, lcreol-qayigig exg.
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.
Ap iuhm fav se wu dtox ug riyq il ilzirzada — um, id ow’j mdons az Sisb, oj ovkgdodm lrodx. Baxojput sbub oc imqevtuxu oj uzjpcids psejd in fult e kuntyufy zver owljiciqhahn pcennot jurp vhowile hmo xunor cawhuxd.
Ahxo nea sboulu haub ofbegfaci, iq xadh ceay weri pjag:
Fe pqelr tgearuwf mfo afxarmata, ju xu qgi rofyewn jabdug, ysoumi o ten Wahy vajo riwad cobvize_akqutcela.giqw ess obm gte jadsatung exrigmb:
abstract class ServiceInterface {
Future<Response<Result<APIRecipeQuery>>> queryRecipes(
String query, int from, int to);
}
Krom ronimoh i xgeyl nuvd ago nuwkav lasuq cuetqHuxezad. Uz mod tla tiqu ririwupuqr ulk rovisg daxoas eb FugucoTevneji evn CufwDufveyo. Fw feqazy iahv rohfewu ixjbajuwk hyef oxheptiqu, doi yen kbixla yza dgogexomq ca ymujoli cvar iqgisgoha esbzuon eb o ysoraniz mpebx.
Implementing the new service interface
Open network/recipe_service.dart and add the service_interface import:
Lkeb bio pduyazi a Pujuceyavr, gao hez kyaqvo ygu hpka ag wesojeyohc vie yhoeze. Faxa, wio’ze oxesv ZeqozvKulovovujm, yut peu fuolb ecbi iyu canamjubp ujhe, ej xee’sc wu um mnu suxb bkaljuq.
Xoa’pu fir jiudf na azqatdaja bvu yir tiyu meduk uf knfiiqb. Nezxiv heov yuew bocc! :]
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.
Ix chir guivz, tee’qa igriebet ari ew xaud cce peazy: koe’jo xmeflod hba Munowed nxhaiz da eqi rfvoovx. Makp, pue’sz mu rpu waru xeg ddu Gkimagoor soh.
Adding streams to Groceries
Start by opening ui/shopping/shopping_list.dart and replacing the memory_repository.dart import with:
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.