Flutter本地數據持久化的幾種方式

目錄

前言

一、shared_preferences

1.添加依賴

2.保存數據

3.讀取數據

4.移除數據

5.Shared_preferences的優缺點

6.完整的示例代碼

二、path_provider

1.導入path_provider

2.創建文件讀寫的目錄

3.向文件中寫入數據

4.從文件中讀取數據

5.完整的示例代碼

三、sqlite數據庫

1.導入sqlite

2.創建數據庫助手類

3.完整示例代碼

四、參考博客


前言

? ? ? ? 這篇文章主要介紹下Flutter中本地數據持久化的幾種方式。

一、shared_preferences

????????如果你要存儲的鍵值集合相對較少,則可以用?shared_preferences?插件。

? ? ? ? 當我們要存儲一些簡單的數據,例如app的系統設置,一些簡單的用戶信息等,可以考慮使用shared_preferences插件。

? ? ? ? 我們以一個切換主題的Demo為例,看一下shared_preferences的用法。

1.添加依賴

????????pubspec.yaml

? ? ? ? pubspec.yaml文件中添加shared_preferences依賴。

? ? ? ? 終端運行pub get命令,安裝shared_preferences插件。

pub get

2.保存數據

? ? ? ? Flutter中我們通過ThemeMode對象獲取當前的主題模式。它是一個枚舉類型,有system,light,dark三個主題。

????????要存儲數據,請使用?SharedPreferences?類的 setter 方法。 Setter方法可用于各種基本數據類型,例如?setIntsetBool?和?setString

????????Setter 方法做兩件事:首先,同步更新 key-value 到內存中,然后保存到磁盤中。

? ? ? ? 在使用shared_preferences保存當前主題的時候,首先創建一個SharedPreferences對象,然后使用一個bool值表示當前的主題類型,調用setBool方法把當前的主題保存到內存中。

  Future<void> _toggleTheme() async {SharedPreferences prefs = await SharedPreferences.getInstance();bool isDarkMode = _themeMode == ThemeMode.dark;await prefs.setBool('isDarkMode', !isDarkMode);setState(() {_themeMode = !isDarkMode ? ThemeMode.dark : ThemeMode.light;});}

3.讀取數據

? ? ? ? 當App啟動的時候,我們調用SharedPreferences對象的get方法獲取上次保存的主題。

????????要讀取數據,請使用?SharedPreferences?類相應的 getter 方法。對于每一個 setter 方法都有對應的 getter 方法。例如,你可以使用?getIntgetBool?和?getString?方法。

? ? ? ? 在我們的例子中,我們調用getBool方法獲取上次保存的主題。

  Future<void> _loadTheme() async {SharedPreferences prefs = await SharedPreferences.getInstance();bool isDarkMode = prefs.getBool('isDarkMode') ?? false;setState(() {_themeMode = isDarkMode ? ThemeMode.dark : ThemeMode.light;});}

4.移除數據

? ? ? ? 上面的兩個方法是SharedPreferences常用的兩個方法,當然有時候我們還需要清空保存在內存中的信息,這個時候我們可以調用對象的remove方法,移除保存在內存中的key-value鍵值對。

final prefs = await SharedPreferences.getInstance();// 移除某個值
await prefs.remove('counter');

5.Shared_preferences的優缺點

? ? ? ? shared_preferences的優點就是通過鍵值對的方式存取數據簡單方便,但是也有以下的局限性:

  1. 只能用于基本數據類型:?intdoubleboolstring?和?List<String>
  2. 不是為存儲大量數據而設計的。
  3. 不能確保應用重啟后數據仍然存在。

