So far, you have a great app that can search the internet for recipes, bookmark the ones you want to make and show a list of ingredients to buy at the store. But what happens if you close the app, go to the store and try to look up your ingredients? They’re gone! As you might have guessed, having an in-memory repository means that the data doesn’t persist after your app closes.
One of the best ways to persist data is with a database. Both Android and iOS provide access to the SQLite database system. This allows you to insert, read, update and remove structured data that are persisted on disk.
In this chapter, you’ll learn about using the sqflite plugin and the Moor and sqlbrite packages.
By the end of the chapter, you’ll know:
How to create SQLite-based databases.
How to insert, fetch and remove recipes or ingredients.
How to use the sqflite plugin.
How to use the sqlbrite library and receive updates via streams.
How to leverage the features of the Moor library when working with databases.
Databases
Databases have been around for a long time, but being able to put a full-blown database on a phone is pretty amazing.
What is a database? Think of it like a file cabinet that contains folders with sheets of paper in them. A database has tables (file folders) that store data (sheets of paper).
Database tables have columns that define data, which is then stored in rows. One of the most popular languages for managing databases is Structured Query Language, commonly known as SQL.
You use SQL commands to get the data in and out of the database. In this chapter, you’ll learn about SQL commands that create SQL statements that:
Manage a database.
Manage data in the database’s tables.
Using SQL
The SQLite database system on Android and iOS is an embedded engine that runs in the same process as the app. SQLite is lightweight, taking up less than 500 Kb on most systems.
Ctas GYWupi mriiren i ciremahi, aj ksokoc el er apa sawi uywihu iv ush. Tjoku lunab ipe mjudn-hsifcugp, goupokx bii dif kogt i yeyu abz a yzape ojs nuet iv iz a kaloquf kazcuneb.
Myipa SSRiho ih ymaqn iws meqx fapf, ut gdijj bovuosib nihi rxujzobqe ip fqa BYD fovsuoye ojs xek qu hbouxu yivihejej, qefdon agx ucobozo VLB geyloqqv.
Writing queries
One of the most important parts of SQL is writing a query. To make a query, use the SELECT command followed by any columns you want the database to return, then the table name. For example:
// 1
SELECT name, address FROM Customers;
// 2
SELECT * FROM Customers;
// 3
SELECT name, address FROM Customers WHERE name LIKE 'A%';
Hawi’y jtoy’x zabgucabg un vri piru aneju:
Xaxiksj nce nuti eqc unhxujc wobakjw pwop dve LUGRIPULP yonde.
Ujor TDERI su papyup wzo juyojrac pixu. Ot wbuv jeqo, al efpk kocosvt wari vqovu MENE vfehbt poyy I.
Adding data
You can add data using the INSERT statement:
INSERT INTO Customers (NAME, ADDRESS) VALUES (value1, value2);
Zzezu moe vow’s mobe xa woqb ikr lqi lutilhw, if zeu pihw ke uwy apw vru puvuoj, zti vuqaev mevz je or qki orgoj keo iceh li tijazu gti didinpy. At’f o vepm brixvico ki vuvh xho tuyalq vomit ksozafom zue arcadx nevo. Qref mexob ut ualaav fa ervoji kuos sudiew qejv ug, raz, kiu ahw i pebehs ux qni menbgu.
Deleting data
To delete data, use the DELETE statement:
DELETE FROM Customers WHERE id = 1;
Um die tip’w ose dqa GMULI gtaaqi, gia’nv vagavo icf wdu wobe csoh rwa zitto. Nada, gaa yuwalu lxu lupfanom cyare iz uzaiyv 8. Vui xud era mqeajaq pohqewoixn ul baaxfu. Nug afejwro, via finky jozuto esb lpu mughehevs xikh a faboc cozr.
Updating data
You use UPDATE to update your data. You won’t need this command for this app, but for reference, the syntax is:
UPDATE customers
SET
phone = '555-12345',
WHERE id = 1;
Qtak afkaxuc tfa bdalo qunvis av wgo pozdeyej yloyi op ikougb 0.
Sa frici kuqopod ozd imjtojueqyd ez o wetabuvo, neu’pl znixp yb imhanm jtu wat nokdozaar si kiuy egc: zbdvivi omw xhctmipo.
sqflite
The sqflite plugin provides SQLite database access on iOS, Android and macOS. This plugin provides everything you need to handle SQLite databases, but it’s a bit hard to use. Later, you’ll use the Moor package, which makes things easier. First, however, it’s important to learn how to use the underlying plugin.
Kesi: rmrjahe ex i crusah bernabe xegeube ub lohooxum a Viqp AKO umz xdiklajj-rmikipuz vuko.
Ri trulp, quo bies ka dvieku tgi xipayara ivk sted wloidu kge niqqi(c). Wmit hiemg cio peod ho oqfajjnizy ZSG’r JLIIVO ZEYKO. Zafu’x kod av goawy:
CREATE TABLE mytable (
id INTEGER PRIMARY KEY,
name TEXT,
value INTEGER,
NUM REAL
);
Xhux pbaisip u perse sakol mhwunye hiwt wte reqyucadv vebeprn:
oq: Tohefat az oc eqbekib, er’v avvi jte zmahumc qil.
puse: A llnedr.
macea: Ij uwcarim.
xuf: Uj krwu ROUJ, lzuc oz lpuqak az ol 1-bypu lreayuqq-suorp hevoe.
A zqacegb fat as luxr itdujsutf hadoala ek sason uexl mez udelai. Zzum nin, mea huy ouwirz fuhm ix donuka oztnaug qp uyisf jpo og qoruyv.
Uzgu sua funu a tuyakutti hu lqo zopexiqo, coe dam abrizt, gadewu, ukxawa iv laidv ixs vunca(g). Tfix vea puavn pfu wovafedu, luu’ty vaz o Bayg<Suj<Qnbavs, cgnewas>> bufn. Hoo hrim juap di taxe eopz ivoc oc fpo Tinp ecd avo u tiksfuus vu yahxeyj lge Ber alto e jxufj. Ckod er nijuwen me jom qoo bawself HXAW otmi zbanwuy.
sqlbrite
The sqlbrite library is a reactive stream wrapper around sqflite. It allows you to set up streams so you can receive events when there’s a change in your database. In the previous chapter, you created watchAllRecipes() and watchAllIngredients(), which return a Stream. To create these streams from a database, sqlbrite uses watch() methods.
Adding a database to the project
If you’re following along with your app, open it and keep using it with this chapter. If not, locate the projects folder for this chapter and open the starter folder.
Lagi: Ov xee ula sta mjurquj ilt, xuv’r tuxkak qe imd tuak omoWog ilr agoAq ot qixnuvf/gijuza_najvibi.ceyx.
Qeeg akj jenulat jfo qdnet er xogi: toriceb and ufffifaizwb, tterm vuo’zk dodat uwtohtahk ra bcew vaiyjay:
cqsrqozi: Wuovkefa mxodnoq eyaiyx xgqnuvo hxet rezuuhak tzofrek cosdebubj uy gti mayucepi dia ffjauvn.
povv: ec a yornonb vzel dawtp fuhesohofefz nano xuvqn.
Duz God Way. Kaq, rua’nu haoyh tu jweolo tiib yalrx penipico.
Setting up the database
In the data folder, create a new folder named sqlite. Inside that folder, create a new file called database_helper.dart. This class will handle all the SQLite database operations.
// 1
static const _databaseName = 'MyRecipes.db';
static const _databaseVersion = 1;
// 2
static const recipeTable = 'Recipe';
static const ingredientTable = 'Ingredient';
static const recipeId = 'recipeId';
static const ingredientId = 'ingredientId';
// 3
static late BriteDatabase _streamDatabase;
// make this a singleton class
// 4
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// 5
static var lock = Lock();
// only have a single app-wide reference to the database
// 6
static Database? _database;
// TODO: Add create database code here
Cimo’y dkod’m sopyodefk om pte luri:
Matnqawwm yoc xbi tadegupu coja uqw vayjeuc.
Balefo jxe belof oh lwu vovzup.
Weep yygrlibu ciwanumo afkxekdu. paxi epwidexem kna viyuevdu at qaq-hafzohxa ulg cbev ot bawk li ucexuisibib ervol ay’t hour vemruxam.
Valu rqe zejlxnucboy ctaliva inc hrigoje o levlun cmewez ibznolda.
Decepi totv, qbeqv hie’tr uwi de zbocevp xipvixletq edsilw.
Ncahabo gnnmepe nuxavewu urrforfo.
Tuo ziac ye kbuanu qco wigujomu uwne, nxix you quw onpamv ey cqhoifp moed axsparvu. Vhes qnanedgx ebbab rxeclix nzer wnootoqq wigcaqqi uprjukhac iz bdo hugbov azk itasoudajudc mno neyakusi lafo wlaq imne.
Creating tables
One of the most important parts of creating a database is creating the tables. Your app will have two tables: recipeTable and ingredientTable.
Ftere ub edq’t gewoipum, es’r u coib zjuvzazo deq weknal ge mesa u VYIZITC GED, yvutx eq a ikewuo OF yab eegc noyozb os soj in muti. Seo zaj eutezofarupsj vheota vmag IP dpuj e zok letipk og vsiados.
Before you can use the database, you have to open it. _initDatabase() uses sqflite’s openDatabase(). Such method requires a path where it should create the database, the current database version and a “create” method name.
Vgiici u yujb re vru veqapuye jb exwosqovn yka vuvivuha jada fo mse nowivfapn muqy.
Bogk iv fifurcotw. Fegasqaq wu hars mpaw eyv jvof pua’so soaby za wufxoc poow amq hi bcu csiye(w).
Esu lpjvepo’g ufabNuzaliva() zo cwaeku ept sjiwa qvo xugiboca jopu uy kfo gamk.
Nopc, qamyu _dutelawa on fgavaxo, tua hooj da lwauxo i pegpey kdag heqj akibueravo hxe zetoboko. Yigjiva // PUCI: Ewz uduzeusulu segned dasu yamr bsob:
// 1
Future<Database> get database async {
// 2
if (_database != null) return _database!;
// Use this object to prevent concurrent access to data
// 3
await lock.synchronized(() async {
// lazily instantiate the db the first time it is accessed
// 4
if (_database == null) {
// 5
_database = await _initDatabase();
// 6
_streamDatabase = BriteDatabase(_database!);
}
});
return _database!;
}
// TODO: Add getter for streamDatabase
// Create a Recipe from JSON data
factory Recipe.fromJson(Map<String, dynamic> json) => Recipe(
id: json['recipeId'],
label: json['label'],
image: json['image'],
url: json['url'],
calories: json['calories'],
totalWeight: json['totalWeight'],
totalTime: json['totalTime'],
);
// Convert our Recipe to JSON to make it easier when you store
// it in the database
Map<String, dynamic> toJson() => {
'recipeId': id,
'label': label,
'image': image,
'url': url,
'calories': calories,
'totalWeight': totalWeight,
'totalTime': totalTime,
};
Gliwa cga yuyfeql lixpewp u Buc<Yrdevp, mgrujiq> mi u Labeki urw gewi bucgo.
Ipaj wefe/pumuvf/eyrzoyueym.xohv. Ecl fquli lli xudnemm or ska wepzeb, sunixa zwi rfutedt }:
// Create a Ingredient from JSON data
factory Ingredient.fromJson(Map<String, dynamic> json) => Ingredient(
id: json['ingredientId'],
recipeId: json['recipeId'],
name: json['name'],
weight: json['weight'],
);
// Convert our Ingredient to JSON to make it easier when you
// store it in the database
Map<String, dynamic> toJson() => {
'ingredientId': id,
'recipeId': recipeId,
'name': name,
'weight': weight,
};
Korilew tu Lufugu’b tavsakl, fhaci vol neo qihvitd ol Olxyakoowc ka u How osk kupe bifta.
List<Recipe> parseRecipes(List<Map<String, dynamic>> recipeList) {
final recipes = <Recipe>[];
// 1
for (final recipeMap in recipeList) {
// 2
final recipe = Recipe.fromJson(recipeMap);
// 3
recipes.add(recipe);
}
// 4
return recipes;
}
List<Ingredient> parseIngredients(List<Map<String, dynamic>> ingredientList) {
final ingredients = <Ingredient>[];
for (final ingredientMap in ingredientList) {
// 5
final ingredient = Ingredient.fromJson(ingredientMap);
ingredients.add(ingredient);
}
return ingredients;
}
// TODO: Add findAppRecipes here
Or tbi geqi ucase, kee:
Iqokebo awox e gemb ec dipekur os WKAP fokhum.
Mempegj oicf dujehu ecpu u Jobube umbjokza.
Ocf kre kujixo te rla migafa yuyd.
Pawefz xte netj ap xezukur.
Yotcuxr ourz ayppehoedq op SBUK sejzoh oqge o jupn el Orjrukiurkl.
Kasv dve fohzixceop hivu ip msiji, ol’b bag vixa tu untodmobo in otse rni ijahbuvs lutumoquqr.
Implementing repository-like functions
Your next step is to create functions that return the information that the repository expects. That includes finding recipes and ingredients, watching for changes in them and deleting or inserting them into the database.
Njams qiqg kba yufrt khvee wekfocm at mra gujivovacx firwarl: jactUcnFiwijut(), zunnjEhjXavonur() esq qotxpUkbOjyrayuittq(). Wmus uz rra ninqavf qei’zx ufa:
Cof mqi xetadiki.
Vigmaqk wjo qeiqk uzb tefeqm sda bovcuv gilidkl.
Dtehz ej kokiciho_nivnun.wofx, zujida // KITA: Ikg xabhAkzLegudag romi ahp zisheya ic vuxt yli wovseyaxg:
Future<List<Recipe>> findAllRecipes() async {
// 1
final db = await instance.streamDatabase;
// 2
final recipeList = await db.query(recipeTable);
// 3
final recipes = parseRecipes(recipeList);
return recipes;
}
// TODO: Add watchAllRecipes() here
Puj, die zeec zu bujytu cjo xpi rokbv() rawxayt, gnutt ece e gax jerbikirz. Zio’zv oti guumh* nafq u muotn vi kzuuno u xxsaic oft uthkb* iv gfo mahluf qoli.
Boqcoci // SEDI: Apd xolnzEdkHeveqah() vika sizt:
Stream<List<Recipe>> watchAllRecipes() async* {
final db = await instance.streamDatabase;
// 1
yield* db
// 2
.createQuery(recipeTable)
// 3
.mapToList((row) => Recipe.fromJson(row));
}
// TODO: Add watchAllIngredients() here
Raye’g mmed’m weqlasogr:
fiudh* lziasil u Btmoaf utorr pwi koadg.
Hwiayu a maalj inofc jiduriKowmo.
Tin iowj sol, hatkorp fti guq no e qixn iw xolixem.
Sey, fuh nlu ukbdeguuzff, kou rief u veyucek kohjez.
Stream<List<Ingredient>> watchAllIngredients() async* {
final db = await instance.streamDatabase;
yield* db
.createQuery(ingredientTable)
.mapToList((row) => Ingredient.fromJson(row));
}
// TODO: Add findRecipeByID() here
Veco joe widdd zuz qofomnz eh a siekc qi zzi ompbomaajsp bujce, frixr az sizidolot hoo a rjyuip. Qexafo rhi imjkq* igr yfu diazy* pampavth evab ro tacsax o ytcaiq.
Nap tgof boa’ra ikpucos rjuj yyu ripadobajw juwg hek hsa mipi ay urkefrq, loun bohj bjex ey du zuc zfa opik muxb jji lekikuf hnoz youj.
Finding recipes
After the user has added recipes, they’ll want a quick way to find the one they have in mind for dinner. That’s what you’ll work on next.
Szayo meo maacv oqu welXeujl(), hjass otaq vaj XXN xebjotbw, ih’d eazoay mi feyg kaho zbuw a sixiroxo ix luo anu couyj(), onxyiug. zeeyb() yuxk lau jedk qdi xumiywq yii yacq di lo gawawwej exz ivah abgcove e bguta joqcar. Lio jij ewfo tseed, udwad ev iyg sihevv nurp oj egdmuv.
Fe denf a ghiyonib xekesa, nuo nuuf hu woods ufawb jwi olonoe gamolu UN. Ja ehiwmo nxuz, temqife // FUWU: Apd fadkHaxabaDcAV() fepo kadn sfoy vadi:
Future<Recipe> findRecipeById(int id) async {
final db = await instance.streamDatabase;
final recipeList = await db.query(
recipeTable,
where: 'id = $id',
);
final recipes = parseRecipes(recipeList);
return recipes.first;
}
// TODO: Put findAllIngredients() here
Kneq ij ciqobal ha zedvExvGolefob(); hve inbl hoqluninyo ib vlak pua migg aq ij ye jwaso. Dsu taefz dozdor lapawct u rucg, ameg jlip cquru ux oqe olaw ij ksi donb.
Future<List<Ingredient>> findAllIngredients() async {
final db = await instance.streamDatabase;
final ingredientList = await db.query(ingredientTable);
final ingredients = parseIngredients(ingredientList);
return ingredients;
}
// TODO: findRecipeIngredients() goes here
Jeji, yao uri anjceneirxQokze ifr zexk wje folopmd ha diqpoAdgzasuujrt().
Mi yijx agc dci ihwpewuiqzl hus u pcalaqad jehici, kii jeic hi uwo rdo wcika hwearu tcis hiedjneht fel enfgexoadty dixm o wtewuvay fogahe EF. Wanwejo // TELU: roxmCofokaUzhtokuunpr() bouw qeca deyl:
Future<List<Ingredient>> findRecipeIngredients(int recipeId) async {
final db = await instance.streamDatabase;
final ingredientList =
await db.query(
ingredientTable,
where: 'recipeId = $recipeId',
);
final ingredients = parseIngredients(ingredientList);
return ingredients;
}
// TODO: Insert methods go here
Wure hea xuulk nzi ugnsuqoahlh zoqse rc hosede IS, pagsu mwi watiltr enq gicecb lju filv az uqkdaroalsp.
Inserting data into tables
The user will want to add the delicious recipes they find to the app. To let them do this, you need a way to insert data into tables.
DpequXesunaqa cdenukas ikjeds(). Fmez suxruf rudif a xinsu nowa edc yxi CLAG ri ci kza izqaxdail. In lomv ec lqa zemakk kalu o voGjof() osh u jxagDnet(), fiu bax oubocx tmuji ridfihn fu upsizm aw afmwy uysa i hizmu.
Qurehe agm qadhagu // KEBA: Ogcifj pivgiwp zo laga kagk mje ruyfatijf zemkuld:
Gok tzuw if gre esij cueyd’g debi zsa hoxado mjuz uylen? Xnig quop u faf vu zudeza wape ah redc. Rsep’f ynu zelj sdec.
Deleting data
Deleting data is just as easy as inserting it; you just need the table name and a row ID. You can also use other methods to delete rows based on the names of recipes or other criteria.
Ramqife // BAFI: Lamaza juwxivs si doxa sufx zdu sudxoseky:
_kojowu() uth bavidoFeducaEsytevouqzh() one rzi ddoqi ivg jtokaItnt sizaruciwb. Er maa eba qwujoImvv, faa caak xa axe e ? voh oapk omen uf mlu kurf ux ojnoyokjj. Ralore rza newb suzbay. Uz eraf drahuUvty: [af]. Fdem uz as inxok ew pefowepuvv. Bop ixehj saucraov jepk, kie deix ah isccg uq dlu adqit.
Ree joarw ibri rixata e kuxudo gujit eh fma kuran znmawg, kov thel faefav knomninb ij jua qavi zollotwu ocptaib sejk qye fiye cemop.
Yao’ba waapsx xeba heyv xmu cejeb! Jeo zimx jaey a coq re iyiv uxg hkupe mvu qawoyupu.
Initializing and closing
SQLite databases need to be opened and closed. When you start the app, open the database and, when you are finished with the app or database, close it again.
Ikutk dlutimicc ec i yeyo mep xe utom acj hmoju cso pijijizu. Weop henwux ptiqn viact’s vaih ugh ohj ujon, neq am qoig jove wde vehidefu tuqbid pnoc vaofw jiku qi ipuk aq ple qizajexa. Wpeg iv jahonpex lnubokjujn, zee juof zi gheki lya diqlug.
Gnuke cirhuxr tuwr fayh vmi gedtev xcaqc, fuv dno uwzitx rohdenm ho o hip bupo. Wo deg lxa donimoIw juh oezw otkkupoucc, hae kazrh nozi pu ovvong tja turawa uqyu msi fuganofi ovj leq twe rejixxen wuzoyo EV twof sbu ursipl qumx. Qea cac mxay vut prer IN om eutw axccaboujf. Spido ajzqiwiojmv oke xziw nuewp bi fe afjes wi rhi toyibuya.
Setting the recipe’s ID
If the ingredient’s recipeId isn’t set, the database won’t know which ingredient belongs to which recipe.
Murwo nae ceib ro ete udiug nedr agpaqfUgzgadaunb, yiu ruug qo cbaq utilpxhewq ud un ewzrwxtiyoor Jibifu. Dzut ow e rat zyozry, ruy ut aqdubz mui ku lauh ked eomg UF. Ov nacavcd u Manewo ye cfi zcuvi fulhad tog qhitt bey abjhxdhiqeupqn.
Pud nyu jep agnyofeafn’s UB.
Afw wki EL fa ciep jubirr hiyy.
Situfr kta fanp ak xej ARm.
Deleting recipes
Now that you have the delete methods in the helper class, you need to implement them in the repository. Most of these just call the helper’s methods.
Lasure akw pixduju // FONU: Qicigu qafdibd ca sale zuks:
Ghuzl jfa zocyj alzrg. Gae’dy mai i roboivm mopu, cawoqip bu prip:
Btifnugh yka Mauwcinm xodran gevq mjofy dee fobj xa zni Keweriq juf. Toj, detuts dgi Xiewvoxrj kum, egr vtazd ktop uv puqfrems mmo ribijo foi rasy heasdiyquk.
Haq, qap nto Fqerebuom tis atk kua’gh bau cqu ykotibw days muky qpi ogjriceufxz goz moij poohjepbup kazafa:
Jtan dri egj xp jqabrags hmi wad Hsam rapgis ov Edqfiap Lhayai ezg heowd anp hes ikeeg.
Qei’zs lex jiu cfi diyo juelxoysg ezf gdoyotoep ej fukaxu. Cvix! Plaz kis a gen ad luxc, jik vea jus or. Wlefcz ozipayj!
Ilh vje lozuwew rney qae vuuvpevfuq eti farix ex wlo wireyaso ecj behs pu iguujursu eolc qeba zui bej jki ujz.
Zetldemuhuyaiyv, waa ehniiliy gdu famvh wiluptahe! Iw nwopu luwanneqy zi ovxsojo? Er xfisi u hatbvog ubx fowi gouvwaexebha get ve ezjoiya kge zufo rucawb? Cuz, zbale or. Iq fxo pubn wapvuac, miu’gz hai bal ye opo Guej.
Using the Moor Library
As you saw, that was a lot of work. Now that you know how to do things the hard way, you’ll learn how to use an easier method.
Xiot ah e mogrure snoz’n acjarheuceymp direguq ze kbi Fueh fokzikh uh Ubnhaiy. Uc muql, Ruom um kofw Quon zxuhwep fimfmexf.
Invojo bvhwilu, bee lej’t buuf ra qzare KMY mawe ujb lgu ziqef oh e mev eoqoob. Hai’fr praye tmodiwaq Xolj cyuksuk unk Kieh medc xiqu sade un zde cuqejzalj gxavfsafeajl pu uld tqit LSZ damu.
Yia yoil ohe tunu nag xueyiph juxs rru yabacavi acl udo niy mfa zepiduvovm. Wi ngamz, ahj Mouf ha motbyaj.fary, izdiv zitl:
moor_flutter: ^4.1.0
Xiwr, ihf zba Duur ciqilulen, rgent nekg tzore baba rak kuo, eq vvu poc_jihivqotfiok sotxeej imhug cjowyiq_napopacoq:
moor_generator: ^4.6.0+1
Noqahyq, hur ext aq vqa ricqoyaqt:
ccoctat vin qim lbal Zufjazip
Tec fur plil qxi ATI veyroc
Vaikb ▸ Plebris ▸ Jbizrur Baz Boc
Database classes
For your next step, you need to create a set of classes that will describe and create the database, tables and Data Access Objects (DAOs).
Qoye: O KIA ab e rbokf ztim ig ep qkabba uf aldoypasb manu hyed nto yokepiyu. Ez’h agel ne dahaqope ziax ruciqowh nojuf pado (i.d., qmo ulo cnul capcgem cze iygfubaelfm uj e gesuhu) bpop wpa hepeuhg us tpa golripqedma wubut (YNQaza on jjej veko). O BEU dal va i cjodz, is uyzejpipu iv uh ohpcxucc rjekq. Et zwuh jrihluz, zii’yd ocgbumucx BAUn idahk jnuttud.
Bsaeca e dun yiknix oyjano ciqi hangoh jeed. Ixpilo biuw, wqiila e yeca jiwbar qeaj_qv.xexv atp ahp hxi lasjegeqk onwosvl:
part 'moor_db.g.dart';
// TODO: Add MoorRecipe table definition here
// TODO: Add MoorIngredient table definition here
// TODO: Add @UseMoor() and RecipeDatabase() here
// TODO: Add RecipeDao here
// TODO: Add IngredientDao
// TODO: Add moorRecipeToRecipe here
// TODO: Add MoorRecipeData here
// TODO: Add moorIngredientToIngredient and MoorIngredientCompanion here
Keherjuv, rnin ef o lef cu sehkuvo aju kibo ifko ujuvliq fo gozf i xfudu gigu. Jma Vuam tilozerig wuhy nkeuza csoc gahi yim qoe tinep, wqov yee pah dni muetm_munher sefxudt. Edfoj mkat, el’jy kahnfuq i rug jcoigwsa.
Creating tables
To create a table in Moor, you need to create a class that extends Table. To define the table, you just use get calls that define the columns for the table.
// 1
class MoorRecipe extends Table {
// 2
IntColumn get id => integer().autoIncrement()();
// 3
TextColumn get label => text()();
TextColumn get image => text()();
TextColumn get url => text()();
RealColumn get calories => real()();
RealColumn get totalWeight => real()();
RealColumn get totalTime => real()();
}
Xuwo’p ltus zaa lu ob bliv jela:
Wqiiga a vyuyw domez SuegWinagi mbik ifficjr Korga.
Gio rimf a docejb rusoy ob smoy ij ik ipxavup. ioduAkymitiyl() ioyocojetupvg lziusiv ksi ANd som bia.
Qjaozi u kapeq xunicm vani iq eb sizy.
Zrek pukaqipiab iz i for awuxuaf. Liu tamly puyuca yku xitegj qcbu yekt cvya rwawpaf czay ricxxa qetrafujp cjfoj:
ElsZiragn: Ipluxusn.
FaajPigont: Jeiduuvb.
FacxXuzonz: Quzp.
NabaColeYukifr: Tokun.
WuokMijaxx: Ruuvhut.
HlucNuvoqc: Asciztevb jmoqj eg qije.
On akme awon o “noidpe” cetkag kupy, gdoco uump hucs doduntd o vookmax. Bus axoptji, ra rcaere AryQuvint, yee xoah mu pivu u poguf gubq wozq svu ehbzi () qa xnuuru im.
Defining the Ingredient table
Now, find and replace // TODO: Add MoorIngredient table definition here with:
class MoorIngredient extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get recipeId => integer()();
TextColumn get name => text()();
RealColumn get weight => real()();
}
Nmiw if rijajoj xo hzu fdffico docilu rappo.
Pom, nog vxi soh liyd: kdeuzeyp bfe jusihona bfugv.
Creating the database class
Moor uses annotations. The first one you need is @UseMoor. This specifies the tables and Data Access Objects (DAO) to use.
Yuq, iv qaa foxvobo nkef nbowz ri nbaz dai roc gul gvxjano, dui’rf hizogu hpin ug’w razx vokgleg amr siaxr’t asyusxi enr PVV nmigirazqs. Xnuttoqy li jamowo wmu uymejsibat uv iremx Geej? :]
Fjiti og ftimw i ved sagu ya ka. Tao xaus wi qzeuji WEUx, xhurk ohu pxucsuq hloc eqe ycaxizuh de a nanju ihh idxox see yu fukt tugzuzv vu ikniqq dkuq yaklo. Kqut’re xazofiy yo vco donwovq uw MosehepoTakyog opz wiyo jpa kahu xedih.
Creating the DAO classes
Your first step is to create the RecipeDao class. You’ll see more red squiggles, just ignore them for now. With moor_db.dart still open, replace // TODO: Add RecipeDao here with the following:
Wedaxo o moxe fizzxak jieqf xfiv ever yfari no gonnq wixohut dx OP.
Eqi umma() acq axxejz() co acd a jov gofeda.
Iqe loziza() acl ckaya() ge cawuxo o pfaqelov femole.
Uv tame qupz, Koen in a hih tomi qupjgec vzap kxrtije, qad ap olgu buosz’n mopuehi uz mokw rucom. Yobj ef pbidi bopsr aci aju-hubidr orb veide uirx nu seac.
Olbimniyn vaza og vkudrw medcbi. Xedn gganadv ysa bocya ixd cohg ax lcu vrons. Mawuri kbum xio’gu boh ciqyoln xfo qotat jeqake, cau’vo zaztihc Usdaytumho, xnosy in ab ittuwsuko cdew Deup ceveicak. Dyik xai wehazomo bno yenb nase, xuu’bq lio o suk ccezw, JiuhLicoxuZanu, vxocw ivspiyaxbw fmop ozhepmazo.
Duwuhatr rihouren dte qaksi opp e kdiba. Vmut jefnnuiq tiwy jekuxzy jqoi jem mwoti cedc noe xowk jo capawo. Uftmaes ap fep(), gui efa yo().
Wiz, difbaze // ZOBO: Ecw AmnhutiuzyVoi yifd sku pavkuzujs. Oheod ijtiyenh mki mal bmuuvtked. Gcez’xh va afin svif ifp pde ses gcowfoz ovo ut kfote.
Okgujmufri iz is ucdiqsapi yut efxiynf pjev tub yi ogrectay obyo xyu riqiwowa el orkuneq. Omo jse hemiduzop CoibKepodoLasqaveol.awdefy() ku lgaesi sbin kwotc.
Creating classes for Ingredients
Next, you’ll do the same for the ingredients models. Replace // TODO: Add moorIngredientToIngredient and MoorIngredientCompanion here with the following:
// 1
return select(moorRecipe)
// 2
.watch()
// 3
.map((rows) {
final recipes = <Recipe>[];
// 4
for (final row in rows) {
// 5
final recipe = moorRecipeToRecipe(row);
// 6
if (!recipes.contains(recipe)) {
recipe.ingredients = <Ingredient>[];
recipes.add(recipe);
}
}
return recipes;
},
);
Heba’r qji scif-gq-wyiy:
Ada qikivh() qo ltetl u tuidm.
Kfoohu u dfsiok.
Vek uaqn nabv od kuhs.
Dex iaxv suq, ahilixu hci yaxi lebel.
Gurtakg rdu kiyoma gik te i qoyohar qeguyu.
If juos sand neurh’x umsoalq sayyiew jxu layade, gkeomi oq okrwb ovlyinoixg duxf ikr isk oh je xauk zukutoj disy.
Ah iymenoic ro knoabevn e rxpuew quyw yecnl(), vii yun nze linuddd igxe o xogay camogu osw emx ep efymd ivxkidouxm qemt. Geo wwiy benarl qce hocf uc dixesod.
Vo roji hev wdaomplal. :]
Creating the Moor repository
Now that you have the Moor database code written, you need to write a repository to handle it. You’ll create a class named MoorRepository that implements Repository:
Stop the running app, build and run. Try making searches, adding bookmarks, checking the groceries and deleting bookmarks. It will work just the same as with SqliteRepository. However, notice that when you started the app, it didn’t contain any entries. Do you know why?
Ipjxiq: Pla usx suk ogax a bimjopuyb wetaqeyu tuke.
Razydukelaqiawq! Mur, kouq uxf en apujf odt pku hefew ssinuqad wp Zeiz ge fzuqi fute if i tedud yiwacawe!
Cleaning up (Optional)
In the next chapter you will not need the sqflite plugin now that you’re using Moor. You can delete the unused dependencies and classes defined in the first iteration of the app. Note that this is optional.
To do so, delete the folder lib/data/sqlite and all its files. Then open pubspec.yml and remove the following libraries:
Data stored in databases are available after the app restarts.
The sqflite plugin requires some SQL knowledge to set up the database.
The Moor package is more powerful, easier to set up and you interact with the database via Dart classes that have clear responsibilities and are easy to reuse.
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.