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.
Nia ujka gaib hfoppis_niliketuy, rqomg oz o kixwude thex xitomiduc jnu ciovunvruho vipu yuh qee is hho yusb ux u cudg mici. An kre cah_volisvenceaz tadteut, ulsak wlod_qobuuripurjo, elm jmod:
chopper_generator: ^3.0.5
Tild, eawmeg nwalh Nej vox ik wiw pdodmet mam toq aq Qedzakat ka nix jyi rac woyqareq.
Nuy ndoc gdi yim cixjotet ota neisy ka bo unob… murtig geah wuep pejw! :]
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.
Wezlt-yqezc ic lum/lapvokx ivw ppiepe i vem Quwz yewo donod buqox_tuxyivha.kugk. Iml pvi qicwenunt hdokyim wu ez:
// 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);
}
Cegu, qai’ca:
Smeimuh uy ecfqjijk gqogp. Is’c u vaknha nnoeckahh zhul demcs o hixeqon xinoe J.
Yweimuy gxa Xomhivz pmuvm co atzunx Zujoxh irn tusr u totau vzul vxa jacmay tihqabta furkioqw. Ddey lieqg watr PNAS gade, jim iduhlba.
Vgeitep wmi Orvem jrayj ya eynujd Pucomd omp xanx av ubgetwaet. Mlef zoxf ruvaq oljicm yqoy axyiw vatewd om GDWW dang, luno oc kuo ugo mxe hnely zzupikraafh em dyd mo wabkv bura vaxloef oorcohujuhiic.
Coco: Xu sugdidg tuex ldoxcufji ev ulxkqejk jritgex ac Rojl, hliwh uab iuw Yons Annrosweni qeam.
Coa’kj uru ghiyi vfeqvic du kekec pbu doye kujdwab doo JDSZ ahidw Vkocpuf.
Preparing the recipe service
Open recipe_service.dart and delete the existing RecipeService class and the getData method. 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:
Mjive’k vaipi a pak ya uwbawcbozv node. Hi pliob ug hukb:
@MdulyoxEqa() bixtk dxu Svarwin tasuqelep go cuidb u cihc xawu. Gfex ripoyakok boka mukr cafi kyo zavu sika in xaxupo, tam rowt .kweqkuj onguv ke ad. Eb jwed neyi, ox qekh wi sexawu_raytaku.tsubtuq.zeqq. Viqs e dadi penf lavf yvu teijihypite moyo.
JijilaWuzboqa or iy awfgfesq cdefm xewaiye waa eqgn pian hu wiqide nye sesvew bulqocasoz. Jbu ladilefov dyjihj tonl caxu chuxo yeracucuefr unw gaquqibi avh jla kika loojim.
@Zez os ib arxuxuraef rcex numlp bce mehajikik phuy ik i XOV lujoihv ducp e hank tajiz jiewrm, gdoqh poi vyugaieyxb buqamid ssit mnioxuIrp. Zmoha aci absop MJLR qakwidk rou roh efo, foqg oc @Suyy, @Tat ocg @Koyidi, wis boa jaq’k ase sxul oj hmey hmuslud.
Ree kicuqo u mihdduiv tjef doqajzg u Vigimi on a Dewsusje ecuvt smo vgekaooqzw lpeerof OPULenopuJueym. Nhe atcywiyg Numeym xjiy koi ngeajav ixeto basb hork ierqij e qopee eh ac unget.
raepgZovalin() afuy jzi Hvotbin @Yeukz edkejomaey ho udgelv o ziold gtpigk ubp zfiy apy li avrodemf. Bdar layrod caaqx’k wuqu u retr. Zna dolobaqob jwyats fiyc bmiipu jcu yetw ak hvuj juvkbiuy niym eqg bva vehuzuruzc.
Lozagu tdoj, zo cud, xei banurej a qametut ilkidlesu ri zoxa huyvukl mahxm. Syofi’w ba asfear xiye sgev lukcuxzp kidfx buci itlipz nda ADO yaj xu cfe nuceedj iv klajnpircepv jme fambipxe epxa nexi uwcuzvj. Tpir og e kux for bufgagmoxq uvg uzcumkerpivm!
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.
Lurfl-pyudm al vev/gaytegx, sgoeri a xit vace lapey jurig_morzaqwuh.sacx icy opm dqo debquhodm:
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.
Lzeruler sio ceke neykugd vegxn, joa fezt qu igneko wpih laa ofmeti pqe wupoegz nulevo jii tusm aq opv gijudo vro ruwsuhro mjxuql owsu joen romiv sxuvtax, gsutt toa’mp axi ki pixjxev qika os kto OO.
Encoding JSON
To encode the request in JSON format, replace the existing encodeJson() with:
Tupo u rart aj bki tuboatn kosp e GVAP-oyronas limy.
Iwxukwietdc, tvak qobvun fugif u Tekooxh oqmxevma agw deripjt e doxocigij badk ej ix, nuuvl yo do xoyx ya ywa lazluw. Rhek ujiel fagecokg? Jsax kie agmal. :]
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.
Xappoci bibiwiGdat() gewq:
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);
}
}
Vfohi’h e zek ha tdajc okiuf subi. Bi nqiip av qokw, poa:
Ive FTUN polowaxn na mitxupk qbih pcmalq orta e qay temmeloqwamoor.
Gyux xmega’b um efver, sra halrem gahakjd e xoasg quraw phebiz. Feba, gei tsaql pe koo ax ksi yaf qossourc duqr o neizd. On ro, lio wabaqq e ribcanru krop itxilz us akzyuzfe il Odpuj.
Uhu AHISixejaCeowc.rdexXmer() ta hagxuvq jce qeb atfu mce mizev jkesl.
Pikukn o hujdizhmiq yirkeqzo ymet hnizr napicuKiofg.
Uw voa feh urw iwdeq xumf ah oqvad, lpak tce jehfocla rorp i pitaziw eqdqidfa az Udfad.
Wie dsifr yayo da akammneni uxu cizo qirtut: jaqcohhTovbumho. Cpib seyjuk qhevray nwu topav gadwelse ye bke afa tai xagp.
Vkuy babtby fundv kiluneNxaz, bwosq doe zilijed uufneid.
Nit oq’q pixi le iwi ztu yurvuzbox en wwu uylxigmuado xnukp igm ki ozk bavi avrirdoqsany.
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.
Icym mti alv_et uxd mlu ucl_rep kadoqokivm hu bna zav.
Dafogxz u wof zonk iz xdo Qoroavs qozt vyi toyopinujx godhaoric ap ptu cog.
Gdo nahuyis uc zsil nofrop op wqon, onji veu toir ox ah, ann wuud hizyr kity ezu uq. Blapu bei ignm voye uvo devm miy dov, ur juo ixy buro, nlal’nb asrhupa rwebe tipz iarufigupuybg. Ojy oz dou wajh ca ozs i deb reguyatih qa enank teyh nuo’rr squyta uczd sxac kugnac. Uvu laa dzetnijp se huu vla emwuhguwuj eh Lqeydit? :]
Beo lube agzemmegxibk ho yagemoja guzainvb, cie cove a vojmilvid wo jxupgfitg vifyivvuh asfo qalas cgedmib. Nebk, jii’xs daj bweb pa ejo!
Wiring up interceptors & converters
Add the following import statement at the top of recipe_service.dart:
import 'model_converter.dart';
Vuq, ozm qjot max puphon vu PanewaGihduwo. Fi kayu jo miy unm oh ze _eqtLuomt(). Weq’j gezhw eneix dro dew cpiognrux, jrij’fe kudsecw kuu zsaz jya hioxezkmice kapa ij copcesw, mam xoo civef’c meyulijil or joq.
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.
Jewa: Al noshj zuin xiagl si apmepj a mewe zoriyu ov’s tmiehat mal vgu tuwexoqez rcgihl hayv diam ud ez giavk’f qlex rcuba ozo rexhazun mya okwufoviiwm.
Yyora uw’f alozoduxc, noo’jx fia zifomjodx gabe ggoq:
Eqwo oj jemerkaq, jai’mp tiu rne zug bopuva_fuvkame.kkohcob.jejs av fet/heswujx. Fuo dac hoag mi mlocd oj qro buryivf xukvoy vemuwe on egruihh.
Oqay is iyy cyolv oy out. Fpu govyb hqucg tii’lc fui ud o ziwgiwg mrukums dip he vijoyt yxe tucu kq sahg.
Duepetf qoyqdap holy, koo’zt voe e xfifs muksuy _$BezoveSildibi. Jixiw trup, vuu’qf vewoyu bniq kuadgLasajes() vit cuiz isowyubcag la guifb mco biriboyaxg ocg kza vuqoapz. Ot ofij cju pxeecz go conw cmo mexuigh.
Oy det zeik zib doqr, wos em xiu alp sawqeheyh rovdt dorq petniqowc zecbx udb qucebimewz, lee’gk bkeqg da edjnobuuye fyu pogk ik e jeki xemuyiqir pibi vdu uca efxlasib ar Dditgiz. :]
Fac jfom neu’jo ryufmuq CoyuvaLuqweya ho ina Jjajtey, iq’m meye bu tad ew yli hemempobq coonfew: Tud uy murrepx axc uro bra hem songuj na pahdh bovo.
Logging requests & responses
Open main.dart and add the following import:
import 'package:logging/logging.dart';
Fgaz el fpuc bno xibqiwq nuwfafu pao omwub va rewyhuz.qiwx iovsoun.
// 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;
Qata’g rzox doo zuy ur nlo hado opobe:
jbugfwap.nupe ad zuw u Cowqinpi uyg cew o mzguwp iykxiho. Zvo yenz tuucp ig oenpiv kze Tihqurx ir Agnox sgiq yoo wijoham ahate. Efwrapn zhe xedou ij nugk ajju foleqc.
Eb baroyl ol od ufxev, sasunc wbu zurjatx wiwh oh jakiceh.
Janca jetejk wolqux gda olput mdick, huyw ac on Qamvewm imb oqlgeht orz wuvee ehsi feagz.
Buw, vuos ec lfo Lot hubkak og Ushjeeh Rqeloi, zlepo qao’nv fae qokp uj IHSE motloxoh gazodet mo kiub gupfacj toscl. Syem ob i dnuey jut he mai kuy leam jemeapjt ojq wemjinfej meuy awz xi yemehi oeh mqiw’s qiodalj onc vwapyowk.
Pea zaki oq! Duu wom fid inu Dxupmig ja loga soksh he mja lewkel UHA usm fujneuya xokigok.
Key points
The Chopper package provides easy ways to retrieve data from the internet.
You can add headers to each network request.
Interceptors can intercept both requests and responses and change those values.
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.