Irbisa · Шпаргалка
Локальное хранилище
SharedPreferences, sqflite, Hive, flutter_secure_storage
Когда использовать
SharedPreferences, sqflite, Hive иflutter_secure_storage?Как открыть базу sqflite и выполнить базовый CRUD?
Future<Database> openAppDb() async => openDatabase( join(await getDatabasesPath(), 'app.db'), // join from package:path version: 1, onCreate: (db, version) => db.execute( 'CREATE TABLE notes(id INTEGER PRIMARY KEY AUTOINCREMENT, ' 'title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0)',…Какие типичные ловушки SharedPreferences и как их избегать?
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); // Load once before the first frame, so the UI never flashes a default theme. final prefs = await SharedPreferencesWithCache.create( cacheOptions: const SharedPreferencesWithCacheOptions( allowList: {'theme', 'filters'},…Что такое
path_providerи в какую директорию класть файл: documents, application support, temporary или cache?Future<void> saveFiles(Note note, List<int> thumbBytes) async { // user content: backed up, the user would miss it final docs = await getApplicationDocumentsDirectory(); await File(p.join(docs.path, '${note.id}.json')) .writeAsString(jsonEncode(note.toJson()));…Вставка в sqflite падает, как только вы передаёте
DateTime: как правильно хранить и читать даты, bool и enum?enum Priority { low, normal, high } class Task { Task({this.id, required this.title, required this.done, required this.dueAt, required this.priority}); final int? id;…Чем отличаются legacy
SharedPreferences,SharedPreferencesAsyncиSharedPreferencesWithCache?import 'package:shared_preferences/shared_preferences.dart';import 'package:shared_preferences/util/legacy_to_async_migration_util.dart'; Future<SharedPreferencesWithCache> initPrefs() async { const options = SharedPreferencesOptions(); // One-time copy of keys the legacy API wrote (a different backend on Android).…Нужно посмотреть, что приложение реально записало на диск тестового устройства: где лежат эти файлы и чем их открыть?
# Android, debuggable build: copy the database together with its WAL fileadb exec-out run-as com.example.app cat databases/app.db > app.dbadb exec-out run-as com.example.app cat databases/app.db-wal > app.db-wal # Preferences written by the legacy SharedPreferences APIadb exec-out run-as com.example.app cat shared_prefs/FlutterSharedPreferences.xml…Как обрабатывать миграции схемы в sqflite?
Future<Database> openAppDb() async => openDatabase( join(await getDatabasesPath(), 'app.db'), version: 3, // Fresh install: create the latest schema directly. onCreate: (db, version) => db.execute( 'CREATE TABLE tasks(id INTEGER PRIMARY KEY, title TEXT NOT NULL, '…Релиз 2 добавляет колонку
due_at. У обновившихся всё в порядке, а чистые установки падают сno such column: due_at. Почему?Future<Database> openDb(String path) => openDatabase( path, version: 2, onCreate: (db, version) async { await db.execute( 'CREATE TABLE tasks(id INTEGER PRIMARY KEY, title TEXT NOT NULL)'); }, onUpgrade: (db, oldVersion, newVersion) async { if (oldVersion < 2) { await db.execute('ALTER TABLE tasks ADD COLUMN due_at INTEGER'); } },);Как работают type adapters и шифрование box-ов в Hive?
@HiveType(typeId: 1)class User extends HiveObject { User({required this.name, required this.email}); @HiveField(0) String name;…Как
flutter_secure_storageна самом деле защищает данные на iOS и на Android?Когда выбрать Drift вместо raw sqflite?
class Tasks extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get title => text().withLength(min: 1, max: 200)(); BoolColumn get done => boolean().withDefault(const Constant(false))(); DateTimeColumn get dueAt => dateTime().nullable()();}…Что такое Isar и ObjectBox и когда они побеждают sqflite?
Эта транзакция sqflite никогда не завершается, а в логе предупреждение, что база заблокирована: что не так?
Future<void> moveTask(Database db, int taskId, int listId) async { await db.transaction((txn) async { await txn.update('tasks', {'list_id': listId}, where: 'id = ?', whereArgs: [taskId]); final count = Sqflite.firstIntValue(await db.rawQuery( 'SELECT COUNT(*) FROM tasks WHERE list_id = ?', [listId])); await txn.update('lists', {'task_count': count}, where: 'id = ?', whereArgs: [listId]); });}Найдите баг: этот
NotesDaoпроходит юнит-тесты, но на экране, который одновременно грузит и сохраняет данные, часть вызовов падает сDatabaseException.class NotesDao { NotesDao(this.path); final String path; Future<T> _use<T>(Future<T> Function(Database db) body) async { final db = await openDatabase(path); try { return await body(db); } finally { await db.close(); } } Future<List<Map<String, Object?>>> all() => _use((db) => db.query('notes')); Future<int> add(String text) => _use((db) => db.insert('notes', {'text': text}));}Импорт JSON-выгрузки на 20 МБ подвешивает интерфейс на четыре секунды, хотя каждый вызов хранилища идёт через await: куда на самом деле уходит время?
// wrong: jsonDecode blocks the UI isolate, plus one hop per rowFuture<void> importSlow(Database db, String json) async { final rows = (jsonDecode(json) as List).cast<Map<String, Object?>>(); for (final row in rows) { await db.insert('items', row); }…Пользователи жалуются, что приложение съедает гигабайты места на диске. Как понять, куда она ушла, и удержать её в рамках?
Future<Map<String, int>> measureStorage() async { final dirs = { 'documents': await getApplicationDocumentsDirectory(), 'support': await getApplicationSupportDirectory(), 'cache': await getApplicationCacheDirectory(), };…Список, построенный из sqflite, не обновляется, когда фоновая синхронизация пишет новые строки: как сделать локальное хранилище реактивным без поллинга?
class TaskRepository { TaskRepository(this._db); final Database _db; final _changes = StreamController<List<Task>>.broadcast(); Stream<List<Task>> watchOpen() async* {…Что нужно стереть при выходе пользователя из аккаунта и о чём обычно забывают?
Что идёт не так при конкурентной записи в локальную БД и как этого избежать?
// Optimistic: the update lands only if nobody changed the row since we read it.Future<bool> rename(Database db, int id, String title, int readRevision) async { final changed = await db.update( 'tasks', {'title': title, 'revision': readRevision + 1}, where: 'id = ? AND revision = ?',…Запрос в sqflite был мгновенным на 500 строках и занимает две секунды на 50 000. Как диагностировать и починить?
// 1. Ask SQLite what it does with the slow query.final plan = await db.rawQuery( 'EXPLAIN QUERY PLAN SELECT * FROM orders ' 'WHERE user_id = ? ORDER BY created_at DESC LIMIT 20', [userId],);…Пользователи, поднявшие новый телефон из бэкапа, жалуются, что приложение открывается пустым или падает на базе: что сделал восстановленный бэкап?
<!-- res/xml/data_extraction_rules.xml (Android 12+), referenced by android:dataExtractionRules in AndroidManifest.xml. Android 11 and lower read android:fullBackupContent instead. --><data-extraction-rules> <cloud-backup> <!-- Encrypted with a Keystore key that never leaves the device -->…Пакет key-value хранилища, на котором построено приложение, заброшен: как перевезти локальные данные уже выпущенного приложения в новое хранилище и ничего не потерять?
Фоновый обработчик FCM увеличивает счётчик в SharedPreferences и Hive, но интерфейс видит новое значение только после перезапуска: почему?
@pragma('vm:entry-point')Future<void> onBackgroundMessage(RemoteMessage message) async { // wrong: the UI isolate cached its map at launch and never sees this write // final prefs = await SharedPreferences.getInstance(); // right: no Dart-side cache, every call goes to the platform store…В Crashlytics ровным ручейком идут ошибки «database disk image is malformed»: как найти причину и вернуть этих пользователей в рабочее состояние?