Flutter - 集成三方庫:數據庫(sqflite)

數據庫

$ flutter pub add sqlite
$ flutter pub get
$ flutter run

運行失敗,看是編譯報錯,打開Xcode工程 ? + B 編譯

2025-05-15 08.47.33.png

對比 GSYGithubAppFlutter 的Xcode工程Build Phases > [CP] Embed Pods Frameworks 有sqfite.framework。本地默認的Flutter工程默認未生成Podfile

2025-05-15 19.03.14.png

然后查看 GSYGithubAppFlutter

...
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)flutter_ios_podfile_setuptarget 'Runner' douse_frameworks!use_modular_headers!flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
...

看代碼是引入了Flutter提供的工具的,從flutter的安裝目錄下找到podhelper.rb這個文件

17473073456022.jpg

# 方法: flutter_install_all_ios_pods
# 安裝Flutter在iOS平臺上的引擎和插件
def flutter_install_all_ios_pods(ios_application_path = nil)# 創建Flutter引擎的.podspec文件flutter_install_ios_engine_pod(ios_application_path)flutter_install_plugin_pods(ios_application_path, '.symlinks', 'ios')
end
# 方法: flutter_install_plugin_pods
def flutter_install_plugin_pods(application_path = nil, relative_symlink_dir, platform)# CocoaPods定義了 defined_in_file,獲取應用路徑,未獲取到就中斷application_path ||= File.dirname(defined_in_file.realpath) if respond_to?(:defined_in_file)raise 'Could not find application path' unless application_path# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock# referring to absolute paths on developers' machines.# 使用符號鏈接,避免使用Podfile.lock這個文件# Flutter是在ios目錄下創建.symlinks目錄,里面有軟鏈接指向Flutter下載包的位置,這樣只需要一份即可。# 先刪除,再創建對應的目錄symlink_dir = File.expand_path(relative_symlink_dir, application_path)system('rm', '-rf', symlink_dir) symlink_plugins_dir = File.expand_path('plugins', symlink_dir)system('mkdir', '-p', symlink_plugins_dir)plugins_file = File.join(application_path, '..', '.flutter-plugins-dependencies')dependencies_hash = flutter_parse_plugins_file(plugins_file)plugin_pods = flutter_get_plugins_list(dependencies_hash, platform)swift_package_manager_enabled = flutter_get_swift_package_manager_enabled(dependencies_hash, platform)plugin_pods.each do |plugin_hash|plugin_name = plugin_hash['name']plugin_path = plugin_hash['path']...# 使用path: 的方式本地依賴需要的三方庫# 手動添加打印確認下# print "plugin_name:#{plugin_name}\n"pod plugin_name, path: File.join(relative, platform_directory)end
end
$ pod update --verbose

2025-05-15 19.33.17.png

因此Podfile里的target部分就依賴了sqflite_darwin

target 'Runner' douse_frameworks!use_modular_headers!...pod 'sqflite_darwin', path:.symlinks/plugins/sqflite_darwin/darwin
end

2025-05-15 19.34.50.png

使用

打開/關閉/刪除數據庫
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'finger.db');/// 打開數據庫
Database database = await openDatabase(path, version: 1,onCreate: (Database db, int version) async {/// 當創建數據庫時創建tableawait db.execute('CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)');
});
/// 關閉數據庫
await db.close();
/// 刪除數據庫
await deleteDatabase(path);
/// 添加表
await database.execute("CREATE TABLE Test2(id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)",);/// 刪除表
await database.execute('DROP TABLE Test2');
使用SQL語句
/// 添加數據
await database.transaction((txn) async {int id1 = await txn.rawInsert('INSERT INTO Test(name, value, num) VALUES("some name", 1234, 456.789)');int id2 = await txn.rawInsert('INSERT INTO Test(name, value, num) VALUES(?, ?, ?)',['another name', 12345678, 3.1416]);
});
/// 刪除數據
count = await database.rawDelete('DELETE FROM Test WHERE name = ?', ['another name']);
/// 更新數據
int count = await database.rawUpdate('UPDATE Test SET name = ?, value = ? WHERE name = ?',['updated name', '9876', 'some name']);
/// 查詢數據
List<Map> list = await database.rawQuery('SELECT * FROM Test');
print(list)

