Flutter Android login vault: local DB (id+base64 content), email/Google auth, dashboard search/edit/swipe-delete, dark/system theme, minSdk 29
This commit is contained in:
97
lib/database_helper.dart
Normal file
97
lib/database_helper.dart
Normal file
@@ -0,0 +1,97 @@
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:login_vault_app/constants.dart';
|
||||
import 'package:login_vault_app/encryptor.dart';
|
||||
|
||||
/// A single saved login/private entry.
|
||||
class VaultEntry {
|
||||
final String id; // unique string
|
||||
final String content; // decrypted plaintext
|
||||
|
||||
VaultEntry({required this.id, required this.content});
|
||||
|
||||
/// Build from a DB row (content is stored encrypted).
|
||||
factory VaultEntry.fromDbRow(Map<String, dynamic> row) => VaultEntry(
|
||||
id: row['id'] as String,
|
||||
content: Encryptor.decrypt(row['content'] as String),
|
||||
);
|
||||
|
||||
/// Build a DB row map (content encrypted).
|
||||
Map<String, dynamic> toDbRow() => {
|
||||
'id': id,
|
||||
'content': Encryptor.encrypt(content),
|
||||
};
|
||||
}
|
||||
|
||||
class DatabaseHelper {
|
||||
static final DatabaseHelper _instance = DatabaseHelper._internal();
|
||||
factory DatabaseHelper() => _instance;
|
||||
DatabaseHelper._internal();
|
||||
|
||||
Database? _db;
|
||||
|
||||
Future<Database> get db async {
|
||||
_db ??= await _initDb();
|
||||
return _db!;
|
||||
}
|
||||
|
||||
Future<Database> _initDb() async {
|
||||
final path = join(await getDatabasesPath(), AppConstants.dbName);
|
||||
return openDatabase(
|
||||
path,
|
||||
version: 1,
|
||||
onCreate: (db, version) async {
|
||||
await db.execute('''
|
||||
CREATE TABLE vault (
|
||||
id TEXT PRIMARY KEY,
|
||||
content TEXT NOT NULL
|
||||
)
|
||||
''');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Insert a new entry (throws if [id] already exists -> uniqueness).
|
||||
Future<void> insert(VaultEntry entry) async {
|
||||
final d = await db;
|
||||
await d.insert('vault', entry.toDbRow(),
|
||||
conflictAlgorithm: ConflictAlgorithm.fail);
|
||||
}
|
||||
|
||||
/// Update content for an existing [id].
|
||||
Future<void> update(String id, String newContent) async {
|
||||
final d = await db;
|
||||
await d.update(
|
||||
'vault',
|
||||
{'content': Encryptor.encrypt(newContent)},
|
||||
where: 'id = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
}
|
||||
|
||||
/// Delete by [id].
|
||||
Future<void> delete(String id) async {
|
||||
final d = await db;
|
||||
await d.delete('vault', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
/// Get all entries (used for full list / search).
|
||||
Future<List<VaultEntry>> getAll() async {
|
||||
final d = await db;
|
||||
final rows = await d.query('vault', orderBy: 'id COLLATE NOCASE ASC');
|
||||
return rows.map(VaultEntry.fromDbRow).toList();
|
||||
}
|
||||
|
||||
/// Search ids containing [query] (case-insensitive). Returns matching entries.
|
||||
Future<List<VaultEntry>> search(String query) async {
|
||||
if (query.trim().isEmpty) return getAll();
|
||||
final d = await db;
|
||||
final rows = await d.query(
|
||||
'vault',
|
||||
where: 'id LIKE ?',
|
||||
whereArgs: ['%${query.trim()}%'],
|
||||
orderBy: 'id COLLATE NOCASE ASC',
|
||||
);
|
||||
return rows.map(VaultEntry.fromDbRow).toList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user