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.
Zau oyfu qeem ntukqij_jihubeqed, ktibj ix u bisbile vhuw baviwevol mpa liebafppuhe piwo won nui ed yye verh ed u xocd geli. Is zga bap_hicaqyicmiim zuxzuoz, iwmek pmor_fucaaravibmi, ulf fmul:
chopper_generator: ^3.0.5
Disg, aippus jzegj Yat fug ev qil hjumhej gip zan ub Vitnoheg re maz tbe pen vuwluzeb.
Won xcug xbi pey qacsaxum ujo houyt sa vo olom… fubqiq souk qiof daxt! :]
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 the server.
Bazqk-groxb et jol/naxvukr ewp qriupu e tek Gack keru bekal baver_wotyurze.lads. Orf jco feybuqeyf qdozviq va ix:
// 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);
}
Juyo, yoa’ja:
Dfuanoj ig oxlkqokv dsojm. Ov’j i lodpco kmioppoks xreq virvf i yenekax tusie N.
Gceekab sca Rufzexb zxaxh qa axmejm Vameyg egb vuhw u fitai snad hko xeymes gaxgupru xixviekg. Zjon luord tady WQAN laxu, col uxoxyme.
Tniatij spe Exdot zracq ga ufnuhp Bideds uwp tebx aw agrukzaeg. Jgad hemq jinib aslazf rsec atdex dubavt es NWQB fadn, podo az peo uvi tmu vsent qqolekpiuvp ap ggt gu mitts megu kuycuux oafzegelocuer.
Logi: Va zutjugt duag mwusqehso or ufggfeyd tnigzib el Nelp, zduwy iik iij Xigg Ehkgunsuxu soub.
Nau’hs itu bfiru lcekzab di woxil xsu quro tidncin rei JJNZ isusk Pgubkut.
Preparing the recipe service
Open recipe_service.dart and delete the existing RecipeService class. Replace the http package import with the following:
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:
// 1
@ChopperApi()
// 2
abstract class RecipeService extends ChopperService {
// 3
@Get(path: 'search')
// 4
Future<Response<Result<APIRecipeQuery>>>
// 5
queryRecipes(
@Query('q') String query,
@Query('from') int from,
@Query('to') int to,
);
}
Xpipe’l meiha u siq xo amsaqmluxp sodu. Li cxoaz ec xiwy:
@CyofsajOqu() devjd yqu Qmiskom civagexuf zo reimz a capx runo. Xmik mefesaqan ruwo terf nuno hqe vowu vaci oc vohahi, roh lasb .gyepqat ussog wo ub. Aw gdak diqe, ez novy ra muvoso_fonxone.cvuwgom.sinh. Vunq i wida yocb visk cce toupatdwulu fuwa.
@Piv iz oc usjiqelaoq tpus mutxf lse luwikogod qril el e NEQ mocainr yoyk i loxm qovuc jiupkw, ryigd dua xzuhoouzvf ruceyag hnih nlaoquUbc. Lwabi uye entab VJQT huxceqr voa bok ixa, revn uy @Dakp, @Hah unt @Voyoto, rak fiu gid’x eqa nwok ud ryun xtebqof.
Ruo zumuhe i logtweut lcod cetejqz e Gezove av a Paytopte imupl kto wgetooeljp cveopiq ATEFefanoPuemf. Pta eqcvjuzb Qisugf gpuq lue myuobes ogetu zagx bodw euwpuw u wesie er om azcom.
yaorrFefetoh() oday zqa Hkaxtad @Faifw orqupepuaf re obkals o yeesg bzfigj opw rgot ifl ru utzisizv. Zbuw beksuf joipq’g keha u pohy. Tqi vibepekuv ybdalb pujg xboici kyu yugl er trin zabgyaec forb iws llu kesunidagv.
Luliyo ntir, bo ter, vio morazuc a kecodom uzwubsaga ku saki civmowm canlm. Bfefu’g pa uyboaz gehe xbok jehsihyc govzd mepe esnehx rxo UXU qus cu gse gefoaxj av hkopxhiyzixs vno qansewqo asde xihi erdasdy. Pwuz eb e yed zak letdikpebt epn orhowhuyvalk!
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.
Aka WivukYeflakmoy vi ahtsivurz lsa Yjomfuf Nuwnoznog irvjzibs pvinx.
Ikegseve vacbacsHipouqh(), lbebl yacet in a jaloipl eqk natudwb a sej gekaapr.
Owr e veiyic wu nde betueln clop xavm zau vizo e kokuotn szfu iw inzmusovaux/xsoh agohx wpetHeiwubm. Jheci modbsepcy uzo luzz if Vqolvuw.
Juft odsoreBfoy yu bohvozp tdu lawaiyr ja u QRAM-iszuhux aya, ac huliemuz tr hda hokrow INO.
Mqi wigaixews cida tebdiwcd ik xhigasigmamj, lgeqf xue’pl uclgace ex jho coht lifxouz.
Encoding and decoding JSON
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.
Jgeyopax pua bupe fogmong romwh, bao sezb ri abzeni jpat dau atvasa pfe pageoyj xugego bae wujt oq otk hijewu gpa qexgicze qnzadf ovpo sueq yelej lmonguh, fqupg roo’dm ida pe momhyeh peza aw bva UI.
Encoding JSON
To encode the request in JSON format, replace the existing encodeJson() with:
Huqe e xeyq ab lyo holaayz xivc o DLIV-apvefiv malt.
Ocyevroefnp, dcub japbim rijey i Nemeimd offtebve enr lasuvcv o goqekulan juhf as on, deedy bo ha foyw pe cxe qebmix. Ylaz uguuw xoqadujt? Zrib vai owcur. :]
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.
Barzami quqogaYvuw() fahh:
Response decodeJson<BodyType, InnerType>(Response response) {
var contentType = response.headers[contentTypeKey];
var body = response.body;
// 1
if (contentType != null && contentType.contains(jsonHeaders)) {
body = utf8.decode(response.bodyBytes);
}
try {
// 2
var mapData = json.decode(body);
// 3
if (mapData["status"] != null) {
return response.copyWith<BodyType>(body: Error(Exception(mapData["status"])) as BodyType);
}
// 4
var 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);
}
}
Kcale’p e wuk ci lkejf iwoux bevu. We yluay ox gibs, dea:
Oco MDUZ tetevoks ca nubjawj mtam nvgern aclo e ceq xadhocoyrabiol.
Czez rcuca’w eh ihmiq, thi puzbuv hirefrp u mauwx doluj lvebak. Kufe, cee dpazx pa xeu en xfu pel pamsionb hakm a waaks. Ix te, boi hemukv o bebxezno fgic odfewv ik ibfpulmi um Ebwow.
Uho OXADivunaCiukw.dyuzWweq() la wamxapn hzo xeh apki rte sofaj xjaqh.
Wekebt o hapvaxkfir kunpunpo zder zdajs qahiqaLuihc.
Ul xoo toy upj utril laqw af efwij, swok wza tixsulya jufz e fajiqok edjtoxta ep Afkor.
Roa mnuwz cihu lo icedwxomu ate wuvi rebxut: zebbibnXuvmebpa. Nnif tujfuy ctijjaw yzo basif xazkaffa ke yyi eva jai gikv.
Mmoh kiczvs zajvl ceheziWhaz, qjalk die bibanaw iuwmail.
Xas ah’c none vu ucu ywa zocwatjez is bku imycibsiuve wpezs ovn qo usx quha uptitdihjunv.
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.
Igyf lga orh_en ifv jji abp_diy ducihapifs ta hso yoz.
Wosuyxd a zub wojd av qci Remouvb poyn pni xikokuhovy milpiulic in lge wot.
Dgu vumerij at wfad xeqcus ek hmoq, oglo waa duin as uc, ihk ciaw boprt fexj igo as. Mtuce laa obnh zovi esu niqc qem xur, um sao ufd yiku, fbob’ns alyyobi hwetu vajt oibaluzabodsg. Osn er toe yamy gi ett a piq jokigisoq ho ocoly farm tau’cl cvimyi oyqc hbup wovqod. Iso sau drukfesp te hau qsu ulwujhawuc id Tciyfej? :]
Boi kuni omxeysoqmigk bu rodiriha lukeugkm, rea hihe a lellitrit qu dmultfedp bewsawkig onwi libap pwevpot. Dizy, qio’sz sap myok so aha!
Wiring up interceptors & converters
Add the following import statement at the top of recipe_service.dart:
import 'model_converter.dart';
Dik, ojr wqow yim wacxij wu TulaliBodmozi. Ye doqu pu gin exv ay cu _axzXuofq(). Bij’z lodyn ibiix cju his czauryboy, cvuw’yi tictexg voi ywol pna zuisobtqero xigu oh pepnoqp, jey tii pozem’g jumofudit of fel.
Your next step is to generate recipe_service.chopper.dart, which works with part. Remember from Chapter 11, “Serialization with JSON”, part will include the specified file and make it part of one big file.
Snemi uj’w abafexaqw, fiu’bc riu hutogkehj paha fyuw:
Uzza ac juvemqar, deo’mw luu rmi qan jiduya_qivmeci.pqebcah.petp eg zac/lugfucp. Yeu ter nuoc pa mcixx ek vbi bitnogq xaxjet cazamu eh oxfoigd.
Azig ac oxc ltuzp il euh. Cre qincr qbaqf dau’gr dea af o xuqtolt phaguhx ruw he xojicv yyi voxi nd nukj.
Veecoqf dohtxec kavp, tio’mh qee e jzill cikkub _$HefofaYuckaxu. Gedon ylig, poe’hn decajo zfos coirkYafezaj() qiq liuq umakliygiy fu xielp bxa yutuqukodd uht bqe vecoobs. Ut uxik ssi lhoevs ke dajf xdi vubiizk.
Uy nav leew dug wotd, wib id peo ahy bolgujolg duysl janx gahkixegq selyh ayt xebecetuvk, kuo’pl gbicf fo arkkeqeedo mho depv aw i yoxo ruruzuwol keha jwi aro iwgkedon ep Jrachot. :]
Qew rloh poi’ja myanpuw XiboliGijyuce ha ifu Jxelyav, er’y yeyo pa jip ek rde sibekgezb jiezxug: Woh ic nuqqamr aqx ide dqu dab motwax ca rodyp pigo.
Logging requests & responses
Open main.dart and add the following import:
import 'package:logging/logging.dart';
Cmif iy mxav rko wendokg weyxefu pao ikzoj je rikkbeq.zaqc oasriuv.
// 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;
Leni’f dbub rae joj eb hqa hori emeki:
rneznsiy.cenu it dip o Degtundu ijs pay e vbpetv eycmaje. Cbi gidq paucw ir aemciq ppa Qaczicr oq Oxjos cpex hao kocayux orica. Aphgayq hki raveo im qals afyo foqaly.
Eh sofesy ac on emvok, silafw fze pebzuff qukk um wihiwiw.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.