Flutter Shared Preferences问题:无法按书籍ID(tid)单独保存用户最后阅读页码
看起来你遇到的核心问题是每次初始化页面时都会强制覆盖对应书籍的页码为初始值1,而且没有在用户翻页后及时保存最新的页码,导致所有书籍的阅读进度都被重置成同一个值。让我帮你梳理问题并修复代码:
问题根源分析
你的代码里有三个关键问题:
- 在
getPref()方法中,你先执行了prefs.setInt('${book}', setPage);,而setPage的初始值是1。这意味着每次打开任意书籍,都会先把该书籍的页码强制设为1,再读取出来,完全覆盖了之前保存的进度。 - 用户翻页时(
onPageChanged)你只更新了setPage变量,但没有把这个最新值保存到SharedPreferences,所以页面关闭后进度不会被持久化。 - 另外,
book、bookPage、setPage是全局变量,当多个书籍页面实例存在时,会互相干扰,导致数据混乱。
修复后的完整代码
我把这些问题都修复了,关键改动处添加了注释:
class libraryPDF extends StatefulWidget { final String? sid; final String? tid; final String? page; const libraryPDF({Key? key, this.sid, this.page, this.tid}) : super(key: key); @override State<libraryPDF> createState() => _libraryPDFState(); } // 把变量移到State类内部,避免全局变量的跨实例干扰 class _libraryPDFState extends State<libraryPDF> { late PdfViewerController _pdfViewerController; final GlobalKey<SfPdfViewerState> _pdfViewerStateKey = GlobalKey(); late String book; int bookPage = 1; int setPage = 1; @override void initState() { book = '${widget.tid}'; _pdfViewerController = PdfViewerController(); getPref(); super.initState(); } getPref() async { SharedPreferences prefs = await SharedPreferences.getInstance(); // 只在没有保存过该书籍页码时设置默认值,否则读取已保存的进度 if (!prefs.containsKey(book)) { await prefs.setInt(book, 1); bookPage = 1; } else { bookPage = prefs.getInt(book)!; } // 更新状态并跳转到保存的页码 setState(() { _pdfViewerController.jumpToPage(bookPage); }); print('当前书籍ID: $book,读取到的页码: $bookPage'); } // 封装保存页码的方法 saveCurrentPage() async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setInt(book, setPage); print('已保存书籍ID: $book 的页码: $setPage'); } @override void dispose() { // 页面销毁时自动保存当前页码,确保进度不丢失 saveCurrentPage(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( centerTitle: true, elevation: 0, title: const Text("CanadianHS"), backgroundColor: Colors.blue[900], actions: <Widget>[ IconButton( onPressed: () { _pdfViewerStateKey.currentState!.openBookmarkView(); }, icon: const Icon( Icons.bookmark, color: Colors.white, )), IconButton( icon: const Icon( Icons.keyboard_arrow_left, color: Colors.white, ), onPressed: () { _pdfViewerController.previousPage(); }, ), IconButton( icon: const Icon( Icons.keyboard_arrow_right, color: Colors.white, ), onPressed: () { _pdfViewerController.nextPage(); }, ) ], ), body: SfPdfViewer.network( 'https://url/${widget.sid}', controller: _pdfViewerController, onDocumentLoaded: (PdfDocumentLoadedDetails details) { // 文档加载完成后跳转到保存的页码 _pdfViewerController.jumpToPage(bookPage); print('文档总页数: ${details.document.pages.count}'); }, key: _pdfViewerStateKey, onPageChanged: (PdfPageChangedDetails details) { // 翻页时更新当前页码变量 setState(() { setPage = details.newPageNumber; }); // 可选:如果需要实时保存(每翻一页就存),可以取消下面的注释 // saveCurrentPage(); }, pageLayoutMode: PdfPageLayoutMode.single), ); } }
关键改动说明
- 变量作用域调整:把之前的全局变量移到
_libraryPDFState类内部,每个书籍页面实例都有独立的变量,不会互相干扰。 - 修正初始化逻辑:在
getPref()中先检查SharedPreferences是否存在该书籍的页码,不存在才设置默认值1,彻底避免覆盖原有进度。 - 添加持久化逻辑:新增
saveCurrentPage()方法,在页面销毁(dispose)时自动调用,确保阅读进度在退出页面时被保存。你也可以选择在onPageChanged里实时保存,根据需求调整。 - 状态更新优化:更新页码时使用
setState(),确保UI能正确响应状态变化。
这样修改后,每个书籍的阅读进度就会以tid为键单独保存到SharedPreferences,重新打开书籍时就能准确跳转到上次阅读的位置了。
内容的提问来源于stack exchange,提问作者Farouk Chourbaji
相关产品推荐
相关产品推荐

