如何合理销毁ChangeNotifierProvider?解决ViewModel释放后调用异常
解决方案
核心问题是页面销毁后,ViewModel的dispose方法已执行,但之前启动的延迟异步任务仍在运行,最终调用notifyListeners()时触发断言错误。解决思路是在ViewModel销毁时取消所有未完成的异步任务,并在更新状态前检查自身活跃状态。
1. 给ViewModel添加异步任务管理能力
使用CancelableOperation(需导入package:async/async.dart)或Timer管理异步任务,并重写dispose方法取消任务:
class ViewModel with ChangeNotifier { ApiResponse<String> _apiResponse = ApiResponse.initial("initial"); CancelableOperation? _cancelableOp; // 管理异步任务 ApiResponse<String> get apiResponse => _apiResponse; set apiResponse(ApiResponse<String> value) { // 更新状态前先检查是否已被释放 if (!mounted) return; _apiResponse = value; notifyListeners(); } Future<void> fonksiyon() async { // 先取消之前的任务,避免重复执行 _cancelableOp?.cancel(); _cancelableOp = CancelableOperation.fromFuture( Future.delayed( const Duration(seconds: 5), () { apiResponse = ApiResponse.completed("Tamamlandı"); }, ), ); await _cancelableOp?.value; } @override void dispose() { _cancelableOp?.cancel(); // 销毁时取消所有未完成的任务 super.dispose(); } }
2. 利用mounted状态检查
ChangeNotifier自带mounted属性,在调用notifyListeners()前检查实例是否活跃,避免在已释放对象上执行操作。
3. 优化页面的Provider使用
在SonrakiSayfa中使用Consumer更安全地监听变化,调用方法时用listen: false避免不必要的监听:
class SonrakiSayfa extends StatelessWidget { const SonrakiSayfa({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( actions: [ IconButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => MyHomePage(), ), ); }, icon: Icon(Icons.arrow_forward_ios_outlined), ), ], ), floatingActionButton: FloatingActionButton( onPressed: () { Provider.of<ViewModel>(context, listen: false).fonksiyon(); }, ), body: Center( child: Consumer<ViewModel>( builder: (context, viewModel, child) { return viewModel.apiResponse.status == Status.completed ? Text(viewModel.apiResponse.data) : CircularProgressIndicator(); }, ), ), ); } }
内容的提问来源于stack exchange,提问作者Burak Dinç
相关产品推荐
相关产品推荐

