Flutter中MobX如何观测普通列表实现UI实时更新?
问题描述
我在Flutter中遇到一个简化场景的问题:MobX可观察对象Book包含一个章节列表,我希望观测该列表,以便在创建新章节时UI能重绘显示新章节。
实际项目中我的模型更复杂,这是能复现问题的最简案例。
当创建新章节时,列表不会立即显示新章节,但返回主页面再进入后,新章节会出现在列表中。
由于项目其他原因,我不能将Book中的chapters属性改为ObservableList,即final ObservableList<Chapter> chapters;不可行。
我在Stack Overflow上查找了多个类似问题,但未找到解决方案。
请问如何观测当前书籍的章节列表,使创建新章节后UI能正确更新?
我的模型
class Book { final String title; final List<Chapter> chapters; Book(this.title, this.chapters); } class Chapter { final String title; Chapter(this.title); }
MobX Store
import 'book.dart'; import 'package:mobx/mobx.dart'; part 'book_editor_store.g.dart'; class BookEditorStore = BookEditorStoreBase with _$BookEditorStore; abstract class BookEditorStoreBase with Store { @readonly Book _currentBook = Book('', []); @readonly Chapter? _currentChapter; @computed int get chapterCount => _currentBook?.chapters.length ?? 0; @computed List<Chapter> get chapters => _currentBook?.chapters ?? []; @action setBook(Book book) { _currentBook = book; } @action addChapter(Chapter chapter) { _currentBook?.chapters.add(chapter); } }
main.dart
import 'book_editor_page.dart'; import 'book_editor_store.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return Provider( create: (context) => BookEditorStore(), child: MaterialApp( title: 'MobX reactions', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MainPage(), ), ); } } class MainPage extends StatelessWidget { const MainPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('Main Page'), ), body: Center( child: ElevatedButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => const BookEditorPage()), ); }, child: const Text('Editor'), ), ), ); } }
BookEditorPage
import 'book_editor_store.dart'; import 'book.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class BookEditorPage extends StatelessWidget { const BookEditorPage({super.key}); @override Widget build(BuildContext context) { final bookEditorStore = Provider.of<BookEditorStore>(context); return Scaffold( appBar: AppBar( title: const Text('Book Editor'), ), body: Column( children: [ Observer( builder: (_) => ListView.builder( shrinkWrap: true, itemCount: bookEditorStore.chapterCount, itemBuilder: (_, index) { final chapter = bookEditorStore.chapters[index]; return ListTile( title: Text(chapter.title), ); }, ), ), ElevatedButton( onPressed: () => addChapter(context, bookEditorStore), child: const Text('Add Chapter'), ), ], ), ); } void addChapter(BuildContext context, BookEditorStore bookEditorStore) { final TextEditingController titleController = TextEditingController(); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('New Chapter'), content: TextFormField( controller: titleController, decoration: const InputDecoration(hintText: 'Enter chapter title'), autofocus: true, ), actions: <Widget>[ TextButton( child: const Text('Cancel'), onPressed: () { Navigator.of(context).pop(); }, ), TextButton( child: const Text('Create'), onPressed: () { final String title = titleController.text; if (title.isNotEmpty) { bookEditorStore.addChapter(Chapter(title)); Navigator.of(context).pop(); } }, ), ], ); }, ); } }
解决方案
由于无法将Book中的chapters改为ObservableList,可以通过以下几种方式触发MobX的响应式更新:
方法一:创建新列表实例替换原列表
核心思路是利用MobX对_currentBook引用变化的监听,当_currentBook的引用更新时,依赖它的计算属性和Observer会自动触发更新。
修改Store中的addChapter方法:
@action addChapter(Chapter chapter) { // 生成包含新章节的新列表 final newChapters = [..._currentBook.chapters, chapter]; // 创建新的Book实例替换原有对象 _currentBook = Book(_currentBook.title, newChapters); }
方法二:给Book添加Observable注解并手动触发更新
如果允许对Book类做小改动,可以给章节列表添加@observable注解,再通过runInAction确保MobX感知到变化:
- 修改
Book类:
import 'package:mobx/mobx.dart'; class Book { final String title; @observable final List<Chapter> chapters; Book(this.title, this.chapters); }
- 更新Store的
addChapter方法:
@action addChapter(Chapter chapter) { runInAction(() { _currentBook.chapters.add(chapter); }); }
方法三:添加自定义触发变量
如果完全不能修改Book类,可以在Store中新增一个可观察计数器,让计算属性依赖这个计数器,每次添加章节时更新计数器来强制触发UI更新:
修改Store:
abstract class BookEditorStoreBase with Store { @readonly Book _currentBook = Book('', []); @readonly Chapter? _currentChapter; // 用于触发更新的计数器 @observable int _updateTrigger = 0; @computed int get chapterCount { // 依赖计数器,每次计数器变化时重新计算 _updateTrigger; return _currentBook.chapters.length; } @computed List<Chapter> get chapters { // 依赖计数器 _updateTrigger; return _currentBook.chapters; } @action addChapter(Chapter chapter) { _currentBook.chapters.add(chapter); // 更新计数器触发UI更新 _updateTrigger++; } }
内容的提问来源于stack exchange,提问作者Rsevero
相关产品推荐
相关产品推荐

