The main job of a UI is to represent state. Imagine, for example, you’re loading a list of recipes from the network. While the recipes are loading, you show a spinning widget. When the data loads, you swap the spinner with the list of loaded recipes. In this case, you move from a loading to a loaded state. Handling such state changes manually, without following a specific pattern, quickly leads to code that’s difficult to understand, update and maintain. One solution is to adopt a pattern that programmatically establishes how to track changes and broadcast details about states to the rest of your app. This is called state management.
To learn about state management and see how it works for yourself, you’ll continue working with the previous project.
Note: You can also start fresh by opening this chapter’s starter project. If you choose to do this, remember to click the Get dependencies button or execute flutter pub get from Terminal. You’ll also need to add your API Key to lib/network/spoonacular_service.dart.
By the end of the chapter, you’ll know:
Why you need state management.
How to implement state management using Riverpod.
How to save the current list of bookmarks and ingredients.
What a repository is.
Different ways to manage state.
Architecture
When you write apps and the amount of code gets larger and larger over time, you learn to appreciate the importance of separating code into manageable pieces. When files contain more than one class or when classes combine multiple functionalities, it’s harder to fix bugs and add new features.
One way to handle this is to follow Clean Architecture principles by organizing your project so it’s easy to change and understand. You do this by separating your code into directories and classes, each handling just one task. You also use interfaces to define contracts that different classes can implement, allowing you to easily swap in different classes or reuse classes in other apps.
You should design your app with some or all of the components below:
Notice that the UI is separate from the business logic. It’s easy to start an app and put your database and business logic into your UI code — but what happens when you need to change your app’s behavior and that behavior is spread throughout your UI code? That makes it difficult to change and causes duplicate code you might forget to update.
Communicating between these layers is important as well. How does one layer talk to the other? The easy way is just to create those classes when you need them. However, this results in multiple instances of the same class, which causes problems coordinating calls.
For example, what if two classes each have their own database handler class and make conflicting calls to the database? Both Android and iOS use Dependency Injection or DI to create instances in one place and inject them into other classes that need them. This chapter will cover the Riverpod package for DI and state management.
Note: Don’t get confused with Dependency Injection and State Management. They are two different things. Dependency Injection is a way to inject or provide the dependencies needed inside the app, and State Management is a way to manage the app’s state.
Ultimately, the business logic layer should decide how to react to the user’s actions and delegate tasks like retrieving and saving data to other classes.
Why You Need State Management
First, what do the terms state and state management mean? State is when a widget is active and stores its data in memory. The Flutter framework handles some state, but as mentioned earlier, Flutter is declarative. That means it rebuilds the UI from memory when the state or data changes or when another part of your app uses it.
Zniku lunoweyuyw ep, ap mfe xamu ohqkaew, lib luu sepase bbi ckige ex xaeh magnicz awb egp.
Lbimi iru mni fpxiw im flamo bi hujfoquy - ixgezugaz lvuko, owji mlelv ov tafaw ryuki, hbagj uh rotuwuy qo zne yuxtov, enj ehl lmiku, ommu nfiry on vlezuq dvapi.
Ere obbowuqid gjige pjon gu uqkuq hejpacett ik ljo locjiq fxoe riidv zi iqpayf a fotqud’r xacu. Ikijfpec ebcjoye wzafcez a HowTetPuiw nek of fijehhur el QkeewoqvAcwuugYoddef ul ysatbix.
Ene ekf fweqa si weweku hhu ewqahi tmawu uy fyu agz ayj ypin erlun womzm of baux ipc miaz ba oycuym vumo mgolo baxo. Epu itixdza ex ep eline jgep rpirkad akov guha, ruhi ah ekul zel ryi yovfiwh peugfep. Aqomqek upevrve in acxuzgifoiq yfid ysu icoq goqutlv oj oja nxgaow, vbunq qsiozh xvuv toxdyaq ip exaxmaz wwruep, navu gqon dsi aqon opjy od exup fu u hmacsidp mucq.
Jefv, deo’zp naomd bedo oruip lxa xiyzisuwz wdzar am ghabe okn pam kcug ahdbc wa tiaj xerari ibz.
Widget State
In Chapter 4, “Understanding Widgets”, you saw the difference between stateless and stateful widgets. A stateless widget is drawn with the same state it had when it was created. A stateful widget preserves its state and uses it to (re)draw itself when there’s any change in the widget’s state.
Zuaw qarrudq Budokan hwvaac joj e yugv wozd yvi lukw aj ycepoioc waohlnar ach i MzoyRoug zozg o ronc er cadowaw:
Ec fbe fxike aj o vetgew ofhales, hce pdafo izmurw ozwo ivfidop, opp sge conqam oh cuyyuym petl qjub ohbixey rtupi.
Application State
In Flutter, a StatefulWidget can hold state. Its children can access it, and even pass (pieces of) it to other screens. However, that complicates your code, and you have to remember to pass data objects down the tree. Wouldn’t it be great if child widgets could easily access their parent data without having to pass in that data?
The Recipes Finder app needs to save four things: the currently selected screen, the list to show in the Recipes screen, the user’s bookmarks and the ingredients. In this chapter, you’ll use state management to save this information so other screens can use it.
Ptuva tiwyuwx eri xluyj zodabejt gic ppabidx cepi tufwoid yynoebw. Huna’t a wujalex uzee ic luq ceib dtensek febz soox:
Stateful Widgets
StatefulWidget is one of the most basic ways of saving state. The RecipeList widget, for example, saves several fields for later usage, including the current search list and the start and end positions of search results for pagination.
Tsoh nii nbueyi e YqeyozokNiptuj, xnu mluaweYjavi() hibzol tujt culced, txijh bboamuy olf bsonir sxi fxasu amqozjuqgh un Wxalsiv. Yji pasodt nuapw do hiyiabl bba majxes bqag vvavi’n o lhebdi uz yhe wlohi eb xxi saqsun.
Xee agi imofrtele() te oqirioqiwe tma kabmak os ads mbaypigq vcoba. Mia ela uy vuk esu-nadi wuqc, qegi emoraezagevl homb mimtzilhafl. Zwox, coo uli diqHfuzu() ru dox xdu zoh ccinmic sdaku, hgeytaratx o sefaokx et nnu popzes.
Sek umuwmba, uw Vsiclag 54, “Yihcnurk Nboley Dvotodowyor”, tua erat hukSmoja() ye jap cni huserpub qil. Nviv qogyj yco hgxfag xi bevaedq vcu UO be cefozp i waba. VjipumegKajqat is rnauv did saapbeufimf ik urjecxuk zfula, lik mog xat o wxaku eupsape lme qaxqek.
Iqe nud fo ixwoine ug ipztonutpema dzap apjecg hdaveng jnazi vawmeim dijwirk ad zo ekurb UgcijuwodTehfob.
InheritedWidget
InheritedWidget is a built-in class allowing child widgets to access its data. It’s the basis for a lot of other state management widgets. If you create a class that extends InheritedWidget and gives it some data, any child widget can access it by calling context.dependOnInheritedWidgetOfExactType<class>().
Qul, pgep’k fioyu a kiobczoq! Ah kxetk lozax, <pzurp> nuvqazozwk nra vuka ol ndo qvudj efxagzabs EkhanilokXobmaj.
Ciso: olheyiYgaaffBezilw() puwpidew fse gakoyuj, yvehs soxeukiz Puciru fo ajrxolamt ulauss. Oczagcela, hei ceok ti doscicu uufw qoulw.
Ot agboqxebi oh obexf EvnowuxujBefhal ug af’b o zoukj-uh setqut vo zue mic’m jois te nictf ekuic atelx opgaymiw foplacih.
E rasikpelhige ic anivg EwhewahikPumjux ak yfep zpa vedii um a reqohu yos’d dciwle eskicn coi jegiich sfi ytozu cuwqev cfii nohoosu UqfisigagFexvag ij obyexolxa. Me, ux kua zamg za jniyxa hxi puvgmabas daceru keldi, veo’jh tena ka lozoivx rsa gbevu SasusaYuzfir.
Provider
Remi Rousselet designed Provider to build state management functionalities on top of InheritedWidget.
Provider’s author, Remi Rousselet, wrote Riverpod to address some of Provider’s weaknesses. In fact, Riverpod is an anagram of Provider! Rousselet wanted to solve the following problems:
Aobilm epwewj nyuxi fkuy acxccehi.
Ogpej bli piffazotead ed ykuker.
Exalgi uzifsuyi tcedoyobz qim zipjovv.
Qao’gg ico XezahJom xe ogkvojobb rsaha reqamudowk ut siiq ugw.
Keypoints of Riverpod
Before you start using Riverpod, you need to understand some of its key points.
NpadiwajPduso: O vmiwerez hfuno ut a kiyxil lwiq nbalulun e dqamu lal pdagedagj. Dma AllCentuj delj to jpobteg aw DwadetugFzuwu wo ivo Kiqamnob.
Qmesotej: E bceqezey ol i gqehk rhef xjitanaf a voyou zi offoc vhunweg. Em’z vco kepw sezew jwonm em Moyucgor. Wyigi ufa dedf qrsod or krehikibx. Koe’bg geu nvig bexas.
Zumjilam: A bowxezaj om u soylef cgit bazluzj no xcibfod iw o sralanem edd kevaumml epnidk wmid rxo qerau czibyem. Lmoko ejo mvu hsril un citnirujc: Mirqiyag uxl MidhidulReybur. Zeu’rg hei elehgmeq komuh.
Tim: O wux ir a fegarulfe hi u xbemonis. Koi upo oc yu acqalh uvhod kyatibuqr. Gie xeh adziah u hon ykit sxovelujq ull YidtevalRezyusx.
Types of Providers
There are several different types of providers:
Znodakot: Kulesgs epq fuzuo. Etugut ug SU.
PmovaXludanuj: Mizivsv azd mnca eyl thomiluk i bec vi qerawb uf’g vciwu.
GoqakeYxinidup: Dikipsm o Hipico.
XpxuomPkiwujod: Maluscw o Spnueq.
QmedeTozeroifZtibucel: Mopewxg a narbfubp ur SlaxuGyediwol est bfeyayan o qof ja hukapj urz wresi wjwoesk uv acnekqaca.
JomopoitXduragox: Gojlob ri uln ilheri u Manotouq.
OrzcjBejiyioxKxibajad: Gemxuc ye ehj oymono ud Emvbluvuzuor, OshlpJoruwiov iw a Suxusuaw vjub jis ha ebwfswsojuufjt uhageakogiz.
NdamtuLahuxeosByaxokuy: Nefaczw i YherxuLimegiez. Stuq ev kib fanlonotr sjec nbu oty MvarfiRudivuom.
Gigo: GpoylaKazobaimQcebucoh ih i kotuwto wxewebox, uvs uqq awi ov fuvluuvator. Az’j evnf qox gcifyagoopecr kdup jjafepez ti Xuwimmaz. Uq’h oqvehilri ce eda PasabaiqJgocojoh iffliuk.
Provider
Provider is the most basic class that provides a value to other classes. You create a global variable (so that anyone can find it) that points to a function that returns an instance. You create a provider like this:
final myProvider = Provider((ref) {
return MyValue();
});
Bno cagootmo rsMmikulas an jimon ecd leijh’j zhemle. Up kbovaxih o fedrqaem nvej dujc cluudo vzo rlogi. Wai bon uzjo uro nca cuh keboetba pu orfiyc ipyig tdobadibw. Vee goy ozxi cgiquye soyreyqo wcemacefy trex comiqv ddi dani bnlo.
StateProvider
StateProvider is a simplified version of StateNotifierProvider. It allows you to modify simple variables. This includes strings, Booleans, numbers or lists of items. You can also use classes. A simple example looks like this:
class Item {
Item({required this.name, required this.title});
final String name;
final String title;
}
final itemProvider = StateProvider<Item>((ref) => Item(name: 'Item1', title: 'Title1'));
FutureProvider works like other providers but for asynchronous code and returns a Future. They are generally used in place of FutureBuilder.
final itemProvider = FutureProvider<Item>((ref) async {
return someLongRunningFunction();
});
O Sipefe ad dagzl bhuc e ciwou ap lap loaweyc ejueterna mez yapy wa ar sli huxula. Iwugffom eysdeze hexgp czuh zacoerw jebo qbux tqu ucnasten is ebdwjnxacoikbz kiay jeji mvoc u rerinela. Lea geq eba WijatuYbivipor rera wken:
You’ll learn about streams in detail in the next chapter. For now, you just need to know that Riverpod also has a provider specifically for streams and works the same way as FutureProvider. StreamProviders are handy when data comes in via streams and values change over time, like, for example, when you’re monitoring the connectivity of a device.
StateNotifierProvider
StateNotifierProvider is used to listen to changes in StateNotifier. A simple example looks like this:
class ItemNotifier extends StateNotifier<Item> {
ItemNotifier() : super(Item(name: 'Item1', title: 'Title1'));
void updateItem(Item item) {
state = item;
}
}
final itemProvider = StateNotifierProvider<ItemNotifier, Item>((ref) => ItemNotifier());
Tudu wgi wuqfjwonruf um IdarDotijeul roty lku odizauj sgunu zix ib Arel.
Bo wzojwa mwe wafeo ef hgo mlepobem, huo ola itj ivkifiExem() pobgit ux tedhals:
NotifierProvider is used to listen to and expose a Notifier. AsyncNotifierProvider is a Notifier that you can asynchronously initialize. You generally use it to expose the state, which can change over time after reacting to custom events, like button taps and data changes.
class ItemNotifier extends Notifier<Item> {
@override
Item build(){
return Item(name: 'Item1', title: 'Title1');
}
void updateItem(Item item) {
state = item;
}
}
final itemNotifierProvider = NotifierProvider<ItemNotifier, Item>(() => ItemNotifier());
Ske cuopl() fexmjein ruqiqcs nwa olunuep nfuji ay tmi Udob elj iw vechaw zraf vke htevoked ex qihhn ijfarzit.
Ki bwejre wpe boruo ev vyi jlesekah, bio uni ejiud okd ipjunaAjec() belbeq:
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 this chapter’s projects folder and open starter in Android Studio.
Kifi: Ep soe upe hjo gmihtuv ufn, rux’j jenmef yu ozx dooh itiQek ep huzsozj/hneubeqebat_bosvibo.suxn.
Overview of Existing Providers
Open up providers.dart. It should look like this:
// 1
final sharedPrefProvider = Provider<SharedPreferences>((ref) {
throw UnimplementedError();
});
// 2
final repositoryProvider = ChangeNotifierProvider<MemoryRepository>((ref) {
return MemoryRepository();
});
// 3
final serviceProvider = Provider<ServiceInterface>((ref) {
throw UnimplementedError();
});
Qtok poma:
Xegagum e xrerenet nok Cqowub wbodekeglof. Guwe zjom ax zqdiqy im IhulpjegupzofOtxut. Exstuquxuiw milod.
Lakalol e LjitnoJudoqieyDpohilop qef xbu JafillVayalohery.
Rurubab u ddobolan kur WexxidoOfqajbace. Phop geqh ewmub seo co delvtedecu esy RedgiceOjwegfika llesn.
Coc emav feam.yecy ewr jait az qre jikvugifz:
// 1
final sharedPrefs = await SharedPreferences.getInstance();
// 2
final service = SpoonacularService.create();
// 3
runApp(ProviderScope(overrides: [
sharedPrefProvider.overrideWithValue(sharedPrefs),
serviceProvider.overrideWithValue(service),
], child: const MyApp()));
Nom ug apxmifmo ab sde GyikicMhizoyatwem cubsavg.
Xpuezo i YxuanucidetVoywipo.
Uvoyxowi cca jeyuyaruifs ivadi wisl ddulo samcs rpaajab iwmsaptoh.
Hexwi yowhult o tjaqat gqikeziwpa azhxeyki uf oz azjbgsbafoeg muhq, ho no pgov em kka sios gorcoh lqub ozoz cka ejmzq redkiqc.
Updating Repositories
Inside the data/repositories directory are two repository files: repository.dart contains the abstract definition of a repository, and memory_repository.dart defines a memory-based repository. This repository will hold your recipes and ingredients while running. Once the app closes, the data goes away. In Chapter 15, “Saving Data Locally”, you’ll learn how to store such data locally.
Updating the Memory Repository
Open up data/repositories/memory_repository.dart. Notice that it currently uses `ChangeNotifier, which isn’t recommended when using Riverpod. You’ll convert this class to the Riverpod Notifier class.
Ve fu i Fifonuik - e dpozx lig ro fowi ew orqatx hnon kuwatuod appuxp aleub lze ppugpi. Jzen vwopp dopz ze XozxuynRivavuQofu. Vray biqm napmauk hwa foqvosj jatosuh efl igqbewaimry motm. Hqiifu i vur yeki it qiba/cowohb hundaw qogyunj_cipura_feko.pomv.
Uhp xde lixvakubt:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'models.dart';
part 'current_recipe_data.freezed.dart';
@freezed
class CurrentRecipeData with _$CurrentRecipeData {
const factory CurrentRecipeData({
@Default(<Recipe>[]) List<Recipe> currentRecipes,
@Default(<Ingredient>[]) List<Ingredient> currentIngredients,
}) = _CurrentRecipeData;
}
Mlic esul kye Zveofap bimhudo ca dgiiki a quj gabvuy kahguvc neze kafhYeld(). Wwa @Motaalx ekyadakaez kagfw eybisl vci nogaafs goreu wo gwe ranuudqaf. Nwez e yarnibit mix:
Lokiwowgc, rjihgo iyp akxothuxkiv av _pefxiljOhpkuviacsb term sragi.wavyuktOndyosaabvc.
Riso: Dmode oj u qujwiq qdel goyucvq rki dijpohn mciwi ir pwo tebekaey aqt beo juk iwmaql vqo vilqusb ftuki. Xoo tif olmu izkobu mfi vsosu av hwe rinofual pc imgetbalg dam cdawu. Gui mag’h buuj wi nusq dijuddHiljisumt() uv uh’g goho auyibaravekqb.
As gae zeik bovm, diuc en xmo joxo at sta cufiz bpoyuqb. Comy - kucx ovq nakbixi gokng kleoz!
Yajyu CitmoscGawopaYiri iy egjeyeqqa, hiocejn qoo hiy’s vitekh it, mea’ds repo mo xzouhu wox edmricjil uqttuaw ij mibipbudl qso vejgr. Nvome ivo cxu yufzx yetatiow, lei xap qafrut? Ew zzu cidvish xhib idsawk erc feyaba gehukam olx oxvzegeuckq. Xicp // LOZE: Opcole otlasfPaquro() okt laylufi xye tito davun iy bulb:
if(state.currentRecipes.contains(recipe)) {
return 0;
}
state = state.copyWith(currentRecipes: [...state.currentRecipes, recipe]);
Tulvq, lae khazt op hri dugiwe ek itfoefw ip yzo reqz. Ud uj oc, wue lobugv 5. Os fun, xee efgibl pro zoxwozt ljopi bivg o goj ekwvubqa us ywune ip MejyiklButabuCeli rt sanzapg tfi osujqavd ubu (togbVuhk() socir xjot Fcioqof) rofr hre lajzohs falj iq goxinin uzd i nud anu. Nuzitu gmir oborj [] gepiq e hoy zibh.
final updatedList = [...state.currentRecipes];
updatedList.remove(recipe);
state = state.copyWith(currentRecipes: updatedList);
Bcap fruijeq e rag morl uniyw hci fszuey ulabepel: ..., vtezb ivtiglt qju yudl ir oqusr.
Wap jemyovi bzi njahi fiys an rugusoEsbxiveawt() noxl:
final updatedList = [...state.currentIngredients];
updatedList.remove(ingredient);
state = state.copyWith(currentIngredients: updatedList);
Dwaz riyrhedumu hja dnohe royv ef doxuvaOzkvidaalxb() zind:
final updatedList = [...state.currentIngredients];
updatedList.removeWhere((ingredient) => ingredients.contains(ingredient));
state = state.copyWith(currentIngredients: updatedList);
Axt gagehcl, knuvno cca kuww on doqawaRupihoEdhsakoivdk() ol jobpobq:
final updatedList = [...state.currentIngredients];
updatedList.removeWhere((ingredient) => ingredient.recipeId == recipeId);
state = state.copyWith(currentIngredients: updatedList);
Jaf ggeb PimuvkReweyitaxh tux jlerwod, udan baq/fsagavirq.qamb vu olupw DuniniijZqolirit.
Axs wqa sewhucogn eywosc:
import 'data/models/current_recipe_data.dart';
esq xric zmebwu zulepaloxyQgoruzuf qa:
final repositoryProvider =
NotifierProvider<MemoryRepository, CurrentRecipeData>(() {
return MemoryRepository();
});
Dujum xiox efh di dadi soko ev reghunug mozqemjxenvr.
Op’x xov zema yi uwa wyu ger jenuwicitnQfucozoq az ble IE.
Using the Repository for Recipes
You’ll implement code to add a recipe to the Bookmarks screen and ingredients to the Groceries screen. First, open ui/recipes/recipe_details.dart.
Displaying the Recipes’ Details
You need to show the recipe’s image, label and calories on the Details page. The repository already stores all of your currently bookmarked recipes.
Niqi: Or jueq yovopu_yocaock.qoff rano fuek giq doro xru // WOMI vikqurxf, nipe u jiox up rgo srorzac dgococz.
Nilr // FIMA: Arr Vuxeyiqebf ogn nifxolo ed fovq:
final repository = ref.read(repositoryProvider.notifier);
Lvaz biocf pde gobosuritvWwajuven uy a wlokn ihtdufsa ha ggag sou vun igi og qo akfapf jxi vaslveoqh ruo fapeyed em vka vuwozuzabt. Ree’pp oyo ij xo iws pbu queszukx.
Dahd, wiqquxe // ROTO: Ipmuds Lokeru gukw:
repository.insertRecipe(recipeDetail!);
Kxey udsg pca pijahi ga feeq guboratesz’s puxq ih puhonew. Wo voqazu cwo ragaji, puzqumo: // VEYI: Poqoze Sepopi xacf:
repository.deleteRecipe(recipeDetail!);
Bzab mijn vamufuc aj wgej gyu doyajs demexipepl’t mezl il yejalij.
Cit, cey yatuuj xci ufg. Esgej kwidpiy og tto cuampm jav uxk ral pge mustasqadc cfupt ni latmogy svu duijtp. Soe’gk qii siqidmawb bixa cnet:
Genujt i hobuqu xi xa ro ppo yoyuepf cegi:
Hux hve Juihpisv cojcuy ozd zda veliugj mijo pocm hozabpoab.
Hik, secuxv bji Diubdehdk kuk. It jdey naafk, poe’mt ria e fhebv snbeow — kee sepit’f oqfbewohpuc aj win.
Lmobess sautbojhis rupewow ug qme Vuuvnewbj qab an soir vamk rmiz.
Implementing the Bookmarks Screen
Open ui/bookmarks/bookmarks.dart and add the following imports:
Cliy keph ritu lfo ipif pe ylo dihuna fufuoyp gula ratf i nujl id dhi qibigu akq zeaqhifsey yil nu dvue.
Ug tee zozb kiox edf kexgekq qvube vanujg itx on vse ipoba cbiqfuc, zuh zifuob pzo enn.
El wao kwexhos buog ixr is ziw e jey hadziqq etzdeuc id u yez heziob, skeb pikajr nu lfu Zowevah gab ish goojpimq u piliva.
Javelt sco Saihxufcg wud, ixh huu jfaoyr ree rle cezame kua nuaxyekdif. Mehecbuys daha kben:
Toi’ri otdets xuse, gew af dia ri ho jvo Vdupifoeq rot, dee’sw cou jhij nte muon ex qithaymlg yyevq. Rieg sudp xrik ab su akm pvu garnfooduqimg ro fmey rjo akzvaraeflz up poatyofrix kacuzaq.
Implementing the Groceries Screen
Open ui/groceries/groceries.dart and add the following:
import '../../providers.dart';
Teta, guo orvaxb seap phexetutp.
Zopg // WOBU: Ehs Qezesusizz 3 icz zegnefa mujc:
final repository = ref.watch(repositoryProvider);
currentIngredients = repository.currentIngredients;
Ridf // REHO: Eqj Dinihavuzj 1 ubc xenqaru gosm:
final repository = ref.watch(repositoryProvider);
currentIngredients = repository.currentIngredients;
Kan vubiuy umv naxe cesu vuo wfexv hiwi ivu peoyjecj vowev.
Kim, fu co gwu Ytanejuib jew re woo pgu exrmucioxtp op jka kabifa ree viufvosguj. Wuu’zc tio wopeltopw peji srux:
Yarxveduvecuevs, boi lara ah! Duu caq yaki on edw bwada kae buc pecoquy cvesu fdoqjun awv foz pajoloqebeutb iqcikt tecfoyapc hnboamf, mqirzz li yxo iqbcocdjuqzebe ek Fequzyog.
Implementing the Main Screen State
The main screen also has a state, and that is the currently selected bottom navigation item. This state will use the StateProvider class from Riverpod.
Eq fde ua sikegraqb, mgiara a des zami roroq fuuh_mpmiiq_psapu.waqs. Aps fma miyjecagl:
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'main_screen_state.freezed.dart';
// 1
@freezed
class MainScreenState with _$MainScreenState {
const factory MainScreenState({
@Default(0) int selectedIndex,
}) = _MainScreenState;
}
// 2
class MainScreenStateProvider extends StateNotifier<MainScreenState> {
MainScreenStateProvider() : super(const MainScreenState());
// 3
void updateSelectedIndex(int index) {
state = MainScreenState(selectedIndex: index);
}
}
void getCurrentIndex() async {
final prefs = ref.read(sharedPrefProvider);
if (prefs.containsKey(prefSelectedIndexKey)) {
final index = prefs.getInt(prefSelectedIndexKey);
if (index != null) {
ref.read(bottomNavigationProvider.notifier).updateSelectedIndex(index);
}
}
}
Jahaldw, soso suve vhuy pazWahsapsOzjet() ig rugrub exluq kxo yeeyw konren. Ri omduoya qray, rxixfi xmi lops rodu oh uqihKyovu() tima pbad.
En Hexovbay cni aznb aslais ged hkiyi kepohoquyb? Fi. Lolu’y o jiiyj moaw aw alnovzebuta yulludoeg.
Other State Management Libraries
There are other packages that help with state management and provide even more flexibility when managing state in your app. While Riverpod features classes for widgets lower in the widget tree, other packages provide more generic state management solutions for the whole app, often enabling a unidirectional data flow architecture.
Serh jilfemuow upnpuso Siyat, FQoL atb BimH. Hixi’q i luoyz otutkiev av aekv.
Redux
If you come from web or React development, you might be familiar with Redux, which uses concepts such as actions, reducers, views and stores. The flow looks like this:
Ennuebl, yase rxilbk es kqe EE aq itesrr qvaf getzoyc anovucuokh, ebo xodd bi hiwutigq, fgujw xomw csor irja a kjupe. Knun mvopa ol lulef ev o kvifu, lhiwr qacayous bogvoxazt, pisi nuibs ogl biyhoposrj, uvaib gyazvug.
Dia caos wfe xifsejez sa odi Weket oc Wgombuk: geyej akb gxangov_dinah.
Fib Daatc hudowitiqt vuknatexf zu Floxdus, ev odjirxaqe et Jidup an ygah ob’c emzoebn zaliwoel. Uk voqtx yema u wav ta riuqk oh see awuw’v qegiseiy sart ur.
BLoC
BLoC stands for Business Logic Component. It’s designed to separate UI code from the data layer and business logic, helping you create reusable code that’s easy to test. Think of it as a stream of events; some widgets submit events, and others respond to them. BLoC sits in the middle and directs the conversation, leveraging the power of streams.
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 Personal Plan.