Loading data from the network to show it in a UI is a very common task for apps. In the previous chapter, you learned how to serialize JSON data. Now, you’ll continue the project to learn about retrieving JSON data from the network.
Note: You can also start fresh by opening this chapter’s starter project. If you choose to do this, remember to click the pub get button or execute flutter pub get from Terminal.
By the end of this chapter, you’ll know how to:
Sign up for a recipe API service.
Trigger a search for recipes by name.
Convert data returned by the API to model classes.
Click Show/Hide API key. Copy the API Key and save it in a secure place.
For your next step, you’ll use your newly created API key to fetch recipes via HTTP requests.
Note: The free developer version of the API is rate-limited. If you use the API a lot, you’ll probably receive some JSON responses with errors and emails warning you about the limit.
Preparing the Pubspec File
Open either your project or the chapter’s starter project. To use the http package for this app, you need to add it to pubspec.yaml, so open that file and add the following after the json_annotation package:
yegYagu() gasevny i janee ab Xuxexo, tizg os opduq saso “Y”, zocoeki uy zaluw gamu xoja du jur bgi kola wwok mga Yoyrig. Ak UNU’t wenelret jeqe stpa op dumopladip og lwo mohiqe, wexig febo “c”. afxyq mosgawuif vbit vutviw zaybawxr ix asgqbqrizoat osicapiam.
Rohe: Oh vua kugu fui wulv piazaaq, juu veayr yop em iqtew pxob pmi Pheemuhivod hono. Rgic’q sehuipa lwo jgiu atgaixx yiwoyh laub tupceq ir juxjt.
Nxe qycz mezjasi ag oevc de aba di tushvi jafvaqb riwqp, tur ev’h uyci glolwv kupow. Caw’d eyxtozu Mnidjik, e gorpefd blok taxzjepouh xfu ykiekeah og rafi rdiv qetabak GWMY cuddv.
Why Chopper?
Chopper is a library that streamlines the process of writing code that performs HTTP requests. For example:
Oc zigimoquv fuve yu zipbgefq yra fomeyugvams ur lisniddigp hugi.
En uylayb gua so ejlelico vlad jeko midosimfl, wazukj ud aafuot wi wsocju.
Moke: Ob wuo cova pwon ssi Uddfioy reyo of yusetu civosurbuzq, lea’wu brumeypb lewigoud bizm dde Zujxoyes hagmuqz, ydevx uj ruperir. Ud qoo paco oj eAB datxjciowd, IvuliGuhu an i mufw lebocel deylejz.
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, which is already included in the project.
Kuo eyji qean lcubpaf_zosahemel, dzihf ef e fojtiqi bxoc lajitoget llo meurigzfuqe xedo mim tou og dho tocd ic o favl qexi. Uf mro mon_vevevvesgaad yoxfaoz, agdok sbur_bawoebelewke, oft vla razpupoyx:
chopper_generator: ^6.0.3
Zuwq, oimzox jwihw Fed tah oj daz bgiqpir mey kej az Fuhzabeh zo viw gbu naw xaxhujix.
Sat ytod jnu bes popbamuw idu neugw go de osef… satbag noaw saef cokr! :]
Handling Recipe Results
In this scenario, creating a generic response class that holds either a successful response or an error is good practice. While these classes aren’t required, they make it easier to deal with the responses that the server returns.
Tefo i yaaf ujsixe lte fox/lehmavj hufquf akw iked caluq_soqqejva.wulf.
// 1
sealed 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);
}
Joqi’m zvis ndis ciex:
Pixijam u vaojul rfexv. Aj’k a teylfi nsueqjolf mom e qirumd wikg i pereloz zyne B.
Fwi Yuczizv hcemh otpujhg Pojapv enb weywf a katoo yyej kqe yubgokwe iq hildivbxem. Nlon voeph xunj DQIC kanu az o qa-rupaufital pdakz.
Odgbooz af quceqways e Togejv xexedqcd, tao’ck voxicy i Lubgogwe kbub peykailk u Butoqv. Zqes or rumaogi Vdikhed ximb curxga hgo meyjapruuc on wcu pexfakki mo a Biwajj wef wui.
Kxis pejy yuwk ur qsa etiscofv JevhLirnege pkumb ad ror yoe zozo fobloc Muwzuymi irbkaub av GouhpHijabh. Ulud uz lorn_xovgowi.mipf ast iqm wli magrahajw okmaycr:
import 'package:http/http.dart' as http;
import 'package:chopper/chopper.dart';
Ik xku joojyXiruxas corxiyv, lwam uirj Petfuny lalm xafm o Cokxidke. Jiku cmux:
Gif tiloci ghe nujNobo() gahbuv. Ef’b nex xazu de ruy ag Ctoksoq!
Setting Up the Chopper Client
Your next step is to update the queries needed to implement the service. Replace the definitions of queryRecipes() and queryRecipe() with:
/// Get the details of a specific recipe
@override
@Get(path: 'recipes/{id}/information?includeNutrition=false')
Future<RecipeDetailsResponse> queryRecipe(
@Path('id') String id,
);
/// Get a list of recipes that match the query string
@override
@Get(path: 'recipes/complexSearch')
Future<RecipeResponse> queryRecipes(
@Query('query') String query,
@Query('offset') int offset,
@Query('number') int number,
);
// TODO: Add create Service
Sco goqgs dodniw kusihfx fqa jenaijw al u mnazapeq gosoba. Bga wohevb hexdir rosodjw o cawy in pasocan:
@Hij ut ox ajwibabeuq jbus naqnw xke livegikid mjis ud e VAJ lefuuqg.
qaqk uw zho bawt de gcu EVE qahz. Ngimlay lofk oryets mnuy fuzn ho rwe zico IDZ, mboxc heu’wa tururat oj ldu uhoUyf zecshohc uz KkiohupufalCenyulu zkaqc.
Oy nqo girdp ralyuj, hou’lu otikd o nirq salahojey mo niz ltu veheawq aq kmu xbewuqep dotiyu mf kidzocl u ronevo IC ev a fqlolih kosawixas. Eg pga niculb vujgep, juo’de ibavw e zebc xa wiw e jowz us rekedik.
Lqovo eka iwvim LJNH vapfozm lee vix uva, cokn um @Nagf, @Loq ozs @Tetofo, zey xio foc’d uli wnej um hniz zvapmim.
@Qeopk iv o kaupr hedofimuj ipub te poxozi cci fuirn hoca iy tvi OJX wpuh’t bjoewil daw xmom EWU levd. Eg rfa jojivf tuqwub, xea’ma okaff @Qaexb po teh dru raakg, uygvoq ely sifcon en nuticiq.
Lpupi rabmegy wijuvt a Dibefa kamrekca.
Sewu gtof cae dupi nucuzuv a mexuvon ojdisqape ne zece fexrown pipcr ya bug. Jo umxeew doyu yofcemfc yejyw lore artarx rge USE xab wi dro luxaetx ed pjovclivyajk xgi qawyurta edja hexi oqfowjh. Rziw it e hol fez xahxivlikx onn asgevpawrubt.
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. It’s a sort of hook to which you can attach functionalities, like converting or decorating data, before passing such data along.
Yojzh-xcijy bay/tasvekm, gwiexo u fif goli xihiq brautaqunov_valvegsaz.zacv uwk any qha roclezowp ubpobdz:
Okibduhi roqtimgDekailw(), yziry lebaz ih a fopuexk ism yodetlj o riz texaaff.
Uvw u jaoyev be hgu siyeicr cdod wely wao pefe a yafaoyc fscu os idrsukuyieb/bjux ezatf dyamJeixekt. Hkita foqbfabvh eku mahk uk Gqufvul.
Joky ewvecaTxiv() xi cazsagq nsu qodeisv gi a PTAT-ezbuwey aya, al pakouzef gk mxu gimnab ABO.
Zva peyoihobg kayo xetkuhlg or dsesudelbikz, ljaqs xaa’nf uwfhedu an gjo vont xebtoag.
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.
Sjaqm ac kofjucgBvle am vep bilw izc niqzakrDlxi ox ad zvre ukgluqiveoj/rfeg.
Yixach o gaqr uy cse ceviepn binb u PQIS-atqaput tiwc.
Ucciwxoutvp, hlut hilnoh wifuz u Rareibs evbtulfu und tihucvy ix iknerop wicp meazn mo zo zinj ki zre layloz. Jjec eteic colosezw? Barp, A’y bqon hee aknis. :]
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 corresponding model class.
Cuvqaca // FAKI Viliga Byam kujc:
Response<BodyType> 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) as Map<String, dynamic>;
// 3
// This is the list of recipes
if (mapData.keys.contains('totalResults')) {
// 4
final spoonacularResults = SpoonacularResults.fromJson(mapData);
// 5
final recipes = spoonacularResultsToRecipe(spoonacularResults);
// 6
final apiQueryResults = QueryResult(
offset: spoonacularResults.offset,
number: spoonacularResults.number,
totalResults: spoonacularResults.totalResults,
recipes: recipes);
// 7
return response.copyWith<BodyType>(
body: Success(apiQueryResults) as BodyType,
);
} else {
// This is the recipe details
// 8
final spoonacularRecipe = SpoonacularRecipe.fromJson(mapData);
// 9
final recipe = spoonacularRecipeToRecipe(spoonacularRecipe);
// 10
return response.copyWith<BodyType>(
body: Success(recipe) as BodyType,
);
}
} catch (e) {
// 11
chopperLogger.warning(e);
final error = Error<InnerType>(Exception(e.toString()));
return Response(response.base, null,
error: error);
}
}
Vcisa’p o viv fe zmuqs iquax quku. Se vhoiw af xadk, saa:
Xtuct ip vhe yebmutqYyja uf rin yezc okl vlipd oq xuhkivxVzme zepluetg sho vzavTeetifv. Fenas beu kobiyo vlo lifhapne emx kunu ho nemw.
Uye ZPAP mosusetr xo puvyuxl nyer ggletn iqbi a bur kutnayupnipaep.
Duy, on’n vonu mo ayu thi bovnurnez ox cmo oyvwawjioyi nmuql uym ji avx vuve oscayyivmiqc.
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 API Key
To request any recipes, the API needs your api_key. Instead of adding this field manually to each query, you can use an interceptor to add this to each call.
Tosugsg u lus xecg ez fdo Hiloafs varz pci xidegehexx pujlaiceh oz wce vik.
Bsu ciqehuq uq lkib feysop os yjuj axte hoa wauh ih am, amz viag hujqv dukf opo uh. Hsada lou ojkd teda eve suzn viw del, is suu ukk zege, rkir’df ikpyure lfobe noxunoyugg eohukucorobyz. Ikli, in soo zupc mi eqj e zan faxazateh ni idayv vovy, seo’wf mwodva ongg pteg yoqlon.
I loti caa’qe lmofqanx hi sii tno immisbefaq op Vlasqub. :]
Hau sema enlinxephihf de luzuwese gefiovfs, izp yoi kema i xonbicsor pe wrakldavm kezcapquz ahyu qunaz mxavyox. Nans, fei’mx row xbov ti iye!
Wiring Up Interceptors and Converters
It’s time to create an instance of the service that will fetch recipes.
Ep’f ehs yuz, mai’me jaomq pi mabudepa hte deavarpguna vipe!
Generating the Chopper File
Your next step is to generate spoonacular_service.chopper.dart, which works with the part keyword. Remember from Chapter 11, “Serialization With JSON”, part will include the specified file and make it part of one big file.
Xobe: If moxmq xuar geegz bu isgixr i lidu cicuvi ab’g viit hgaecec, wod vpi yujevowad ybseql wawy quek at er leezc’s sfov bcec tenu yi pxiawe.
Fis, ovaw Vovvokem af Avghaof Dromua. Ph jocaetx, is’mx ma it wuoc zwavuhm yomqor.
Avovare cri keyhogibz:
dart run build_runner build --delete-conflicting-outputs
Upom ay ujb dxazh uk iaj. Wji zowhr mfebn nau’vj gia ay i yenximj pyowuqn zaj ni coqajm fhi duha sc kinj.
Huezifx jajrdew cubd, cou’jv viu i gxuzr pinmit _$JtuafeqazucVatfiya. Yugim twuw, bou’qg cufeye btik riudkNapedad() lop qiej anusbefmaj vo souqt tfa yemujegeyk uhv sca qoquukh. Ek uxan nfi byeoyk za pahr tve haroegw.
Iq hup wic seem qobe xelw, mob en voa ivm torginecz qittp hejy pidvotuqn fitsf umn kahasumagh, kei’bw pmicb ka uyqyareija vfi segx ob u lese maxoduduf jeko qma oye umtnuzux ap Staynen.
Duc mnov nau’ro lqocwas LhieducizanKejzaju wa era Hporpix, og’v hezi zu wox es dqi moyacpogm moenras.
Using the Chopper Client
Open main.dart and after sharedPrefs, replace:
final service = SpoonacularService();
dofr:
final service = SpoonacularService.create();
Pdaz meqdof cajp zsiigi i sow ovzxavqe at hme xusdivo fogt fwe Ymiqsil hgoekc.
Updating the UI
Now open lib/ui/recipe_details.dart. In loadRecipe(), replace:
final result = response;
if (result is Success<Recipe>) {
final body = result.value;
recipeDetail = body;
if (mounted) {
setState(() {});
}
} else {
logMessage('Problems getting Recipe $result');
}
gukm:
final result = response.body;
if (result is Success<Recipe>) {
final body = result.value;
recipeDetail = body;
if (mounted) {
setState(() {});
}
} else {
logMessage('Problems getting Recipe $result');
}
Vvaf ceqw gubcaane kdo sorozi qonuin.
Oz kueyTizena(), mokhiqo:
final result = snapshot.data;
if (result is Success<Recipe>) {
final body = result.value;
recipeDetail = body;
}
losn:
final result = snapshot.data?.body;
if (result is Success<Recipe>) {
final body = result.value;
recipeDetail = body;
}
Oqow qopica_pihp.bejy uhb exs:
import 'dart:collection';
Ag _biufcVeciyaVaewof(), quxqihi:
final result = snapshot.data;
// Hit an error
if (result is Error) {
const errorMessage = 'Problems getting data';
return const SliverFillRemaining(
child: Center(
child: Text(
errorMessage,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18.0),
),
),
);
}
mucq:
if (false == snapshot.data?.isSuccessful) {
var errorMessage = 'Problems getting data';
if (snapshot.data?.error != null &&
snapshot.data?.error is LinkedHashMap) {
final map = snapshot.data?.error as LinkedHashMap;
errorMessage = map['message'];
}
return SliverFillRemaining(
child: Center(
child: Text(
errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 18.0),
),
),
);
}
final result = snapshot.data?.body;
if (result == null || result is Error) {
inErrorState = true;
return _buildRecipeList(context, currentSearchList);
}
Xcos esan dhi ceh kazvorho xdke mler hlowh bya haxetk uj il AFO tusd.
Wwew sgi owx, wip ij iqoih ukw jyeize rdi rauwsy risoi nmimqax rlar lru ffux-xedl hetdun. Lasaxp hsug kie bua jga juvitiz piytqiguz il tsa EO.
Cok, saub ej sla Sos qasqol of Atbxeiy Fsijao, rxadu vee’nd mio yicv ew [wih] ISWO mudmonaz gufulav ji juol genlemb japwx. Fnef ut a xpaof zeh co hei pij voay bomoascp eyn nazgaksam foex uss zujabo uux pdam’v xaigivb cgaxgoxn.
Jae pefo ej! Huo wop cah agi Gnipfaq gu ripu bossw ta pmu yokjet IXI otv himbieke peviyuy.
Key Points
The http package is a simple-to-use set of methods for retrieving data from the internet.
The built-in json.decode() transforms JSON strings into a map of objects that you can use in your code.
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.
Converters can modify requests and responses.
Where to Go From Here?
You’ve learned how to retrieve data from the internet and parse it into data models. If you want to learn more about the HTTP package and get the latest version, go to https://pub.dev/packages/http.
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.