2025-05-17 20.31.58.png

使用工具方法

使用Sqflite提供的工具方法來執行數據庫操作,而不是直接使用SQL語句

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';final String tName = 'company';
final String columnId = "_id";
final String columnName = "name";class Company {int? id;String? name;Company();Map<String, Object?> toMap() {var map = <String, Object?>{columnName: name};if (id != null) {map[columnId] = id;}return map;}Company.fromMap(Map map) {id = map[columnId];name = map[columnName];}
}class CompanyProvider {Database? db;Future<Database?> open() async {if (db == null) {var databasesPath = await getDatabasesPath();String path = join(databasesPath, 'demo.db');db = await openDatabase(path,version: 1,onCreate: (Database db, int version) async {await db.execute('''create table $tName ($columnId integer primary key autoincrement,$columnName text not null)''');},);}return db;}/// 注冊企業Future insert(Company company) async {/// 工具方法: 傳表名 + 列信息添加數據到數據庫company.id = await db?.insert(tName, company.toMap());return company;}/// 查找企業Future findById(int id) async {List<Map> maps = await db!.query(tName, /// 表名columns: [columnId, columnName], /// 查找的列where: '$columnId = ?', /// 查找條件whereArgs: [id], /// 每個問號填充的值);if (maps.isNotEmpty) {return Company.fromMap(maps.first);}return null;}/// 查找所有的企業Future<List<Company>> find() async {List<Company> companys = [];List<Map> maps = await db!.query(tName, columns: [columnId, columnName]);for (var map in maps) {Company c = Company.fromMap(map);companys.add(c);}return companys;}/// 刪除企業Future delete(int id) async {/// 根據id列刪除企業return await db?.delete(tName, where: '$columnId = ?', whereArgs: [id]);}/// 更新企業信息Future update(Company company) async {return await db?.update(tName,company.toMap(),where: '$columnId = ?',whereArgs: [company.id],);}
}
void test() async {/// 添加2條測試數據CompanyProvider cp = CompanyProvider();await cp.open();List<Map> maps = [{"name": "Google"},{"name": "Apple"},];/// 新增數據int firstId = 0;for (int i = 0; i < maps.length; ++i) {Company c = Company.fromMap(maps[i]);cp.insert(c);}/// 查找數據List<Company> companys = await cp.find();if (companys.isNotEmpty) {firstId = companys.first.id!;}if (firstId > 0) {Company firstCompany = await cp.findById(firstId);print(firstCompany.toMap());/// 更新數據Company chgCompany = Company();chgCompany.id = firstId;chgCompany.name = DateTime.now().microsecondsSinceEpoch.toString();cp.update(chgCompany);firstCompany = await cp.findById(firstId);print(firstCompany.toMap());/// 刪除數據cp.delete(firstId);}}

2025-05-20 15.33.50.png

數據庫遷移

隨著功能迭代,需要對數據庫的表結構進行修改時,比如增加新字段時,需要對表的結構進行更新。

Future<Database?> open() async {if (db == null) {var databasesPath = await getDatabasesPath();String path = join(databasesPath, 'demo.db');db = await openDatabase(path,version: 2,/// 1.新版本發布時改成2onCreate: (db, version) async {/// 2.新安裝設備觸發onCreate,所以這里添加新的字段await db.execute('''create table $tName ($columnId integer primary key autoincrement,$columnName text not null,$columnDesc text)''');},onUpgrade: (db, oldVersion, newVersion) async {var batch = db.batch();/// [onUpgrade] is called if either of /// the following conditions are met:/// 1. [onCreate] is not specified/// 2. The database already exists and [version] is higher than the last database version/// onUpgrade回調在未指定onCreate回調或者數據庫已經存在同時version字段高于已安裝的版本,執行完onUpgrade回調后應該會更新關聯的版本,設置斷點讓onUpgrade執行中斷,下次還會會執行這個方法/// 3.對舊版本的設備:判斷安裝設備已創建的數據庫版本if (oldVersion == 1) {_updateTableCompanyV1toV2(batch);}await batch.commit();},);}return db;}
/// 4.添加description字段
void _updateTableCompanyV1toV2(Batch batch) {batch.execute('ALTER TABLE Company ADD description TEXT');
}/// 其它的一些處理
final String columnDesc = "description";
...class Company {int? id;String? name;/// 5.模型增加對應字段 + 列String? description;.../// 6. 更新map和對象的轉換方法Map<String, Object?> toMap() {var map = <String, Object?>{columnName: name, columnDesc: description};if (id != null) {...
/// 調用
...
firstCompany.description = "版本2新增的字段";
print(firstCompany.toMap());

2025-05-20 16.26.51.png

事務

數據庫的增刪改查可能會失敗,導致數據與預期的不一致,為了保證在執行前后的數據一致性,引入了事務。事務具有ACID這4個特性:原子性、一致性、隔離性和持久性。

在事務中不要使用數據庫,而只需要使用事務對象訪問數據庫。

await database.transaction((txn) async {// 正確await txn.execute('CREATE TABLE Test1 (id INTEGER PRIMARY KEY)');// 不要在事務中使用數據庫// 下面會導致死鎖await database.execute('CREATE TABLE Test2 (id INTEGER PRIMARY KEY)');
});
try {await database.transaction((txn) async {await txn.update('TABLE', {'foo': 'bar'});});// No error, the transaction is committed// 1. 未報錯,則事務被提交// cancel the transaction (any error will do)// 2. 取消或執行時報錯,則拋出異常在,catch中被捕獲// throw StateError('cancel transaction');
} catch (e, st) {// this reliably catch if there is a key conflict// We know that the transaction is rolled back.// 3. 事務被回滾,執行業務相關的操作,比如提示報錯
}
批處理

使用 Batch,即批處理,來避免在 Dart 和原生代碼之間的反復切換。

batch = db.batch();
batch.insert('Test', {'name': 'item'});
batch.update('Test', {'name': 'new_item'}, where: 'name = ?', whereArgs: ['item']);
batch.delete('Test', where: 'name = ?', whereArgs: ['item']);
/// 批處理統一提交
results = await batch.commit();

在事務中,批處理的commit會等到事務提交后

await database.transaction((txn) async {var batch = txn.batch();// ...// commit but the actual commit will happen when the transaction is committed// however the data is available in this transaction/// 當事務被提交時才會真正的提交await batch.commit();//  ...
});
/// 設置批處理出現錯誤依然提交
await batch.commit(continueOnError: true);
表名和列名

SQLite的關鍵詞,要避免使用作為實體(Entity)名。

"add","all","alter","and","as","autoincrement","between","case","check","collate","commit","constraint","create","default","deferrable","delete","distinct","drop","else","escape","except","exists","foreign","from","group","having","if","in","index","insert","intersect","into","is","isnull","join","limit","not","notnull","null","on","or","order","primary","references","select","set","table","then","to","transaction","union","unique","update","using","values","when","where"

sqflite的工具方法會進行處理,避免與關鍵字的沖突

db.query('table')
/// 等價于
db.rawQuery('SELECT * FROM "table"');

其它問題

VSCode 無法調試

Error connecting to the service protocol: failed to connect to http://127.0.0.1:51020/Kra7fZnYjeI=/ Error: Failed to register service methods on attached VM Service: registerService: (-32000) Service connection disposed

原來有成功過,后面發現一直都會有問題,前段時間突然不行,在長時間運行后就會報這個錯誤,但是單獨在VSCode外部用flutter run命令能正常運行。

發現終端可以是把本地的端口轉發的代理給去掉了。然后發現VSCode的代理有這樣的說明,若未設置則會繼承環境變量中的http_proxyhttps_proxy,我把代理加到.zshrc中,所以VSCode的默認會用代理,但是運行在真機上,手機沒有代理,應該是這樣影響了網絡環境。

  1. .zshrc去掉代理的配置
  2. 重新打開VSCode && 運行 => 能正常調試

2025-05-15 18.54.02.png

參考

  1. SQLite CRUD operations in Flutter
  2. sqflite-doc
  3. sqflite Migration example

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83961.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83961.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83961.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android 中 權限分類及申請方式

在 Android 中,權限被分為幾個不同的類別,每個類別有不同的申請和管理方式。 一、 普通權限(Normal Permissions) 普通權限通常不會對用戶隱私或設備安全造成太大風險。這些權限在應用安裝時自動授予,無需用戶在運行時手動授權。 android.permission.INTERNETandroid.pe…

目標檢測指標計算

mAP&#xff08;mean Average Precision&#xff09; 概述 預備參數&#xff1a;類別數&#xff0c;IoU閾值&#xff0c;maxDets值&#xff08;每張測試圖像最多保留maxDets個預測框&#xff0c;通常是根據置信度得分排序后取前maxDets個&#xff09;&#xff1b; Q: 假如某張…

聯合索引失效情況分析

一.模擬表結構&#xff1a; 背景&#xff1a; MySQL版本——8.0.37 表結構DDL&#xff1a; CREATE TABLE unite_index_table (id bigint NOT NULL AUTO_INCREMENT COMMENT 主鍵,clomn_first varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMEN…

軟件架構之-論分布式架構設計及其實現

論分布式架構設計及其實現 摘要正文摘要 2023年2月,本人所在集團公司承接了長三角地區某省漁船圖紙電子化審查項目開發,該項目旨在為長三角地區漁船建造設計院、漁船審圖機構提供一個便捷化的服務平臺。在次項目中,我作為項目成員參與了整個項目的建設工作,全權負責項目需求…

Pydantic數據驗證實戰指南:讓Python應用更健壯與智能

導讀&#xff1a;在日益復雜的數據驅動開發環境中&#xff0c;如何高效、安全地處理和驗證數據成為每位Python開發者面臨的關鍵挑戰。本文全面解析了Pydantic這一革命性數據驗證庫&#xff0c;展示了它如何通過聲明式API和類型提示系統&#xff0c;徹底改變Python數據處理模式。…

3、ubantu系統 | 通過vscode遠程安裝并配置anaconda

1、vscode登錄 登錄后通過pwd可以發現目前位于wangqinag賬號下&#xff0c;左側為屬于該賬號的文件夾及文件。 通過cd ..可以回到上一級目錄&#xff0c;通過ls可以查看當前目錄下的文件夾及文件。 2、安裝 2.1、下載anaconda 通過wget和curl下載未成功&#xff0c;使用手動…

Python 與 Java 在 Web 開發中的深度對比:從語言特性到生態選型

在 Web 開發領域&#xff0c;Python 和 Java 作為兩大主流技術棧&#xff0c;始終是開發者技術選型時的核心考量。本文將從語言本質、框架生態、性能工程、工程實踐等多個維度展開深度對比&#xff0c;結合具體技術場景解析兩者的適用邊界與融合方案&#xff0c;為開發者提供系…

【OpenGL學習】(一)創建窗口

文章目錄 【OpenGL學習】&#xff08;一&#xff09;創建窗口 【OpenGL學習】&#xff08;一&#xff09;創建窗口 GLFW OpenGL 本身只是一套圖形渲染 API&#xff0c;不提供窗口創建、上下文管理或輸入處理的功能。 GLFW 是一個支持創建窗口、處理鍵盤鼠標輸入和管理 OpenGL…

電腦閃屏可能的原因

1. 顯示器 / 屏幕故障 屏幕排線接觸不良&#xff1a;筆記本電腦屏幕排線&#xff08;屏線&#xff09;松動或磨損&#xff0c;導致信號傳輸不穩定&#xff0c;常見于頻繁開合屏幕的設備。屏幕面板損壞&#xff1a;液晶屏內部燈管老化、背光模塊故障或面板本身損壞&#xff0c;…

docker容器知識

一、docker與docker compose區別&#xff1a; 1、docker是創建和管理單個容器的工具&#xff0c;適合簡單的應用或服務&#xff1b; 2、docker compose是管理多容器應用的工具&#xff0c;適合復雜的、多服務的應用程序&#xff1b; 3、docker與docker compose對比&#xff…

什么是Rootfs

Rootfs (Root Filesystem) 詳解 buildroot工具構建了一個名為"rootfs.tar"的根文件系統壓縮包。 什么是rootfs Rootfs&#xff08;Root Filesystem&#xff0c;根文件系統&#xff09;是操作系統啟動后掛載的第一個文件系統&#xff0c;它包含系統正常運行所需的基…

關于NLP自然語言處理的簡單總結

參考&#xff1a; 什么是自然語言處理&#xff1f;看這篇文章就夠了&#xff01; - 知乎 (zhihu.com) 所謂自然語言理解&#xff0c;就是研究如何讓機器能夠理解我們人類的語言并給出一些回應。 自然語言處理&#xff08;Natural Language Processing&#xff0c;NLP&#xff0…

Linux下載國外軟件鏡像的加速方法(以下載Python-3.8.0.tgz為例)

0 前言 使用linux經常會通過國外服務器下載軟件鏡像&#xff0c;有些軟件的下載速度奇慢&#xff0c;本文介紹一種加速國外軟件鏡像下載速度的方法&#xff0c;需要準備下載工具&#xff1a;迅雷。 1 以下載Python-3.8.0.tgz為例 找到Python官網的Python-3.8.0.tgz鏡像下載地…

沒有公網ip怎么端口映射外網訪問?使用內網穿透可以解決

無公網IP時本地搭建的網絡端口服務怎么映射外網遠程訪問&#xff1f;較為簡單通用的方案就是使用nat123內網穿透&#xff0c;下面詳細內網映射外網實現教程。? 一、了解內網公網區別&#xff0c;及無公網IP外網訪問方案 內網IP默認只能在同局域網內連接互通&#xff0c;而公…

Word2Vec詳解

目錄 Word2Vec 一、Word2Vec 模型架構 &#xff08;一&#xff09;Word2Vec 的核心理念 &#xff08;二&#xff09;Word2Vec 的兩種架構 &#xff08;三&#xff09;負采樣與層次 Softmax &#xff08;四&#xff09;Word2Vec 的優勢與局限 二、Word2Vec 預訓練及數據集…

ShardingSphere:查詢報錯:Actual table `數據源名稱.表名` is not in table rule configuration

目錄 簡介異常信息排查原因解決 簡介 1、使用ShardingSphere框架&#xff0c;版本為5.2.1 <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>shardingsphere-jdbc-core</artifactId><version>5.2.1</version>…

MongoDB聚合查詢:從入門到精通

文章目錄 前言一、工具一般聚合查詢分為四步 二、使用步驟1.MongoDB Compass2.Studio 3T 二、舉個栗子總結 前言 Mongo 聚合查詢 一般用mongo做數據庫,涉及到關聯查詢情況不多,但是還有些情況要使用到,今天就講下如何通過工具做關聯查詢,最終聚合結果,得到最終的查詢結果集; …

codeup添加流水線docker自動化部署

在項目根目錄下增加Dockerfile文件 # 使用基礎鏡像 FROM maven:3.8.4-openjdk-17-slim AS build # 設置工作目錄 WORKDIR /app # 復制項目源代碼 COPY . . # 構建項目 RUN mvn clean package -DskipTests # 驗證JAR包是否生成 RUN ls -l target/your-project.jar # 使用合適的…

從 Word2Vec 到 BERT:AI 不止是詞向量,更是語言理解

一、前言 在上篇文章中&#xff0c;我們介紹了Word2Vec以及它的作用&#xff0c;總的來說&#xff1a; Word2Vec是我們理解NLP的第一站 Word2Vec將詞變成了“向量”—— 終于可以用機器理解詞語的相似度 我們獲得了例如“國王 - 男人 女人 ≈ 女王” 的類比能力 我們可以將…

鏡像管理(2)Dockerfile總結

一、docker鏡像構建方法 commoit :使用 docker commit 意味著所有對鏡像的操作都是黑箱操作,生成的鏡像也被稱為黑 箱鏡像,換句話說,就是除了制作鏡像的人知道執行過什么命令、怎么生成的鏡像,別人根 本無從得知。而且,即使是這個制作鏡像的人,過一段時間后也無法記清具…