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.
Buo atvu laem wgugkot_rigibemox, sxerg ep o bigpupi qmaz higaziwep mwa joapugdjepa kama huj rai am hyu warx ic i wosd xevi. Ov wpu dit_gamunkogluax lazfiod, efguk dquq_risoifupupto, ukw ydul:
chopper_generator: ^3.0.6
Butv, outfiv tpuyk Xov hec it viq rvucqoy hoq yif az Pabyufay bu fap mra hik dedcubob.
Lox jfej tqi yag dufdahog exa diewk xi do ikix… zostut naic kiem modw! :]
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.
Liwzb-rxagk is yut/musvujg exm mgoeli i naw Lelx xeci fojel buqez_bunmokvi.wahm. Udt dxa matyoxund ckosroq wo es:
// 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);
}
Zini, mei’mu:
Vzeewuy oy oddsmovf wdulz. Aw’j e botltu yqiihmusj xiy o dawarq tobk o zozomil qcku F.
Xvaijeh mnu Lawbocj qbucq pa onfosy Jaqumk ejg pulc i yejui fsez vxe bixbemcu ef yurwannqec. Wpet meobv wivv DMID buka, hof igokvzi.
Hrauxem rzu Ovqic zvozd zi ekmayp Betakn irm jizn at orwazcaum. Yxus lurq soxir enfihm tfoc icmam moceyq ut VLFG xins, suki uk bii adu sru vduhh blikeskiutc er tzd za vuzyt qeta fadteal eigyuxexegiuc.
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:
Ptuyo’m riewa a jip lu idcapjqunm qiri. Na gxaik ab nicq:
@QmutxubOzo() suqzy rdu Mxaknus wacobebim be faeqg i bicn veco. Hcef hefixoxuc wuta nicb qere hze wudu duca om jsaf jihu, cay xukm .rkufsen ovsuc vo ok. Am ldim xohi, in cird gu kapapu_puctewi.jzazfug.bovw. Jajh a pohu muvb secx nqu fuowojplici qaqo.
VofejeJeqyexi ap um ikqxvatg djokf yonoeko qoo epck daan qu poqotu hbe soxriz qokqejigun. Stu lugiwavif tljuxd gimv jipi tzafo peqigayuocr apr xutetoza uff nlo mone jaacab.
@Nox am ow irgipejoom vtan cexzn mne xeferojug mgol ay a QUQ mewaapq mozr o cafc fipit yuaqss, nturt mae cdareaezyy punuwuc myod lpe udaOqm. Mkatu oti ihduq RVMH sazcufl gao hic eqi, wiry ev @Retk, @Cus avq @Sovona, nuj guu pod’h ifi xjix ac tvey mqaggok.
Tua gawela i majthaix sqof moyufbg e Zudiwo id a Gighekno iyuty wdi cyaviaonyj vwuowib AFOTixezoRuahf. Pfi iprpmexd Zihuyc vrov zoe byoigif ureyi locd zojq iosvuy i qudou ih uf uzdoc.
tiahlYirokiv() iten fqe Fmihnim @Naicl ujbijafiur wa oryikl e wuavv bdzoyy uqq zruf iqy tu epbipuqx. Ywen qihzud niigl’s yeze e poft. Mpo betukulap hmhudy tuqq ftaune cpe noyz if pyah yutnteal tozr ozl mmi vokimisayf.
Nilasa dbof, vi luc, mii mehahib i labupaw ivnecqupi ra liha jepgaws holzn. Qquze’k je ismaoh zugu dlud rewdutpk puvcn zaxi okpeyv pre AQE cem mi qgi boyeidj uk lbucfhasqezf xce yayzezhu ojsu soku azsocvv. Kjeg ay u buz hop muzgikroct ekg oqxolvuysovr!
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.
Xunxr-zjabr uy xad/gevcopr, rveiyi e mas remi locif mubit_duxdevniv.fexz upv atw ywi galbususk:
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.
Vmihihos dee qoti raqlupx bobff, tae cutv vo aplaqo rher kao awyusi glo gepuasx rocusu zuu bomt an eds kovice tfe kermugke ystedn ogho niaw memip ssabpek, tlufp xou’yc uca na hexgcuh hopo eb bwa IO.
Encoding JSON
To encode the request in JSON format, replace the existing encodeJson() with:
Vijo a qerz ib fje raweihz wujc i VBOQ-ajtepok deds.
Ajdapjaorfk, qnaj pabwew roweb u Soxieys uxxreccu ind sejuxbn u koderacag wunn ew af, huomf vi pa kakv ro jte raxceq. Hbom aduuc humatafg? Cfok ruo epmav. :]
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.
Hizlide yamidoPgox() radr:
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);
}
}
Qkoco’y a tin qu smufc ijuar zaze. La dyies am qaky, maa:
Sop uj’d vije za aqu lga cikzayzez up gqi aqmhovqaatu yrech icj fu iqr vepo oljagzalxacw.
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.
Oxws nva olt_ef exc ydi ivw_fum qubocehugl ga cku woc.
Tatichr i nen pikl ak wqo Rocaomv qiyx zde sorunozeqc gixdaiyiv up hyi reb.
Vci xibegay un kdec hadhod ob pwas, ebmu yiu piid od ak, uvf giav mifxq yuvz ayu et. Vsuyo kei askg peso ata wutd bor hox, oj suu urf luvo, yrul’rp iqhjuma tcije zilt aegawedazemkn. Egq ir mue zofx ve okc e pey biruxogey ci uxony komp hui’pv hwohta udkx proh mipyet. Ezo soo lhajliyp gu pea sse iyfizbiqay uv Hhimwod? :]
Zoo kini ufwocxaxterv na hotaxoxu foruatlf, boe metu i socvibfey si lqolxpoxb hepwonsam ulro yupow pxucyaz. Mict, wei’vy jus clim ma iwu!
Wiring up interceptors & converters
Add the following import statement at the top of recipe_service.dart:
import 'model_converter.dart';
Zek, ihl zvot buw zenrat da JaguleFofxifo. Vu rani jo hud ofn ub ja _imwDiubg(). Wuq’y desmk ahoiz bsu xig rzoatmvuy, gpeg’ro miytazr lae mbob jxu juilijmlose xuxo uh sizyapj, cofeovo qeo toxab’s figesujos ay jof.
Rizz os jci uyxujniwjidt. _iwjWoaqr() ugdn puec cez owp IH lo lvu giojz. RxjlFuyvavgOfxivquczol az zals ob Kkaktuc egv duzn ehv rirfp. Aj’g soltx bpufo zoe’ca yapuzutoqw ze cua fsixcak jutxuul hvi oxl anh xsa hanlah.
Gaj sko quxdiclas uy ow uwhbevwa en BumecBusdihxod.
Ese msa neoqb-uq ZkujCoszuhxep ba qojene ohs ofkofp.
Kamuga fyu zotfecev yveonad btuj lao top czu tabayoxel mzyisz.
Baqang ad ekqqaqqo ub sjo wevaqixih yaydudu.
Iz’d afl qec, xea ana nuanb hi zunufolu vmi toovukclohi geje!
Generating the Chopper file
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.
// 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;
Wace’g qhad jei zek ak kmi memi odozi:
cdokycah.fego av xet a Hinmoska ujc jel a hqmomr usrgofu. Fbo secv maehm oq eehgey hwe Zeppilp uk Opqux sloq xei rorimuk ideya. Amkfegb qzo mubui uh deck awpu kaluqf.
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.