先來前提回顧一下:
在上一篇,筆者分享了如何解決Future
與non-Future
這兩種不同類別物件的存取,在花了一點時間後,終於利用FutureBuilder
解決了這個問題!本篇中將會先主要談論另一個問題(怎麼這麼多問題呀......),而想看關於FutureBuilder
的讀者可以直接滑到下方的FutureBuilder該如何使用?
tldr
sqflite目前僅支援Android/iOS/MacOS的系統,sqflite_common_ffi增加Windows/Linux/> > DartVM/flutter的支援功能。
今日筆者在測試程式時,跳出了以下問題通知:
You must call `databaseFactory = databaseFactoryFfi;` before using global openDatabase API
所以,像所有遇到bug的人,理所當然的我就將這段文字複製後貼上Google做搜尋,而後便看到這個人給出的答案:
不知看到這裡的讀者是否會與筆者產生相同的問題:「這個package是什麼?為何要用它?」
而後筆者在尋找sqflite_common_ffi說明時則看到了這篇官方文件:Using sqflite_ffi instead of sqflite
想做資料庫的基礎編輯與操作,需要使用到DatabaseFactory
這一個類別,而sqflite理想中能夠直接提供一個該類別的物件databaseFactoryFFi
,但目前這個package還沒有完全製作好,仍有一些不足的地方,因此要使用sqflite_common_ffi
來輔助部份作業系統。
// pubspec.yaml
dependencies:
sqflite_common_ffi: [version]
sqlite3_flutter_libs: [version] // for ios/android/macos
其中,sqflite已經包含在sqflite_common_ffi之中,因此可將其從dependencies中移除
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future main() async {
if (Platform.isWindows || Platform.isLinux) {
// Initialize FFI
sqfliteFfiInit();
}
// Change the default factory. On iOS/Android, if not using `sqlite_flutter_lib` you can forget
// this step, it will use the sqlite version available on the system.
databaseFactory = databaseFactoryFfi;
runApp(MyApp());
}
sqfliteFfiInit()
是用於方便開發而使用的函式,我們可以將此部份自行做更動以自訂設定內容(尋找與讀取sqlite的shared library)openDatabase()
一起使用的getDatabasesPath()
目前與ffi一同使用時並不穩定,可能會出現bug,建議使用其他方式獲得資料庫路徑,如path_provider
以下為筆者目前於Todo頁面所編寫的內容:
import 'package:flutter/material.dart';
import 'package:schedrag/data/models/child_blocks.dart';
class TodoPage extends StatefulWidget {
final TextStyle optionStyle;
const TodoPage(this.optionStyle, {super.key});
@override
State<TodoPage> createState() => _TodoPageState();
}
class _TodoPageState extends State<TodoPage> {
final TimeBlocksDb db = TimeBlocksDb();
late Future<List<TimeBlock>?> dbList;
int num = 0;
@override
void initState() {
dbList = db.getAll();
super.initState();
}
@override
Widget build(context) {
return Scaffold(
body: FutureBuilder<List<TimeBlock>?>(
builder: (con, snap) {
if (snap.hasData) {
return ListView.builder(
itemCount: snap.data!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snap.data![index].name.toString()),
onTap: () {});
},
);
} else {
return const Center(
child: Text("There isn't any todo being created yet."));
}
},
future: dbList,
),
floatingActionButton: FloatingActionButton(
// add new timeblock
onPressed: () {
db.insert(TimeBlock.name('name_$num'));
print('name_$num\n');
num++;
},
child: const Icon(Icons.add),
),
);
}
}
可以看到該頁面主要是由FutureBuilder
作為body的widget所組成,且FutureBuilder
主要包含以下兩樣內容:future
與builder
。
future
:將要存取資料的變數這一欄將會放一個物件類別為Future<[ObjectType]?>
的變數,其中,中括號內的資料類別與上方的FutureBuilder<[ObjectType]>
中相同。
late Future<List<TimeBlock>?> dbList;
...
body: FutureBuilder<List<TimeBlock>?>(
builder: ...
future: dbList,
),
builder
:欲建立的內容
上方圖片中有標記部份參數的資料型態。可以看出,builder一欄的物件為:Widget? function(BuildContext, AsyncSnapshot<[ObjectType]?>)
,一個回傳null或widget的函式,並能夠有一個snapshot的變數,作為之後存取future欄位資料的媒介。
關於AsyncSnapshot
說明文件請點此。
程式中利用該類別的properties: hasData
來看是否有獲取資料、data
來得到從future中最新獲得的資料。
謝謝讀到這裡的人,因為目前程式中仍有著bug,因此目前將不在此放上其他有做更動的部份的程式碼。至於nnyjan02426/Schedrag將會持續同步更新,有興趣的人可至那裡看到詳細內容。
如果有任何想說的內容,都可以留言或是email~
email: nnyjan02426@gmail.com
github: nnyjan02426