【Flutter】Isarで初期データ投入を試す
やりたい事
アプリ初回起動時に、必要な初期データなどを事前に登録しておきたい!
※ API経由などではなく、アプリ内に初期データ用のjsonファイルを用意してデータ投入する想定です
試した方法
isar/samples: Sample apps using Isar
↑で、初期データが無い場合、 assets/quotes.json
から読込んで登録を行なっていたので、これにならって進めてみたいと思います。
環境構築
今回は fvm を使用し以下の環境で実施します。
macOS Monterey バージョン12.1 Apple M1
iOS15.0 iPhone 13 シュミレータ
Flutter SDK version: 2.10.3
Dart SDK version: 2.16.1
まずは空のFlutterプロジェクトを作成し、以下isar関連のパッケージを追加します。
dependencies:
isar: 2.2.1
isar_flutter_libs: 2.2.1 # contains the binaries
path_provider: ^2.0.9
dev_dependencies:
isar_generator: 2.2.1
build_runner: any
Spotスキーマ作成
今回は↓のSpotスキーマを作成し、Spotの初期データを登録していきます。
(「事前に世界中の有名なスポットを登録しておく必要がある」 といった、ありそう(?)な前提で進めます。)
import 'package:isar/isar.dart';
part 'spot.g.dart';
()
class Spot {
int? id;
()
late String name;
late double longitude;
late double latitude;
late DateTime createdAt;
late DateTime updatedAt;
}
build_runner
実施し、spot.g.dart
を作成しときます。
$ flutter pub run build_runner build
Isar初期化
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final dir = await getApplicationSupportDirectory();
final isar = await Isar.open(
schemas: [SpotSchema], directory: dir.path, inspector: true);
runApp(const MyApp());
}
※ Isar Inspector で参照する為 inspector: true
を設定してます
Asset内のjsonファイル読み込んでDB書き込み
Future<void> _loadSpots(Isar isar) async {
try {
final bytes = await rootBundle.load('assets/spots.json');
final jsonStr = const Utf8Decoder().convert(bytes.buffer.asUint8List());
final json = jsonDecode(jsonStr) as List;
final now = DateTime.now();
final spots = json.map((e) => Spot()
..name = e['name']
..longitude = double.parse(e['longitude'])
..latitude = double.parse(e['latitude'])
..createdAt = now
..updatedAt = now);
isar.writeTxn((isar) async {
await isar.spots.putAll(spots.toList());
});
} catch (e) {
debugPrint(e.toString());
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final dir = await getApplicationSupportDirectory();
final isar = await Isar.open(
schemas: [SpotSchema], directory: dir.path, inspector: true);
await _loadSpots(isar); // ← 追加
runApp(const MyApp());
}
_loadSpots
に関しては Isar sampleのこちらを少しカスタムしたもので、やってる事は同じです。
assets/spots.json
の中身は単純な内容になってます ↓
[
{
"name": "アテネ",
"longitude": "23.718989",
"latitude": "37.973620"
},
{
"name": "イスタンブール",
"longitude": "29.006069",
"latitude": "41.065960"
},
{
"name": "ウィーン",
"longitude": "16.364571",
"latitude": "48.201841"
}
]
pubspec.yaml
に assets/spots.json
を設定します。
flutter:
assets:
- assets/spots.json
いざアプリ起動して、Isar Inspector で登録できているか確認します。
↑ちゃんとjsonで設定したデータが登録されています ✨
importJson
を使う
isarが用意している 実はisarにはjsonファイルからimport出来そうな importJson
, importJsonRaw
, importJsonSync
, importJsonRawSync
が用意されてます。
こちらも試してみたいと思います 👀
先ほどの _loadSpots
を以下に修正します。
Future<void> _loadSpots(Isar isar) async {
try {
final bytes = await rootBundle.load('assets/spots.json');
final jsonStr = const Utf8Decoder().convert(bytes.buffer.asUint8List());
final json = jsonDecode(jsonStr) as List;
final now = DateTime.now().microsecondsSinceEpoch;
final importJson = json
.map((e) => {
'name': e['name'],
'latitude': double.parse(e['latitude']),
'longitude': double.parse(e['longitude']),
'createdAt': now,
'updatedAt': now,
})
.toList();
isar.writeTxn((isar) async {
await isar.spots.importJson(importJson);
});
} catch (e) {
debugPrint(e.toString());
}
}
今回 createdAt
や updatedAt
がある為、importJsonRaw
でダイレクトにインポート出来ない為、importJson
を使用しています。
putAll
で登録する場合とパフォーマンスに違いがあるか比較してみます 👀
試しに 1万件登録してみた所、手元の環境では
-
importJson
だと59msec
-
putAll
だと90msec
でした。大量のデータ登録やダイレクトにインポートできる場合は importJson
の方が良さそうかもです。
既に登録されている場合はスキップ
最後に、既に登録済みの場合は初期データ登録部分をスキップするように実装します。
SharedPreferences
使ってフラグで登録済みか判断したり、登録件数に応じて判断したりと、複数パターンがありそうですが、今回は1件でも登録されていたらスキップする様に実装しました。↓
final isar = await Isar.open(
schemas: [SpotSchema], directory: dir.path, inspector: true);
final exists = await isar.spots.count() > 0;
if (!exists) {
debugPrint('Loading spots...');
await _loadSpots(isar);
}
Discussion