6.完整的示例代碼

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';void main() {runApp(const MyApp());
}class MyApp extends StatefulWidget {const MyApp({super.key});@overrideMyAppState createState() => MyAppState();
}class MyAppState extends State<MyApp> {ThemeMode _themeMode = ThemeMode.light;@overridevoid initState() {super.initState();_loadTheme();}Future<void> _loadTheme() async {SharedPreferences prefs = await SharedPreferences.getInstance();bool isDarkMode = prefs.getBool('isDarkMode') ?? false;setState(() {_themeMode = isDarkMode ? ThemeMode.dark : ThemeMode.light;});}Future<void> _toggleTheme() async {SharedPreferences prefs = await SharedPreferences.getInstance();bool isDarkMode = _themeMode == ThemeMode.dark;await prefs.setBool('isDarkMode', !isDarkMode);setState(() {_themeMode = !isDarkMode ? ThemeMode.dark : ThemeMode.light;});}@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Theme Demo',theme: ThemeData.light(),darkTheme: ThemeData.dark(),themeMode: _themeMode,home: MyHomePage(themeMode: _themeMode,onThemeChanged: _toggleTheme,),);}
}class MyHomePage extends StatelessWidget {final ThemeMode themeMode;final VoidCallback onThemeChanged;const MyHomePage({super.key, required this.themeMode, required this.onThemeChanged});@overrideWidget build(BuildContext context) {bool isDarkMode = themeMode == ThemeMode.dark;return Scaffold(appBar: AppBar(title: const Text('Theme Demo'),),body: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Center(child: Text(isDarkMode ? "當前模式:暗黑模式" : "當前模式:白天模式",style: const TextStyle(fontSize: 24),),),const SizedBox(height: 20),ElevatedButton(onPressed: onThemeChanged,child: const Text('切換當前模式',style: TextStyle(fontSize: 18),),),],),);}
}

二、path_provider

? ? ? ? 有時候我們需要把一些數據以文件的形式保存到本地,這個時候path_provider就派上用場了。

? ? ? ? path_provider提供一種平臺無關的方式以一致的方式訪問設備的文件位置系統。該 plugin 當前支持訪問兩種文件位置系統:

????????磁盤文件的讀寫操作可能會相對方便地實現某些業務場景。它常見于應用啟動期間產生的持久化數據,或者從網絡下載數據供離線使用。

????????臨時文件夾:?這是一個系統可以隨時清空的臨時(緩存)文件夾。在 iOS 上對應?NSCachesDirectory?的返回值;在 Android 上對應?getCacheDir()?的返回值。

????????Documents 目錄:供應用使用,用于存儲只能由該應用訪問的文件。只有在刪除應用時,系統才會清除這個目錄。在 iOS 上,這個目錄對應于?NSDocumentDirectory。在 Android 上,則是?AppData?目錄。

? ? ? ? 我們以計數器為例,當我們點擊計時器的時候,把當前的計時器的值保存到文件中。

? ? ? ? 看看如何實現這個實例:

1.導入path_provider

path_provider: ^2.1.3

2.創建文件讀寫的目錄

? ? ? ? 以Document目錄為例,首先我們確認文件的目錄:

import 'package:path_provider/path_provider.dart';
// ···Future<String> get _localPath async {final directory = await getApplicationDocumentsDirectory();return directory.path;}

然后創建這個文件目錄位置的引用:

Future<File> get _localFile async {final path = await _localPath;return File('$path/counter.txt');
}

3.向文件中寫入數據

? ? ? ? 當我們點擊按鈕之后,把點擊次數的值轉成字符串保存到文件中即可。

Future<File> writeCounter(int counter) async {final file = await _localFile;// Write the filereturn file.writeAsString('$counter');
}

4.從文件中讀取數據

? ? ? ? 我們使用File類獲取文件中的數據。

