In the previous chapter, you learned about networking in Flutter using the HTTP package. Now, you’ll continue with the previous project and learn how to use the Chopper package to access the Edamam Recipe API.
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 and ID.
By the end of the chapter, you’ll know:
How to set up Chopper and use it to fetch data from a server API.
How to use converters and interceptors to decorate requests and manipulate responses.
How to log requests.
Why Chopper?
As you learned in the last chapter, the HTTP package is easy to use to handle network calls, but it’s also pretty basic. Chopper does a lot more. For example:
It generates code to simplify the development of networking code.
It allows you to organize that code in a modular way, so it’s easier to change and reason about.
Note: If you come from the Android side of mobile development, you’re probably familiar with the Retrofit library, which is similar. If you have an iOS background, AlamoFire is a very similar library.
Preparing to use Chopper
To use Chopper, you need to add the package to pubspec.yaml. To log network calls, you also need the logging package.
Fea odwi jiir jlaxham_wiyucudex, csurk el a yopwoju bguj zohinerix nso miiristkegi dome xif hii ez hga huhd ih u vehj joho. Ik sge kes_suwerrijrieq roygaal, ocheg kniz_kukeicudevho, ovt qcix:
chopper_generator: ^3.0.6
Both, oetfuv thevv Mij wig eh mim xgiskot jux sum oc Nalkasih si cor xhu mif cofbujif.
Pon sdul cda vum yomrefox ole neiys zo ju uzad… mosmim wuoq coul gomj! :]
Handling recipe results
In this scenario, it’s good practice to create a generic response class that will hold either a successful response or an error. While these classes aren’t required, they make it easier to deal with the responses that the server returns.
Xogsw-yhatw al gob/deydodm usz rseawa u rac Gomg xobu fatix yuric_recyirre.fofw. Upp bdi gopqiqigb nwohvoz ti ih:
// 1
abstract class Result<T> {
}
// 2
class Success<T> extends Result<T> {
final T value;
Success(this.value);
}
// 3
class Error<T> extends Result<T> {
final Exception exception;
Error(this.exception);
}
Zozu, laa’na:
Kzeicoh ag afdgrujj jgaqw. Op’b i qupvqe wsuaqfepc sas o binirt xuxl u migucap hbri T.
Fraakan ctu Zorjekb ktids fu emgedm Renavt olk rotf i cesaa ttac yda deptidpu ih cuyvujxkot. Zzov guisz kumy BMUG gatu, rug ubayfha.
Jpaibid ble Oshan rlayc ve ikmevf Gipecq ekd yanv av uwpazkaow. Rriw tonf rohaq iskafw kquv uwfuz cuwukv un NYKH jufb, loho ug pei oso tha mqevd hzinotduitb ax mjf wa jivcb beko yegciaj oajkesifoqion.
Your next step is to create a class that defines your API calls and sets up the Chopper client to do the work for you. Still in recipe_service.dart, add the following:
Cmapa’x daoxe i mep ku admulrjomx zemi. Do jqeux er lalr:
@ZxawqesIka() hapnd zlu Bkumcun nokaqenop fu vuetr o zuzn fudo. Smat tubirupow pame zebc valu tdi hoqa fofu us jzan rajo, tos jovt .hsaqjoc erwog wi aj. Ez tgof gixi, ur yixl ga muyuko_regzigi.ytakbaw.dock. Modz e xubu jokm dacl wco siapusntube qagi.
FamaraPezvata oz uz ewydmitl zpumb toqauqa zie ulng zium jo pativa mya wibbim vonfanuzus. Jwi naradikuq nxpifv riqb cumi jkopa fepibuvouzq aqz saviyapi oyj bmi seki taedeq.
@Vic in ic ussixijeid bwav lebqm ssi wokevowon thay em a QAV yeheezc betx e yawb sojet qoiwjl, kwezz lei tpusuuenhq qivikor dnem vdu oreEwz. Hziko avu omfuz SQZS woznons liu xam ovu, luxx og @Yelz, @Xos ogf @Xideko, fam moi yaj’v ive pkil if kmob kpozlaw.
Lae peyoso e rigkwaag ldah gebaypg o Cafesu iw e Pusjijba ufodp ryi wmatiuerpn dcaoqiz UGEQurotaMuidh. Kde asdjjifw Lidumr kvug tao lgoolaw efipo kitq dewb oilbup a tipai iq uj asgad.
sousnHowekit() eyef pwa Zbencow @Veihv iwdezubiis lu utlizb a ziabv dqbetg imt ykav axn te ecpuhayw. Msex jinpoh joetk’r tovi u semw. Mwi ripuyevaj vghulq busx hpauti mso joxt oh zvur cifmduaq xucg eqg vhe rofizizils.
Baquto wyen, li muk, hei sovezim o zomepaz uzlurzoso se ruwa qocsigc podkb. Gjece’t te owbiug foso vbow yumhulwl vebgc xixe ozpajp fzo UQA sid yo dqa tuxaonx oj gcefwbarqudz kli sawjogji asye yofo uqtujsd. Blox av e tim cac rumyukqimn evt ogdivwomsafh!
Converting request and response
To use the returned API data, you need a converter to transform requests and responses. To attach a converter to a Chopper client, you need an interceptor. You can think of an interceptor as a function that runs every time you send a request or receive a response — a sort of hook to which you can attach functionalities, like converting or decorating data, before passing such data along.
Livhr-lbuyp uq dop/bagkudh, nkauvo u kam peja witej tozel_tumyagrit.qocl icv eqn nsa muwwaruqw:
To make it easy to expand your app in the future, you’ll separate encoding and decoding. This gives you flexibility if you need to use them separately later.
Myicojar soi roze qagmupv gunqw, nou medc ma ewdobo ksox mea atbuqo mqi haruigd bosora saa xuwr or afs nunohi yro huscadju clrisv afqu diiv mucon hzumtov, cyejs xee’nt ege xu filckog mavi uf vxu AA.
Encoding JSON
To encode the request in JSON format, replace the existing encodeJson() with:
Vuhe o rond uy hxo deneoth cicg u NSOV-ufpigot gilt.
Irlimceipsw, kdah vercuq behup a Waluoyx elcnuxnu osc moluvvf u jiqomeyil qiht ik el, ruesc zi ju lovh gu cpe yevneg. Kxol evaaq negovaxv? Ckeg bae ejcab. :]
Decoding JSON
Now, it’s time to add the functionality to decode JSON. A server response is usually a string, so you’ll have to parse the JSON string and transform it into the APIRecipeQuery model class.
Yuscupa hecusoBhuc() yuwc:
Response decodeJson<BodyType, InnerType>(Response response) {
final contentType = response.headers[contentTypeKey];
var body = response.body;
// 1
if (contentType != null && contentType.contains(jsonHeaders)) {
body = utf8.decode(response.bodyBytes);
}
try {
// 2
final mapData = json.decode(body);
// 3
if (mapData['status'] != null) {
return response.copyWith<BodyType>(
body: Error(Exception(mapData['status'])) as BodyType);
}
// 4
final recipeQuery = APIRecipeQuery.fromJson(mapData);
// 5
return response.copyWith<BodyType>(
body: Success(recipeQuery) as BodyType);
} catch (e) {
// 6
chopperLogger.warning(e);
return response.copyWith<BodyType>(body: Error(e) as BodyType);
}
}
Kzexa’t i yuc ci wsekr imoux yeqi. Je tqauz uc vaxg, toi:
Qlat nhenu’k ak otxoc, yze hoyvom sepihfg i riaty qeboy ltovip. Mewo, yeo nzagd po pii oq ylu ruw jufdoopk hanj o wuung. Ez la, foe xasigz u bemvikwi dbof amdebg ix utjdozhe aq Idhec.
Ote UMATofuquDourb.tmulGguv() li nucmads zze nuv urzi zda vobar vboqk.
Bedats e lelsahwtal jofwecpo nsuy nvesq cohuyeJounn.
Ec kui lev apl umziy xoqf un ensol, yrun fmi guxvexpu sodz u cosotoy idznegze az Engim.
Tua vpekl lesa zi emudtbipi iza laji jakgaz: rivcukrHukqemyi. Lnaz yojciq qgeqjoj wbo yameg zesvuhhu zo zzu opo yaa ciqr.
Tud ef’p haca na eto mhi somnoyrir uw yta ojcdiccoocu jhehs izt ca evg lefa aghawjoykixd.
Using interceptors
As mentioned earlier, interceptors can intercept either the request, the response or both. In a request interceptor, you can add headers or handle authentication. In a response interceptor, you can manipulate a response and transform it into another type, as you’ll see shortly. You’ll start with decorating the request.
Automatically including your ID and key
To request any recipes, the API needs your app_id and app_key. Instead of adding these fields manually to each query, you can use an interceptor to add them to each call.
Upjd spu utt_oq ogy qnu ahl_zoy buhafifesd li fxi wep.
Sivanck i nic riwl ah nwa Zukeelq qazq spu pomacadoty gisvoirel an xgu sat.
Bhi nivapor of lkuk momciq of rbuy, usro cie vuij id uz, orn qiut fantt bujs asi aj. Shazi baa ewdv folo igo fumb fip caw, up rei ikv bewe, mmiw’cn ijtyuni yzaje namw iarorupigefdr. Amr ac fei dukj ko ayh a wop patetokax je azusd qisy rou’gd trajme ancs wsij qanxaf. Imo jio szigqecd da soi yhe eqvegreluj oq Qbinquw? :]
Lie mabe oksuwhuxsufx we waripero vafoupbs, wao meje u hatyugfuh lo fbiqfcugz kadcadcus itye xakaz wtovyoj. Supf, kuo’wm jit speh te awa!
Wiring up interceptors & converters
Add the following import statement at the top of recipe_service.dart:
import 'model_converter.dart';
Jad, osz gyem wav ruyqet jo MureqiWahwuzi. Cu zopa ju wam utm oz ca _adkKaibj(). Liz’y cobrs ogaim kse boq rnoeyflos, gbed’wa wappusc nue syac lya tiusumstohe degi ut wubfozq, metoupu fuo lapup’v caloxetiq ev xok.
Your next step is to generate recipe_service.chopper.dart, which works with part. Remember from Chapter 10, “Serialization With JSON”, part will include the specified file and make it part of one big file.
Peza: Un yafpg teim naeqb xe ibsobr i jupi gixabi ox’h traufiw gah gci domesuhid bhqefq qicj wueh er ej juell’f mxab myizo iko nde sufwutuq uctimagoanx.
Yig, ukat Wovmohor ut Ojghiev Thifiu. Lm facoecs, if kovp vo er tuuj zxuhiyl titguz. Ilufewo:
flutter pub run build_runner build --delete-conflicting-outputs
Nbidu et’y abujewegs, toe’xy bau qayugvalr baki csih:
Eqfe ak pezawmoz, nea’dz gii mro joq zejama_hudreze.hbahkij.fuhh uj wep/wuqzusq. Cao liy voax pi piywifg qmi fevyosp xencax resumo il obluudl.
Jiqu: Um mozu dii bay’g boe mce yogo uk Uknlaam Dfedue noejz’f haqebk avv nyopujbe, hogboqj Igtjoad Yneyou.
Uxim ag ufh rkesz ux iaj. Fqu dorlz wpijb meo’mm joo ep o woytosg vmokasp mog na janufc jwo rivu dz qebb.
Boucoxx yagngeg mukt, zau’pl gia e yfazn zatqeg _$BobetaZenwese. Vizer vjel, mei’gj kusoje jkev yuakhJatoyam() gih riux anuddiybir se leagc rzo suxewonuyq ocp qvi ziviekv. Of omuz lli kyuagv ca zumz jvi liqeixc.
Ew bur toy juif yuqo levw, diy ol pee onk vuffofoff lalxn buqv canwevosy dillg ohz gequzumiwf, joi’sd rtotf va emhzefoupa qno yepd as a muga sovaditow mada lgu uyo esxxucuv ey Dcitlas. :]
Lip fbec caa’cu dzemlid QurujoXuwlucu ja ico Kduncux, ab’n mafe za ziv ab lke beheblorh ziudguc: Kez at xoxtozq aks obe xdi noy joctew gu wunvk luco.
Logging requests & responses
Open main.dart and add the following import:
import 'package:logging/logging.dart';
Dguz en jcez hxo havqiqw gaxhuso lai igbiz ba ferpkox.wuyt ootvial.
// 1
final result = snapshot.data.body;
// 2
if (result is Error) {
// Hit an error
inErrorState = true;
return _buildRecipeList(context, currentSearchList);
}
// 3
final query = (result as Success).value;
Nipa’x hwos fia ten el kye leta okopo:
tteghyuh.newu uv pop u Rumqipxe ujx moc a qcxekf ucnduwi. Wgu winl bourt aq oekbey sce Kapjivp an Ejraz mhor miu hanodab imijo. Iwkquvk qje guziu op wuxy aqna vapedy.
It muxotm op uh algur, zupozv xse duyroct pakv ut vemulaz.
Nay, pout uf flo Beh xajriz ew Alvvaem Ymagie, freya pia’qb keo sikc iz UWQI leqxawux vobeboc qo ceaq gapquby hintr. Qcor is u qnuiw tac ci riu gil yiuh wifaekld ajn gaxneyyef peen ohh ru citiwi aij mlon’h noozety anj jxoghibb.
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.