کار با دیتابیس در فلاتر -Flutter
Flutter پکیج های پیشرفته بسیاری را برای کار با database فراهم می کند. مهمترین پکیج ها ها عبارتند از –
- sqflite – برای دسترسی و دستکاری پایگاه داده SQLite ، و
- firebase_database – برای دسترسی و دستکاری در پایگاه داده cloud NoSQL میزبانی شده از Google استفاده می شود.
در این فصل ، در مورد هر یک از آنها به طور مفصل بحث کنیم.
مقالات
SQLite
بانک اطلاعاتی SQLite موتور دیتابیس جاسازی شده مبتنی بر sql است. این موتور دیتابیس کوچک و time-tested است. بسته sqflite عملکرد زیادی برای کار با بانک اطلاعاتی SQLite فراهم می کند. این متدهای استاندارد برای دستکاری engine پایگاه داده SQLite را فراهم می کند. عملکرد اصلی ارائه شده توسط پکیج sqflite به شرح زیر است –
- Create / Open (متد openDatabase) یک پایگاه داده SQLite.
- Execute SQL statement (متد execute ) را در برابر پایگاه داده SQLite اجرا کنید.
- متدهای پیشرفته query (متد کوئری) برای کاهش کد مورد نیاز برای و کسب اطلاعات از پایگاه داده SQLite.
ما یک نرم افزار product ایجاد کنیم تا بتوانیم اطلاعات محصول را از یک موتور استاندارد پایگاه داده SQLite با استفاده از پکیج sq sqlite ذخیره کرده و از آن استفاده کنیم و مفهوم پشتیبان بانک اطلاعاتی SQLite و sqflite package را بهتر درک کنیم.
- یک برنامه جدید Flutter را در Android studio ایجاد کنید ، product_sqlite_app.
- کد startup پیش فرض (main.dart) را با کد product_rest_app ما جایگزین کنید .
- پوشه assets را از product_nav_app به product_rest_app کپی کنید و assets موجود در پرونده * pubspec.yaml` را اضافه کنید.
1 2 3 4 5 6 7 8 |
flutter: assets: - assets/appimages/floppy.png - assets/appimages/iphone.png - assets/appimages/laptop.png - assets/appimages/pendrive.png - assets/appimages/pixel.png - assets/appimages/tablet.png |
پیکج های sq sqite را در پرونده pubspec.yaml مانند شکل زیر تنظیم کنید –
1 |
dependencies: sqflite: any |
به جای هر نسخه از جدیدترین نسخه sqflite استفاده کنید
- بسته path_provider را در پرونده pubspec.yaml مانند شکل زیر تنظیم کنید –
1 |
dependencies: path_provider: any |
- در اینجا از پکیج_ path_provider برای بدست آوردن مسیر پوشه موقتی سیستم و مسیر برنامه استفاده می شود. به جای هر نسخه از جدیدترین نسخه sqflite استفاده کنید .
- Android studio هشدار می دهد که pubspec.yaml به روز شده است.
- بر روی Get dependencies option کلیک کنید. Android studio پکیج را از اینترنت دریافت کرده و آن را برای برنامه به درستی پیکربندی می کند.
- در بانک اطلاعاتی ، به key، id به عنوان field اضافی به همراه properties – Product مانند name, price, و غیره نیاز داریم ، بنابراین ، در کلاس Product ، ویژگی خاصیت id را اضافه می کنیم. همچنین ، یک متد جدید ، toMap اضافه کنید تا شی Product را به شی Map تبدیل کند. از serMap و toMap برای serialize و de- serialize کردن شیء Product استفاده می شود و درمتدهای دستکاری پایگاه داده استفاده می شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class Product { final int id; final String name; final String description; final int price; final String image; static final columns = ["id", "name", "description", "price", "image"]; Product(this.id, this.name, this.description, this.price, this.image); factory Product.fromMap(Map<String, dynamic> data) { return Product( data['id'], data['name'], data['description'], data['price'], data['image'], ); } Map<String, dynamic> toMap() => { "id": id, "name": name, "description": description, "price": price, "image": image }; } |
- برای نوشتن عملکردهای مربوط به SQLite ، یک پرونده جدید ، Database.dart را در پوشه lib ایجاد کنید .
- import statement لازم را در Database.dart ایموپورت کنید.
1 2 3 4 5 6 |
import 'dart:async'; import 'dart:io'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite/sqflite.dart'; import 'Product.dart'; |
- در اینجا به نکات زیر
توجه کنید –
- async برای نوشتن متدهای ناهمزمان (asynchronous ) استفاده می شود.
- io برای دسترسی به فایل ها و دایرکتوری ها استفاده می شود.
- path برای دسترسی به عملکرد ابزار dart core مربوط به مسیرهای پرونده استفاده می شود.
- path_provider برای دستیابی به مسیر موقتی temporary و application path. استفاده می شود.
- sqflite برای دستکاری پایگاه داده SQLite استفاده می شود.
- کلاس جدید SQLiteDbProvider ایجاد کنید
- یک singleton شیء SQLiteDbProvider را طبق شکل زیر Declare کنید –
1 2 3 4 5 |
class SQLiteDbProvider { SQLiteDbProvider._(); static final SQLiteDbProvider db = SQLiteDbProvider._(); static Database _database; } |
از شیء SQLiteDBProvoider و متد آن می توان به متغیر static db دسترسی داشت.
1 |
SQLiteDBProvoider.db.<emthod> |
برای بدست آوردن بانک اطلاعاتی (Future option) از نوع Future <Database> یک متد ایجاد کنید. جدول product را ایجاد کنید و داده های اولیه را هنگام ایجاد پایگاه داده بارگیری کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
Future<Database> get database async { if (_database != null) return _database; _database = await initDB(); return _database; } initDB() async { Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, "ProductDB.db"); return await openDatabase( path, version: 1, onOpen: (db) {}, onCreate: (Database db, int version) async { await db.execute( "CREATE TABLE Product (" "id INTEGER PRIMARY KEY," "name TEXT," "description TEXT," "price INTEGER," "image TEXT" ")" ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"]\ ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"] ); } ); } |
- در اینجا ، ما از متدهای زیر
استفاده کرده ایم –
- getApplicationDocumentDirectory – مسیر فهرست برنامه (directory path) را باز می گرداند
- join – برای ایجاد مسیر خاص سیستم استفاده می شود. ما از آن برای ایجاد مسیر پایگاه داده استفاده کرده ایم.
- openDatabase – برای باز کردن یک پایگاه داده SQLite استفاده می شود
- onOpen – برای نوشتن کد هنگام باز کردن بانک اطلاعاتی استفاده می شود
- onCreate – هنگام نوشتن یک پایگاه داده برای اولین بار برای نوشتن کد استفاده می شود
- db.execute – برای اجرای نمایش داده شدگان SQL استفاده می شود. یک پرس و جو را می پذیرد. اگر پرس و جو دارای حفره (؟) باشد ، در آرگومان دوم مقادیر را به عنوان لیست می پذیرد.
- متدی بنویسید تا همه محصولات را در بانک اطلاعات دریافت کنید –
1 2 3 4 5 6 7 8 9 10 11 12 |
Future<List<Product>> getAllProducts() async { final db = await database; List<Map> results = await db.query("Product", columns: Product.columns, orderBy: "id ASC"); List<Product> products = new List(); results.forEach((result) { Product product = Product.fromMap(result); products.add(product); }); return products; } |
- در اینجا ، موارد زیر را
انجام داده ایم –
- از متد query برای واکشی ( (fetchکلیه اطلاعات مربوط به Product استفاده شده است. query بدون نوشتن کل query ، shortcut را برای query در یک جدول فراهم می کند. متد کوئری با استفاده از ورودی های ما مانند columns، orderBy و غیره ، کوئری مناسب را ایجاد می کند
- برای بدست آوردن جزئیات products با حلقه زدن به شیء results ، که همه row ها را در جدول نگه می دارد از روش fromMap استفاده شده است.
- متدی بنویسید تا محصول مخصوص id باشد
1 2 3 4 5 |
Future<Product> getProductById(int id) async { final db = await database; var result = await db.query("Product", where: "id = ", whereArgs: [id]); return result.isNotEmpty ? Product.fromMap(result.first) : Null; } |
- در اینجا ، ما از where و whereArgs استفاده کرده ایم تا filter ها را اعمال کنیم.
- برای insert، update و delete محصول از پایگاه داده ، سه متد ایجاد کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
insert(Product product) async { final db = await database; var maxIdResult = await db.rawQuery( "SELECT MAX(id)+1 as last_inserted_id FROM Product"); var id = maxIdResult.first["last_inserted_id"]; var result = await db.rawInsert( "INSERT Into Product (id, name, description, price, image)" " VALUES (?, ?, ?, ?, ?)", [id, product.name, product.description, product.price, product.image] ); return result; } update(Product product) async { final db = await database; var result = await db.update("Product", product.toMap(), where: "id = ?", whereArgs: [product.id]); return result; } delete(int id) async { final db = await database; db.delete("Product", where: "id = ?", whereArgs: [id]); } |
کد نهایی Database.dart به صورت زیر است –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
import 'dart:async'; import 'dart:io'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite/sqflite.dart'; import 'Product.dart'; class SQLiteDbProvider { SQLiteDbProvider._(); static final SQLiteDbProvider db = SQLiteDbProvider._(); static Database _database; Future<Database> get database async { if (_database != null) return _database; _database = await initDB(); return _database; } initDB() async { Directory documentsDirectory = await getApplicationDocumentsDirectory(); String path = join(documentsDirectory.path, "ProductDB.db"); return await openDatabase( path, version: 1, onOpen: (db) {}, onCreate: (Database db, int version) async { await db.execute( "CREATE TABLE Product (" "id INTEGER PRIMARY KEY," "name TEXT," "description TEXT," "price INTEGER," "image TEXT"")" ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [1, "iPhone", "iPhone is the stylist phone ever", 1000, "iphone.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [2, "Pixel", "Pixel is the most feature phone ever", 800, "pixel.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [3, "Laptop", "Laptop is most productive development tool", 2000, "laptop.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [4, "Tablet", "Laptop is most productive development tool", 1500, "tablet.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [5, "Pendrive", "Pendrive is useful storage medium", 100, "pendrive.png"] ); await db.execute( "INSERT INTO Product ('id', 'name', 'description', 'price', 'image') values (?, ?, ?, ?, ?)", [6, "Floppy Drive", "Floppy drive is useful rescue storage medium", 20, "floppy.png"] ); } ); } Future<List<Product>> getAllProducts() async { final db = await database; List<Map> results = await db.query( "Product", columns: Product.columns, orderBy: "id ASC" ); List<Product> products = new List(); results.forEach((result) { Product product = Product.fromMap(result); products.add(product); }); return products; } Future<Product> getProductById(int id) async { final db = await database; var result = await db.query("Product", where: "id = ", whereArgs: [id]); return result.isNotEmpty ? Product.fromMap(result.first) : Null; } insert(Product product) async { final db = await database; var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id FROM Product"); var id = maxIdResult.first["last_inserted_id"]; var result = await db.rawInsert( "INSERT Into Product (id, name, description, price, image)" " VALUES (?, ?, ?, ?, ?)", [id, product.name, product.description, product.price, product.image] ); return result; } update(Product product) async { final db = await database; var result = await db.update( "Product", product.toMap(), where: "id = ?", whereArgs: [product.id] ); return result; } delete(int id) async { final db = await database; db.delete("Product", where: "id = ?", whereArgs: [id]); } } |
برای بدست آوردن اطلاعات محصول متد main را تغییر دهید.
1 2 3 |
void main() { runApp(MyApp(products: SQLiteDbProvider.db.getAllProducts())); } |
- در اینجا ، ما از متد getAllProducts برای واکشی ((fetch کلیه product ها از پایگاه داده استفاده کرده ایم.
- برنامه را اجرا کنید و نتایج را مشاهده کنید. این مشابه با مثال قبلی Accessing Product service یاAPI خواهد بود ، مگر اینکه اطلاعات مربوط به محصول از پایگاه داده محلی SQLite ذخیره و گرفته شود.
Cloud Firestore در فلاتر
Firebase یک بستر توسعه برنامه BaaS است. این ویژگی بسیاری از ویژگی ها را برای سرعت بخشیدن به توسعه برنامه های تلفن همراه مانند سرویس authentication ، cloud storage و غیره فراهم می کند ، یکی از ویژگی های اصلی فایربیس Cloud Firestore است ، یک پایگاه داده مبتنی بر ابر real time NoSQL.
Flutter پکیج ویژه ، cloud_firestore را برای برنامه نویسی با Cloud Firestore فراهم می کند. بگذارید یک فروشگاه آنلاین کالا در Cloud Firestore ایجاد کنیم و یک برنامه کاربردی برای دسترسی به فروشگاه محصول ایجاد کنیم.
- یک برنامه جدید Flutter را در Android studio ایجاد کنید ، product_firebase_app.
- کد startup پیش فرض (main.dart) را با کد product_rest_app ما جایگزین کنید .
- پرونده Product.dart را از product_rest_app در پوشه lib کپی کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Product { final String name; final String description; final int price; final String image; Product(this.name, this.description, this.price, this.image); factory Product.fromMap(Map<String, dynamic> json) { return Product( json['name'], json['description'], json['price'], json['image'], ); } } |
بسته cloud_firestore را در فایل pubspec.yaml مانند شکل زیر تنظیم کنید –
1 |
dependencies: cloud_firestore: ^0.9.13+1 |
- در اینجا ، از آخرین نسخه پکیج cloud_firestore استفاده کنید.
- Studio Android هشدار خواهد داد که pubspec.yaml مطابق تصویر زیر به روز شده است –
- بر روی Get dependencies option کلیک کنید. Studio Android بسته را از اینترنت دریافت کرده و آن را برای برنامه به درستی پیکربندی می کند.
- با استفاده
از مراحل زیر یک پروژه در Firebase ایجاد کنید –
- با انتخاب برنامه رایگان در https://firebase.google.com/pricing/ ، یک حساب Firebase ایجاد کنید.
- پس از ایجاد حساب Firebase ، به صفحه نمای کلی پروژه هدایت می شوید. این لیست کلیه پروژه های مبتنی بر Firebase را لیست کرده و گزینه ای را برای ایجاد یک پروژه جدید فراهم می کند.
- روی Add project کلیک کنید و صفحه Create project باز خواهد شد.
- products app db را به عنوان نام پروژه وارد کنید و روی گزینه Create project کلیک کنید.
- به کنسول Firebase بروید.
- روی نمای کلی پروژه کلیک کنید صفحه نمای کلی پروژه را باز می کند.
- بر روی آیکن Android کلیک کنید. تنظیمات پروژه ویژه توسعه Android را باز خواهد کرد.
- نام پکیج Android ، com.tutorialspoint.flutterapp.product_firebase_app را وارد کنید.
- روی Register برنامه کلیک کنید این یک فایل پیکربندی پروژه ، google_service.json تولید می کند.
- google_service.json را دانلود کنید و سپس آن را درون دایرکتوری android/app قرار دهید. این پرونده ارتباط بین برنامه ما و Firebase است.
- کد
- android / app / build.gradle را باز کرده و کد زیر را اضافه کنید –
1 |
apply plugin: 'com.google.gms.google-services' |
- android / app / build.gradle را باز کنید و کد زیر را نیز در آن وارد کنید.
1 2 3 4 5 6 7 8 9 |
buildscript { repositories { // ... } dependencies { // ... classpath 'com.google.gms:google-services:3.2.1' // new } } |
در اینجا از plugin و کلاس path به منظور خواندن پرونده google_service.json استفاده می شود.
android / app / build.gradle را باز کنید و کد زیر را نیز در آن وارد کنید.
1 2 3 4 5 6 7 8 9 10 11 |
android { defaultConfig { ... multiDexEnabled true } ... } dependencies { ... compile 'com.android.support: multidex:1.0.3' } |
این وابستگی برنامه اندرویدی را قادر می سازد تا از قابلیت های چندگانه dex استفاده کند.
- مراحل باقیمانده در کنسول Firebase را دنبال کنید یا از آن صرف نظر کنید.
- با استفاده از مراحل زیر product store را در پروژه تازه ایجاد شده ایجاد کنید –
- به کنسول Firebase بروید.
- پروژه تازه ایجاد شده را باز کنید.
- از منوی سمت چپ بر روی گزینه Database کلیک کنید.
- روی گزینه Create database کلیک کنید.
- شروع را در حالت تست کلیک کنید و سپس فعال کنید.
- روی add collection کلیک کنید. product را به عنوان collection name وارد کرده و سپس بر روی Next کلیک کنید.
- اطلاعات مربوط به product را مطابق تصویر در اینجا وارد کنید –
- با استفاده از گزینه های Add document ، اطلاعات محصول اضافی را اضافه کنید .
- فایل main.dart را باز کنید و فایل افزونه Cloud Firestore را وارد کنید و پکیج http را حذف کنید.
1 |
import 'package:cloud_firestore/cloud_firestore.dart'; |
parseProducts را حذف کرده و محصولات fetchProducts را به روز کنید تا products را از Cloud Firestore به جای API خدمات محصول دانلود کنید.
1 2 |
Stream<QuerySnapshot> fetchProducts() { return Firestore.instance.collection('product').snapshots(); } |
- در اینجا ، از متد Firestore.instance.collection برای دسترسی به product collection موجود در cloud store استفاده می شود. Firestore.instance.collection گزینه های بسیاری را برای فیلتر کردن collection برای دریافت اسناد لازم فراهم می کند. اما ، ما هیچ فیلتری را برای به دست آوردن کلیه اطلاعات مربوط به محصول استفاده نکرده ایم.
- Cloud Firestore مجموعه ای را از طریق مفهوم Dart Stream فراهم می کند و بنابراین نوع محصول را در ویجت MyApp و MyHomePage از لیست Future <list <Product>> to Stream <QuerySnapshot> اصلاح کنید.
- متد build از ویجت MyHomePage را تغییر دهید تا از StreamBuilder به جای FutureBuilder استفاده کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Product Navigation")), body: Center( child: StreamBuilder<QuerySnapshot>( stream: products, builder: (context, snapshot) { if (snapshot.hasError) print(snapshot.error); if(snapshot.hasData) { List<DocumentSnapshot> documents = snapshot.data.documents; List<Product> items = List<Product>(); for(var i = 0; i < documents.length; i++) { DocumentSnapshot document = documents[i]; items.add(Product.fromMap(document.data)); } return ProductBoxList(items: items); } else { return Center(child: CircularProgressIndicator()); } }, ), ) ); } |
- در اینجا ، ما اطلاعات مربوط به محصول را به عنوان List <DocumentSnapshot> دریافت کرده ایم. از آنجا که ویجت ، ProductBoxList با اسناد سازگار نیست ، ما اسناد را به نوع List<Product> تبدیل کرده ایم و از آن استفاده می کنیم.
- در آخر ، برنامه را اجرا کنید و نتیجه را ببینید. از آنجا که ، ما از همان اطلاعات محصول به عنوان برنامه SQLite استفاده کرده ایم و تنها فضای ذخیره سازی را تغییر داده ایم ، برنامه نتیجه یکسان با SQLite application دارد.
مطالب زیر را حتما مطالعه کنید
آموزش فلاتر – Flutter
ابزار توسعه فلاتر – Flutter
تبدیل اپلیکیشن فلاتر به اندروید و IOS
تست کردن در فلاتر – Flutter testing
ترجمه اپلیکیشن ها در فلاتر – Flutter
REST API در فلاتر – Flutter
2 دیدگاه
به گفتگوی ما بپیوندید و دیدگاه خود را با ما در میان بگذارید.
https://firebase.google.com/pricing/
لینک فایر بیستون خرابه
سلام
خراب نیست ، به خاطر تحریم این وضعیته