Future<int> readCounter() async {try {final file = await _localFile;// Read the filefinal contents = await file.readAsString();return int.parse(contents);} catch (e) {// If encountering an error, return 0return 0;}
}

5.完整的示例代碼

import 'dart:async';
import 'dart:io';import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';void main() {runApp(MaterialApp(title: 'Reading and Writing Files',home: FlutterDemo(storage: CounterStorage()),),);
}class CounterStorage {Future<String> get _localPath async {final directory = await getApplicationDocumentsDirectory();return directory.path;}Future<File> get _localFile async {final path = await _localPath;return File('$path/counter.txt');}Future<int> readCounter() async {try {final file = await _localFile;// Read the filefinal contents = await file.readAsString();return int.parse(contents);} catch (e) {// If encountering an error, return 0return 0;}}Future<File> writeCounter(int counter) async {final file = await _localFile;// Write the filereturn file.writeAsString('$counter');}
}class FlutterDemo extends StatefulWidget {const FlutterDemo({super.key, required this.storage});final CounterStorage storage;@overrideState<FlutterDemo> createState() => _FlutterDemoState();
}class _FlutterDemoState extends State<FlutterDemo> {int _counter = 0;@overridevoid initState() {super.initState();widget.storage.readCounter().then((value) {setState(() {_counter = value;});});}Future<File> _incrementCounter() {setState(() {_counter++;});// Write the variable as a string to the file.return widget.storage.writeCounter(_counter);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Reading and Writing Files'),),body: Center(child: Text('Button tapped $_counter time${_counter == 1 ? '' : 's'}.',),),floatingActionButton: FloatingActionButton(onPressed: _incrementCounter,tooltip: 'Increment',child: const Icon(Icons.add),),);}
}

三、sqlite數據庫

????????如果你正在編寫一個需要持久化且查詢大量本地設備數據的 app,可考慮采用數據庫,而不是本地文件夾或關鍵值庫。總的來說,相比于其他本地持久化方案來說,數據庫能夠提供更為迅速的插入、更新、查詢功能。

? ? ? ? 這個熟悉數據庫的同學們對這個應該比較熟悉。我們以一個demos為例,看一下sqlite的用法

1.導入sqlite

? ? ? ? 和另外兩種方式的導入方式差不多,我們在yaml中配置sqlite和path_provider.

path_provider: ^2.1.3

sqflite: ^2.3.3+1

2.創建數據庫助手類

? ? ? ? 這一步,我們創建一個名為database_helper.dart的文件,用于管理數據庫的創建和操作:

import 'dart:async';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';class DatabaseHelper {static final DatabaseHelper _instance = DatabaseHelper._internal();factory DatabaseHelper() => _instance;static Database? _database;DatabaseHelper._internal();Future<Database> get database async {if (_database != null) return _database!;_database = await _initDatabase();return _database!;}Future<Database> _initDatabase() async {String path = join(await getDatabasesPath(), 'demo.db');return await openDatabase(path,version: 1,onCreate: _onCreate,);}Future _onCreate(Database db, int version) async {await db.execute('''CREATE TABLE items (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT)''');}Future<int> insertItem(String name) async {Database db = await database;return await db.insert('items', {'name': name});}Future<List<Map<String, dynamic>>> getItems() async {Database db = await database;return await db.query('items');}Future<int> deleteItem(int id) async {Database db = await database;return await db.delete('items', where: 'id = ?', whereArgs: [id]);}
}

3.完整示例代碼

? ? ? ? 我們在UI文件中,使用數據庫管理類進行數據庫的增刪改查操作:

import 'package:flutter/material.dart';
import 'database_helper.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'SQLite Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const MyHomePage(),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key});@override_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {final DatabaseHelper _dbHelper = DatabaseHelper();final TextEditingController _controller = TextEditingController();late Future<List<Map<String, dynamic>>> _items;@overridevoid initState() {super.initState();_refreshItems();}void _refreshItems() {setState(() {_items = _dbHelper.getItems();});}void _addItem() async {if (_controller.text.isNotEmpty) {await _dbHelper.insertItem(_controller.text);_controller.clear();_refreshItems();}}void _deleteItem(int id) async {await _dbHelper.deleteItem(id);_refreshItems();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('SQLite Demo'),),body: Column(children: <Widget>[Padding(padding: const EdgeInsets.all(8.0),child: TextField(controller: _controller,decoration: const InputDecoration(labelText: 'Item name',border: OutlineInputBorder(),),),),ElevatedButton(onPressed: _addItem,child: const Text('Add Item'),),Expanded(child: FutureBuilder<List<Map<String, dynamic>>>(future: _items,builder: (context, snapshot) {if (snapshot.connectionState == ConnectionState.waiting) {return const Center(child: CircularProgressIndicator());} else if (snapshot.hasError) {return const Center(child: Text('Error'));} else if (!snapshot.hasData || snapshot.data!.isEmpty) {return const Center(child: Text('No items'));} else {return ListView.builder(itemCount: snapshot.data!.length,itemBuilder: (context, index) {final item = snapshot.data![index];return ListTile(title: Text(item['name']),trailing: IconButton(icon: const Icon(Icons.delete),onPressed: () => _deleteItem(item['id']),),);},);}},),),],),);}
}

四、參考博客

1.shared_preferences

2.sqflite

3.path_provider

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

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

相關文章

Mac本地部署大模型-單機運行

前些天在一臺linux服務器&#xff08;8核&#xff0c;32G內存&#xff0c;無顯卡&#xff09;使用ollama運行阿里通義千問Qwen1.5和Qwen2.0低參數版本大模型&#xff0c;Qwen2-1.5B可以運行&#xff0c;但是推理速度有些慢。 一直還沒有嘗試在macbook上運行測試大模型&#xf…

我這個經驗好找嵌入式的工作嗎?

大家好&#xff0c;我是麥鴿。最近網友的提問&#xff0c;這樣的經驗&#xff0c;好找嵌入式的工作嗎&#xff1f; 下面是網友的情況&#xff1a; 本人目前大二機器人工程&#xff0c;未來想要入職嵌入式行業&#xff0c;有robomaster比賽經驗本人負責電控&#xff0c;但是由于…

基因組學系列3:基因分型Phasing與單倍型參考序列HRC

1. 基因分型Phasing概念 基因分型&#xff0c;也稱為基因定相、單倍體分型、單倍體構建等&#xff0c;即將一個二倍體&#xff08;或多倍體&#xff09;基因組上的等位基因&#xff08;或雜合位點&#xff09;正確定位到父親或母親的染色體上&#xff0c;最終使得來自同一親本…

相親交友APP系統婚戀交友社交軟件開發語音視頻聊天平臺定制開發-婚戀相親交友軟件平臺介紹——app小程序開發定制

互聯網飛速發展的時代&#xff0c;相親交友軟件成為了許多年輕人首選的相親方式&#xff0c;越來越多的單身男女希望在婚戀交友軟件平臺上尋找靈魂伴侶&#xff0c;相親交友軟件因此具有很高的市場價值。 多客婚戀相親交友系統是一款定位高端&#xff0c;到手就能運營的成熟婚戀…

軟件測評中心▏軟件驗收測試方法和測試內容簡析

在當今數字化轉型的浪潮下&#xff0c;軟件驗收測試變得越來越重要。軟件驗收測試&#xff0c;顧名思義&#xff0c;是對軟件進行驗收的過程中進行的一項測試。它用于確保軟件在滿足需求、達到預期效果后才能正式交付給客戶使用。軟件驗收測試是一項全面、系統的測試過程&#…

sublime 3 背景和字體顏色修改

sublime 4 突然抽風&#xff0c;每次打開都顯示 “plugin_host-3.3 has exited unexpectedly, some plugin functionality won’t be available until Sublime Text has been restarted” 一直沒調好&#xff0c;所以我退回到sublime 3了。下載好了軟件沒問題&#xff0c;但是一…

半導體光電

《半導體光電》創刊于1976年&#xff0c;是由中國電子科技集團公司主管、重慶光電技術研究所&#xff08;中國電子科技集團公司第四十四研究所&#xff09;主辦的中文科技期刊。本刊國內外公開發行&#xff0c;經過四十余年的發展已經成為我國光電子專業領域有代表性的刊物。 …

Zabbix 配置grafana對接

zabbix對接grafana簡介 Zabbix與Grafana對接可以實現更加豐富和美觀的數據可視化&#xff0c;可以讓您利用Grafana強大的可視化功能來展示Zabbix收集的數據。 zabbix插件的兩種安裝方式 使用grafana-cli 命令進行安裝在grafana管理頁面中進入Administration/Plugins and dat…

2024.7.4學習日報

1、ppt前三章 5日計劃 1、至少做到實驗 2、java

css中文字書寫方向

writing-mode 是 CSS 中的一個屬性&#xff0c;用于設置文本、內聯元素、表格單元格和表格列的書寫方向、文本排列以及塊流方向。以下是對 writing-mode 屬性的詳細介紹&#xff1a; 1. 語法和值 語法&#xff1a;writing-mode: horizontal-tb | vertical-rl | vertical-lr |…

在RT-Thread-Studio中添加arm_math庫

1.在CMSIS\Lib\GCC中找到對應的庫&#xff0c;如本文使用的libarm_cortexM4lf_math.a。將庫拷貝到工程&#xff0c;并做如下圖設置。搜索路徑為庫文件在項目中的實際位置。 2.將CMSIS\DSP\Include下的文件復制到工程目錄中&#xff0c;并添加包含路徑 3.添加宏定義&#xff0c…

Memcached緩存預熱深度解析:加速應用性能的秘訣

Memcached緩存預熱深度解析&#xff1a;加速應用性能的秘訣 在高性能計算環境中&#xff0c;Memcached作為一種廣泛使用的分布式內存緩存系統&#xff0c;其緩存預熱機制對于提升應用性能至關重要。緩存預熱可以減少系統啟動時的延遲&#xff0c;避免緩存未命中&#xff0c;從…

2806. 取整購買后的賬戶余額

2806. 取整購買后的賬戶余額 題目鏈接&#xff1a;2806. 取整購買后的賬戶余額 代碼如下&#xff1a; class Solution { public:int accountBalanceAfterPurchase(int purchaseAmount) {return 100-(purchaseAmount5)/10*10;} };

QTreeWidget的簡單使用

使用 QTreeWidget 實現復雜樹控件功能的詳細教程_treewidget 加控件-CSDN博客 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QTreeWidget> namespace Ui { class MainWindow; }class MainWindow : public QMainWindow {Q_OBJECTpu…

阿里巴巴Arthas分析調優JVM實戰及常量池詳解

目錄 一、阿里巴巴Arthas詳解 Arthas使用場景 Arthas命令 Arthas使用 二、GC日志詳解 如何分析GC日志 CMS G1 GC日志分析工具 三、JVM參數匯總查看命令 四、Class常量池與運行時常量池 字面量 符號引用 五、字符串常量池 字符串常量池的設計思想 三種字符串操作…

墨烯的語言技術棧-C語言基礎-005

在VS的安裝路徑下有一個文件: newcfile.cpp的文件 在VS工程中創建新的.c或者.cpp文件的時候,都是拷貝newcfile.cpp這個文件的! everything工具中 有一個newcfile.cpp 然后打開文件路徑在newcfile.cpp 添加#define _CRT_SECURE_NO_WARNINGS替換即可 五.變量的作用域(局部變量…

freemarker生成pdf,同時pdf插入頁腳,以及數據量大時批量處理

最近公司有個需求&#xff0c;就是想根據一個模板生成一個pdf文檔&#xff0c;當即我就想到了freemarker這個遠古老東西&#xff0c;畢竟freemarker在模板渲染方面還是非常有優勢的。 準備依賴&#xff1a; <dependency><groupId>org.springframework.boot</gr…

【IDEA】maven如何進行文件導入,配置并打包

一&#xff0c;介紹、安裝 1、maven介紹 maven是一個Java世界中&#xff0c;構建工具。 核心功能&#xff1a; (1) 管理依賴&#xff1a; 管理文件運行的順序邏輯依賴關系。對配置文件&#xff0c;進行構建和編譯。其也是在調用jdk&#xff0c;來進行編譯打包工作。 (2) 打…

JavaScript中的原型和原型鏈

一、原型&#xff1a;每個函數都有prototype屬性&#xff0c;稱之為原型&#xff0c;這個屬性也是個對象所以也稱之為原型對象。 1.原型可以放一些屬性和方法&#xff0c;供實例對象使用。 <body><script>const arr new Array(1,3,5,7,6)document.getElementByI…