fix: resolve flutter analyze errors - remove leaked code, fix method calls, cleanup imports

This commit is contained in:
2026-03-23 16:55:54 +07:00
parent 4f202936fa
commit 71f1feaf98
33 changed files with 2851 additions and 224 deletions
+45 -15
View File
@@ -1,31 +1,61 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/reading_settings.dart';
class LocalStore {
static const _kFontSize = 'reader_font_size';
static const _kLineHeight = 'reader_line_height';
static const _kLetterSpacing = 'reader_letter_spacing';
static const _kFontFamily = 'reader_font_family';
static const _kProgressChapterId = 'progress_chapter_id_';
static const _kProgressChapterNum = 'progress_chapter_num_';
static const _kProgressOffset = 'progress_offset_';
Future<void> saveReadingSettings({
required double fontSize,
required double lineHeight,
required double letterSpacing,
required String fontFamily,
}) async {
// ── Reading settings ──────────────────────────────────────────────────────
Future<void> saveReadingSettings(ReadingSettings settings) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble(_kFontSize, fontSize);
await prefs.setDouble(_kLineHeight, lineHeight);
await prefs.setDouble(_kLetterSpacing, letterSpacing);
await prefs.setString(_kFontFamily, fontFamily);
await prefs.setDouble(_kFontSize, settings.fontSize);
await prefs.setDouble(_kLineHeight, settings.lineHeight);
await prefs.setDouble(_kLetterSpacing, settings.letterSpacing);
await prefs.setString(_kFontFamily, settings.fontFamily);
}
Future<Map<String, dynamic>> getReadingSettings() async {
Future<ReadingSettings?> loadReadingSettings() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey(_kFontSize)) return null;
return ReadingSettings(
fontSize: prefs.getDouble(_kFontSize) ?? 18,
lineHeight: prefs.getDouble(_kLineHeight) ?? 1.8,
letterSpacing: prefs.getDouble(_kLetterSpacing) ?? 0,
fontFamily: prefs.getString(_kFontFamily) ?? 'serif',
);
}
// ── Reading progress ──────────────────────────────────────────────────────
Future<void> saveProgress(
String novelId,
String chapterId,
int chapterNumber,
double offset,
) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('$_kProgressChapterId$novelId', chapterId);
await prefs.setInt('$_kProgressChapterNum$novelId', chapterNumber);
await prefs.setDouble('$_kProgressOffset$novelId', offset);
}
Future<Map<String, dynamic>?> loadProgress(String novelId) async {
final prefs = await SharedPreferences.getInstance();
final chapterId = prefs.getString('$_kProgressChapterId$novelId');
if (chapterId == null) return null;
return {
'fontSize': prefs.getDouble(_kFontSize) ?? 18,
'lineHeight': prefs.getDouble(_kLineHeight) ?? 1.8,
'letterSpacing': prefs.getDouble(_kLetterSpacing) ?? 0,
'fontFamily': prefs.getString(_kFontFamily) ?? 'serif',
'chapterId': chapterId,
'chapterNumber': prefs.getInt('$_kProgressChapterNum$novelId') ?? 1,
'scrollOffset': prefs.getDouble('$_kProgressOffset$novelId') ?? 0.0,
};
}
}
final localStoreProvider = Provider<LocalStore>((_) => LocalStore());
+126
View File
@@ -0,0 +1,126 @@
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart' as p;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/chapter_model.dart';
class OfflineCache {
static const _dbName = 'reader_offline.db';
static const _version = 1;
Database? _db;
Future<Database> get db async {
_db ??= await _open();
return _db!;
}
Future<Database> _open() async {
final dir = await getDatabasesPath();
final path = p.join(dir, _dbName);
return openDatabase(
path,
version: _version,
onCreate: (db, _) async {
await db.execute('''
CREATE TABLE cached_chapters (
id TEXT PRIMARY KEY,
novel_id TEXT NOT NULL,
chapter_number INTEGER NOT NULL,
title TEXT,
content TEXT NOT NULL,
prev_chapter_id TEXT,
prev_chapter_number INTEGER,
next_chapter_id TEXT,
next_chapter_number INTEGER,
volume_title TEXT,
cached_at INTEGER NOT NULL
)
''');
await db.execute('''
CREATE INDEX idx_novel_chapters ON cached_chapters(novel_id, chapter_number)
''');
},
);
}
Future<void> saveChapter(ChapterModel chapter) async {
final database = await db;
await database.insert(
'cached_chapters',
{
'id': chapter.id,
'novel_id': chapter.novelId,
'chapter_number': chapter.number,
'title': chapter.title,
'content': chapter.content,
'prev_chapter_id': chapter.prevChapterId,
'prev_chapter_number': chapter.prevChapterNumber,
'next_chapter_id': chapter.nextChapterId,
'next_chapter_number': chapter.nextChapterNumber,
'volume_title': chapter.volumeTitle,
'cached_at': DateTime.now().millisecondsSinceEpoch,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<ChapterModel?> loadChapter(String chapterId) async {
final database = await db;
final rows = await database.query(
'cached_chapters',
where: 'id = ?',
whereArgs: [chapterId],
limit: 1,
);
if (rows.isEmpty) return null;
return _rowToChapter(rows.first);
}
Future<List<String>> cachedChapterIdsForNovel(String novelId) async {
final database = await db;
final rows = await database.query(
'cached_chapters',
columns: ['id'],
where: 'novel_id = ?',
whereArgs: [novelId],
orderBy: 'chapter_number ASC',
);
return rows.map((r) => r['id'] as String).toList();
}
Future<void> deleteNovelCache(String novelId) async {
final database = await db;
await database.delete(
'cached_chapters',
where: 'novel_id = ?',
whereArgs: [novelId],
);
}
Future<int> getCacheSizeBytes() async {
final database = await db;
final result = await database.rawQuery(
'SELECT SUM(LENGTH(content)) as total FROM cached_chapters',
);
return (result.first['total'] as int?) ?? 0;
}
ChapterModel _rowToChapter(Map<String, dynamic> row) {
return ChapterModel(
id: row['id'] as String,
novelId: row['novel_id'] as String,
number: row['chapter_number'] as int,
title: (row['title'] as String?) ?? '',
content: row['content'] as String,
prevChapterId: row['prev_chapter_id'] as String?,
prevChapterNumber: row['prev_chapter_number'] as int?,
nextChapterId: row['next_chapter_id'] as String?,
nextChapterNumber: row['next_chapter_number'] as int?,
volumeTitle: row['volume_title'] as String?,
createdAt: DateTime.fromMillisecondsSinceEpoch(row['cached_at'] as int),
);
}
}
final offlineCacheProvider = Provider<OfflineCache>((_) => OfflineCache());