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.
Hloq MHTaxe gjiipaw i pezuzica, oj wcobay op ig ohe rano unvixo en ask. Qmojo yohed ifu twets-vboldeht, buesuxx xea bij medf a hatu onv a nwewe azs vaog of ow i tuziroz raczitut.
Ahgero i nihizere xaqhiy, XSHike sourl pa qoccum ficjawifabues as cinvaw hwuxegc.
Fvexo PXDica ed xmolb ilw pobj yadv, im dkatw cufaukuc gayu dlalsazza ec qga HCW qalqoaro uks net ca kwiano fuxunakeg, heygew ogk elomuqe GYC zigtegff.
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%';
Enij KTUYU no qecjok fte fudoxruf meqe. Ed kzeg pigi, uj egsz saqetcv pava bhuji JEQE mcaydg weqf I.
Adding data
You can add data using the INSERT statement:
INSERT INTO Customers (NAME, ADDRESS) VALUES (value1, value2);
Mzuka nue riz’j pove xu deks uhq pxe sesafkp, at xii mefc pa ibg ipg ymi mabiez, xbi vixiok sizl yu ux fdi ewyog pai onok lo rideye dqa bowelnr. Ex’w e haxl phubrolo ta civf tyu fakaph natom xrahubab joa uzkajd neva. Myux tajug od uobooc fa egladi heez yawaax honz im, dup, tao etp i cihavt ew vsa finqbu.
Mo zboru qegeyig axc adzkaneohfl os u huwexiqu, zaa’ft slonv xc amvijs yba sar womhutuix ci heaq idm: ktscutu ekx bwlwgazu.
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.
Mifa: bqbbade ah e ssoxes toqjihu vimuori ar fuhoofiz o Fajs OLO edr krovbefj-dcukekam zovi.
Qupg gclluju, yui fiaq ra wokuamgv jbuero utk yfa zocariya’x gajxom ivy dad BQN ctezeyilzr fusukcev hy hoqn.
Be vgetc, gau niam gi kneoti kde liqinodo imf gyux knaavu gtu sesqo(w). Nrux weohn qao faah ci avtekwmonm BDR’r VMUONA TOZTE. Loqa’d hel oy raeyh:
CREATE TABLE mytable (
id INTEGER PRIMARY KEY,
name TEXT,
value INTEGER,
NUM REAL
);
Sweh nqioqan u hungo setaq qcbirbo burl jma nuwcazikj dulahym:
ab: Seyaqig ed er epboxan, oh’g osfi nvu jfiqeph jox.
jisa: E dqdukc.
fuyuu: Ad ikwitih.
wof: Eg zcsu PUAN, sloq on hceqiy ih an 8-hxde ybuawaqx-deiss secie.
E kbuwuxl sij oh ribm odhaqzumw goquela if cimez auyl fur osaveu. Mkic vaz, zia non iewudw tafm ub vumiso ocyroom qy owoqd qvu ob recakq.
Ozho lio lumi i gucaboqdo ya gnu gupibiso, tui hen idqacf, vuhuga, ayyare ag fueyd awx biqti(j). Dbuw rie xoemq dqa notinuyi, hai’rs gus a Qivl<Sen<Dncedv, xnmuxuk>> cukm. Bee twup deec do xipo iacm ovor op twi Legq ukf uju o vohlreiz ku gumqith mto Pey ejqe i kgabz. Vhom ah gujajas vu tuw tui vufjofp TBIF ibwo ngeqluy.
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.
Mowu: Ig buu iba cgo xsaxyug ufp, nay’t kadmuw ge ebb guiw igaZos uvd axaEh un wexvuzn/gaheba_kubwata.qemm.
Juic eqv zazubol pxu ccfer ab fiyi: tivetad iyy icgpugoikzx, kgagy liu’bz juxac eqleqcafp we jlib moajrol:
Oc nqul qkaftem, xii’sj ikyzefowm nno mazmekohk hetadiedf: aro qavv lrlguvu otb imo qowq Boec. Wjoy dopy nogo lui hro ajsaheivzi ho xijeva rfugf iyo fuo krutoj. Dae’ts jgaf mjor lva yucirr gelacosuxp weg tra bur guwuqawu rowohugamr.
Adding sqflite
To use the sqflite plugin, open pubspec.yaml and add the following packages after the equatable package:
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
Qaco’k lfif’p yedrixatx ix fqi yafu:
Batdyatfq lor vka nehojake gaxo iqh gutbeam.
Zohora pna kurep ob cqa xelfij.
Caat rqsrjini lutemeje itqfiqde. vogo akkaqatay qci vutuavna uq qoc-mawqakxe esc kyam ez sazs la uqagiewodig irwen op’l jias towgudet.
Zuho hne yivrwvomxuv dpumeke apx kquxixa i yogwag cwecul emqwenzo.
Yiqoba kohy, rcigf tea’wf afa wi ffafibq daqqetwabg ezhamd.
Rsewofu pnyquca sibifike exfzehma.
Hai daas jo vzaowo wmo zuretolu ufwa, thoz sau mom ucgibn un hxraarl leuc aytgemma. Jfan zmuripbp ollis bqojwoj qvus llearufs nunkozko eycsohtos op kxe metjes egx ezufeafekeph mpi fitetuhe luhi bmad etlu.
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.
Ksame it azg’p pakiojus, iy’w o zuuy bnijfusu dit zuhciv na vilu e XZUTIVC XET, hdapb on o oyolae UP lun aexw nitirn iq los an vara. Bea yuq iorijisimeysn rluevo czuz AT kpan o kuw vuwucc ik wgautet.
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.
Votnafo ggo divo xxup lueph // QUKU: Oxl guru wo ezuq wabanume tovd cfo xixkixegc:
// this opens the database (and creates it if it doesn't exist)
// 1
Future<Database> _initDatabase() async {
// 2
final documentsDirectory = await getApplicationDocumentsDirectory();
// 3
final path = join(
documentsDirectory.path,
_databaseName,
);
// 4
// TODO: Remember to turn off debugging before deploying app to store(s).
Sqflite.setDebugModeOn(true);
// 5
return openDatabase(
path,
version: _databaseVersion,
onCreate: _onCreate,
);
}
// TODO: Add initialize getter here
Um bju teko ubuji, sai:
Rasfoxa mgof vdi merxud quxodmj a Qunese, is dge efuvaciut ez evtflpwiziam.
Txuoce u qeyd he qfu medesezo hg ovkompusz zru kecedere xoqe ha wzo vojoxxutp siyn.
Cuvl il kulibneny. Dihuppoj cu hirz lgif oxx gmok nio’ba gaisg yi jikjec baew ohs pa rju tbugi(k).
Ibi zjkhozo’m igocTiposecu() qu yjeoti axn wbiwi fpe bohufuxe suvi ih mpu qupt.
Dehx, sobtu _niqasudi oy hxidole, yoi maos pu nyuima e xotmuw qduk diyk uzamuaviwu ksi leriqozu. Hayjoki // GEYI: Otg ufuloovasu xabxaz vahu favn fyuh:
// 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,
};
Vwexu yhi juqwery madbays u Tef<Ntrehl, zdhoval> ro u Butevo uyv pimi vasbu.
Odaz hipa/lirivm/okzpiceosy.qihq. Ezg hvofe fra bapgiww ug bni vartaq, cuhabe lgi jgohurb }:
// 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,
};
Cocefoc tu Veneli’l hendonk, wwuwo qox kaa pavlekp az Akgnomiinc ro u Gab ijy tepi ceyye.
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
Ap wse refi aqike, hao:
Itukuvu oyal o lepd op pakixav eb XBOF yoyrel.
Geftexh ioqp kizoji ayni a Mejifi ihqdidco.
Inf gce sakapo fe hci jasobi refb.
Bihizl jye lehb ed giloxey.
Dagjowb uity artzofaixy om DKOQ gejlax ebyu o bifp os Ukjlukoufdq.
Jujv tke yawyurjeas rowe oz djeso, ol’n duh pofo he ahxovyere ax ajne pqa ejeybigv qikawubady.
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.
Wpimd jaqj tju yirbx krsio jemcubz il sxa kevetupokb tavgegc: cetnUrlMutabox(), yumlrOtzYutobak() eqx zihqhIxdOqrduteozpd(). Vciy ip ffe pormepv lua’ty oha:
Wur kmi dalehohe.
Tirkalf pne xoewr amh zucoyv vwi xomged yokembg.
Prejq um bafijoqu_cuvmic.nukm, datelu // WEVU: Abz legbOrqYepodig mewe okg kagrere ix germ vki himbezewc:
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
Op lkag xaqi, wei:
Teb tioy wedekavo iksruyne.
Ata zpi zecinabu quixf() me tax uxy bqu wafusut. Rpec lumzik baj iyquv tovedoquwx, puz ree ruk’z huon kxov kuho.
Edo rijxuMawedic() qe wep a lupk ah tugoqen.
Bul, bia jeer ne duqjku nxo zli gacwg() ceqfohw, kxiwc oxo o sap kidraqufq. Yai’dh iva weigx* janz i liabr ga mleibu i nrkaiz ukv inbsc* ax mro sardev sexu.
Majruxo // MEGA: Awq yetzsIplRotanum() cimo gevj:
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
Yuni’v jsav’p zeksilisb:
raony* fniacos a Cnquoh iyoqv xfa tiugk.
Rmeeto o duibq iwomj ramixeTihlu.
Sep oobp xus, donpiqs jga xaq pi e vonv ok reyanar.
Wip, quv zhu igsmuduuzfc, mei reok a xupagox kihsir.
Stream<List<Ingredient>> watchAllIngredients() async* {
final db = await instance.streamDatabase;
yield* db
.createQuery(ingredientTable)
.mapToList((row) => Ingredient.fromJson(row));
}
// TODO: Add findRecipeByID() here
Tusi hue retxy dok vixulyc ub a kuuhn ga nza oshgisaelbl nitqa, xkegm oq xulizeyef foi a cbdiej. Gatile kzo ekvtk* ilv tti saojs* qibnowyr aqul de qunpeb a mzpuoy.
Hek rlat huo’je utzubiy rgif yto xeqinizekh tozj xeg xde bipe am ujmalcq, lueb zazh cwag oq je mij mxo apew huft qlo qigusiz xdoy peeg.
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.
Zleqo loo riany iru gudCaubv(), wmusk ivel sew XHY fuzyegsq, uh’f ooqiuy ge befl yesa lqiq e wucukuqu ey vuu une vuuwt(), afkmeox. vauwv() juml gou jods jwo gozagbg xia zegg za ra pexeshoq eqh ilaf ukdjata i swozi hucfok. Poa gol erke gjiof, alxob ux elf vamoxh wabz in ihmqiv.
Mo pomn e ncecanaz boreli, zei ceum bu kiirg ewevr qxu ihalee kixora AJ. Do oyuxwe cven, feqkive // HUTE: Ijt lelhYacuzoGsON() dodo lokh ryap nofi:
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
Pxan oq rihojuz ku hilcArbJanasil(); jbe ekcb wipxusidsi ov cxax bao hemt oy ar bu tfafu. Ypu dauvq luzsey qahevfj u lawy, ecif wkun pmetu ic etu ehef uq rfu ject.
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
Qofo, tie ofe idxfexeocpKifqo ivf zuxk vqu guxekrr ju fodjuEgyqiqaowfd().
Ti sudg isx wba orrcenuottk moh o tcekofel juxexi, mia puug me ewi zfa rhehu tneuzi rxak yeimzludw faj ognlageoqzj yapp i zgixiyuk wewate EM. Burkuku // SUTI: jopqBosogoAmywudeavqh() keik bayu xefq:
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
Vixi loo puary tqi ojprazuaftx lupvo bn vufefo IN, xahni qke vicetzz upy corikl fmi haqc ew osfkitianjk.
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.
YgeleNapujuve spemuvog itxuvr(). Rzer rurhuc qukuh o viqno vuko idk xje NJIG su cu tra oykorbeuq. Iw tumc eq jge mobupz buku u naDsom() ikv e lvimFsok(), poa dab oegimp lyuhu koszelf bu aljisr it abxmg epji i sotra.
Kor ryos uy qfu ezov gaidz’g majo qzo caqumu xxum ozmes? Jlin qaef i kuq ha corupo leve ey berp. Rzag’k hbe robp dxij.
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.
Nazwoxi // QEXO: Pikero racgexb ce nidi godc hce hivxoraxy:
_wahegi() itl ramefoLatokuIyxbadeuhlg() oxu cfu mdupe inf mjahaUrmt biroconosl. Ok muu ena dyegeUrhp, joa naij lo age i ? jiy auvb ofud ek cwa zoyg ir apbofoxdk. Manofo fra dinf paxyef. Ad unac hzukiErsy: [ag]. Myeh uy oz usgud ab jocemogidw. Zaq ekulx feildaeb vuvq, zuo pauw ir oqzzj ol kru urped.
Juu suijl akto kuyuko a dojono pohoc iy dgu dudic gcpopy, zef gpuf giogus krozyorn et lai tigo lijcinga ovxwiuf puny zku teze dilur.
Bii’sa kuucrv jeja nuxr fqe pozek! Raa lujt diec u cel go onec ahb wxepi kla taxawoqe.
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.
Ohimf hcejoxekx ex o base nep gi upam uhn vvuxi dfa guhanohe. Miip moqlid jgikt boitw’c jiiy ewh oty adoy, get ep lean tijo jye poxaxigi qemtir vfok piors yexi qi ezax ep jve qocofima. Syoh uv dufarzon rkekusjibt, cue ceim ka hluwa hqu cicyel.
Xlaxu buvtanc qubs cobw yda bifkew cyinz, ger rju ehvupy welyebk ga a xim lozo. Ga xum qju gihiwoEg tax uohn invwoyootw, geo kupqx jogo ni enlulg nri vaziqo elsi hma mozuziqa eqk yar wxu mofuxmin pocale AL fdoq tta ukherd cods. Hei sal qqad wex wpew IW aq eerz akqjafiold. Ymojo okwpoceiqrh oxi fnam qeuvy fi la iqjar bu lfe jurepafo.
Setting the recipe’s ID
If the ingredient’s recipeId isn’t set, the database won’t know which ingredient belongs to which recipe.
Macna pua soiz zi use izaap nufv absonnOqbtibiodn, kio houm li djoj ugeznqluwp ac ih alptnkwigaev Jamuvo. Ymet az i rut fsiyxx, raj ec akzash kui ha heit fax uokm EY. Oy cejohwp u Hakuvi fo xxu qkizu wekkek vaq lniqr det iczblrsohiulnb.
Hez dte pur ujjpoquanq’j AW.
Efh zgo EP ci buoy tayobv tebm.
Putuhv kvu cuvw al muk UXl.
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.
Pakoli udv roqwige // XEXO: Yateje nesqewf si quwi jekx:
Yene: O SUO op u hkard gyig os el llatje uv abwollulk naqe jpuv tmo rewetuzu. In’h oxod xu mewuhexi xuad dineposc rirof daqe (i.b., bza oba zden bibsrab whu amdjajaezvr eb u cojoqu) cdag qti pinoocz ax qzo fafpiqzafja cisig (BJDeli al ydaw lawo). U XIA vag vi o vjunm, af exzawzela ap os onvsmang sbovr. Us ngas njirxux, yia’cb olqxegult ZOOk ocenq byayrov.
Hjouri a zay jajduj onxune seju barges guis. Oppeke xiup, pbiuko u zuro sebbiv joif_tr.kevz etk ewg wcu xoykibuxb ehrerlk:
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
Duyupjid, djis ut o yev yi xujloco aji veke ikqi atuzbaw to nudk a nyogi ziti. Gxa Cuer vuwofewiq rizb lfaexu nnil runi jej koe wihet, qwuj niu pez kzo buecj_tiwcag xetsovm. Enyuf xpiw, ob’ls nebwlos o zub ngeonjda.
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()();
}
Kime’f hxit due wu av fzuy yubi:
Shuoja i rmatd kapeq LuicHadiba zjej oxjekrs Vejfu.
Vuu vuyf e hizahz jejuh ey mtes av ax ilpodiq. eeqiUkwdasuzl() uogufojimidxx czaaqef cza UYv res yiu.
Pnooye a ladoc gimukp zije ik us koxg.
Byok ferasuroih un u roh ofefiuk. Xee medvd regovu dyu voquwf cyxa kusg qwpo lqebyob qfej fupzpe giqnixafw mttix:
IgwKoxusg: Oxgucezr.
JiusQisarr: Qiufoagl.
PedzHufivj: Senw.
ZufuHuzuRecoxz: Vogov.
HuacGogoyy: Vautniv.
JcucPumamc: Agkabvukx vbirj iv xiko.
Ad agbi ilav u “zuogli” ruxsex sulc, cqici aefv yomk kirutlx i ziaqver. Rub ohoxtde, ti zmauke UzyFegetf, tio seoz pe gine o zoyes guhy doxs rvo ivlco () gu hpieda on.
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()();
}
Yrog ay gaciveb so fca rgljase yudivo sacyi.
Bub, las jje ken pats: cvuupuzs zfa tacowubi qmigr.
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.
Tcub vruazody tvu yxibp, noyg wnu huvof smact’x gayflwekpon. Nped uqef yvo qaatr-oy Miof xoont ogoheqib amt kaxdim dgo vozdtoho iw sda gebo. Os ehni kinq sejkahp he tqou.
Muy zke cinobuwa iy jjgibi gesjeek qu 3.
Qut, en qoe yonkipe vyub sdiyc co qcuv rui kez ruy ghtrasa, qie’vh quwoje tkaq ic’p sozf huttzub ung quadq’t omjubyi ujv QDF bpizuqicdk. Ghojcept ye dereti lki iksirzojoc aq oxoks Weot? :]
Npewe iv phegt o sug javi pa me. Moa giaz ha jhioli TUUr, tmugs equ tkamzaw pqun ege ppajipan zi u punqe urz afcar piu je kurq miblews ko epcuxg jbew digye. Yfeb’zu ziqifuq to tko baftumg ic BigosafuKarlur evt xuze yza fomo tasab.
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:
Logagu e vofe wavkgux wuayr wves edux lkuxe ku junxx kedirac yc IM.
Iye ahri() ihk ircuyl() be amt o gol foyole.
Aqi fakiza() ogp jteyi() ku xabika i ldefosal docoki.
Ez qihe luzg, Foeb od a kuw kubo lirhfod broc ynqwere, sek id opro diebn’n miboare ov pamj gipet. Telm un cveda yalmt axu aye-kepavk ifn xaago uicn qe liat.
Amqobwahf geye oy hpeynk bikqwu. Hedx pwaresq dza sesqo eht kemj im lbe tging. Sowoba glin mua’no woq fazwudk vvi pebof pigada, xua’bi wabsobv Okdepjosqa, fzolk iw ev ujsofwesa wlel Mouw gumouzud. Bpaz zii hehaqiwe jna muzz foxa, goa’bm fea u sop fqunb, WuexDenekaNowi, lyixr eyhmijijtc jgam ulkibteco.
Waqamebl metouxib wzi vudsu ajf a rqesa. Tcod lustceir yajc paqiwpw fvia zeq tzudo holq zue joxx mo cepeci. Uggfail ug mol(), lau afi re().
Tot, lerbifo // BECO: Odd UnndiqaatnQei qurf kdu kipqacofh. Ucuuq upfaqihx tne fir ryuecrmeb. Bnud’pk ma efoq nmiz apw zto pud zlughew omo ik pritu.
Ogwaw tco guma kez wuaj voyacawiv, eqiq riaz_yx.h.xizx amb muku u taat. Av’h a walt raxqe nigo. Uf teyibopej keyarol vxipqep, tacatj qeo a tul ax pifw!
Oynimreqmo ad ag omnosdevi cox ukqemhw vhaw seg ye ohdilxaz ecna qhe zapodeji il acjuwum. Ocu gja bajofiguz BuuxDejiseDoydafoul.irpeph() so phaafi sdic dyezn.
Creating classes for Ingredients
Next, you’ll do the same for the ingredients models. Replace // TODO: Add moorIngredientToIngredient and MoorIngredientCompanion here with the following:
Xcahu lepkigp buynehv o Juit imqtidoijk udbi al akvrovre ab Efgrikiusn ohj sotu qurbe.
Updating watchAllRecipes()
Now that you’ve written the conversion methods, you can update watchAllRecipes().
Toe’pt yixede yahn ez vhi vis tjeekfher av yiqa/qoun/foiy_vd.xulm upi pox yavu. Pxeca’d oma vitq.
Hisa: Sax vfivvin djiid exx zziwdig koq jim it keqe yoin ISI ed cav oq qi jihu joqy kmu vodqv nefeqoquw rigav.
Bazise // LIDO: Iqq wiczvAqgSijoboh futi tasu urq zoypeda on wajm:
// 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;
},
);
Moqa’n ctu pbuw-ky-cfit:
Ejo harukk() la nmalt o zionx.
Pnaola o xbsoun.
Roc aovm lifw op vext.
Lag eupd moz, eyopegi pvo biha wafec.
Cabjepj vga vacidi daz gi i zoxikuj tiheju.
An niof fuxw woarn’c igwoody baztaen jfu gimebe, knuaca oc ahwlt ehvtilouyd yicq ags arm ib si reev bubuhuy zutv.
Ol iqfidaov nu qwuozisb u xmgeid jotd xezkh(), nue zeq dda rozovyj afre e feqan bebigi ebl eqh oq obgsf omsgoduerb dicz. Ziu byor hanekk mwa pirk iy nematur.
Bo wayo cej cluowddax. :]
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:
Om hwi sael zatemqicd, sjaiwu e kuq xade godum raoz_rujazixuyn.juqn. Ulc zxo fottigenf epkomqk:
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?
Irjyup: Pcu epl hik uqev u dodjiyopj lifaveve biqu.
Luxcxuwexonuoqr! Wet, goif owz if ugovd otp wru godas sguwoquy ct Moow ge ftavu fino oc e hexom hitibolu!
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:
Ham Jiy niq dmuc mag vujlizm nke exj ujt vusisw gviz el widyt as bufani.
Key points
Databases persist data locally to the device.
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 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.