Riverpod中StateProvider状态更新后Widget不重建问题求助
Riverpod StateProvider状态更新后UI不重建问题
问题描述:
- 使用Riverpod的StateProvider时,更新状态后Widget树并未重建,但控制台打印显示状态已实际更新
- 仅在热重启/重载页面、调整Chrome窗口大小等操作时,Widget树才会重建一次
- 只想通过StateProvider解决该问题,不考虑Provider、Bloc、StateNotifierProvider等其他状态管理模式
相关代码:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:math'; void main() { runApp(const ProviderScope(child: MyApp())); } class Data { final String data; Data({required this.data}); } final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data')); class MyApp extends ConsumerStatefulWidget { const MyApp({super.key}); @override ConsumerState<MyApp> createState() => _MyAppState(); } class _MyAppState extends ConsumerState<MyApp> { @override void initState() { print("Init state"); super.initState(); // getData(); } // getData() async { // // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1')); // // final title = jsonDecode(response.body)["title"];; // // ref.read(helloWorldProvider.notifier).update((state) => title); // SharedPreferences prefs = await SharedPreferences.getInstance(); // prefs.setString('valueTemp', 'newValue'); // String? valueTemp = prefs.getString('valueTemp'); // String value = valueTemp ?? ''; // Data data = Data(data: value); // ref.read(helloWorldProvider.notifier).update((state) => data); // print("Đã thực hiện xong"); // } void _change() { print("change"); final rawString = generateRandomString(5); Data data = new Data(data: rawString); ref.watch(helloWorldProvider.notifier).update((state) => data); print(ref.read(helloWorldProvider.notifier).state?.data); } String generateRandomString(int len) { var r = Random(); return String.fromCharCodes(List.generate(len, (index) => r.nextInt(33) + 89)); } @override Widget build(BuildContext context) { print('Rebuild'); final data = ref.watch(helloWorldProvider.notifier).state; final dataText = data?.data ?? 'No text'; print(dataText); return MaterialApp( title: 'Google Docs Clone', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: Center( child: Column(children: [ Text(dataText) ] ) ), floatingActionButton: FloatingActionButton( onPressed: _change, tooltip: 'Change', child: const Icon(Icons.add), ), )); } }
问题现象:控制台打印出新的状态值,但界面上的文本并未更新,只有在热重启或调整窗口大小后才会显示最新状态。
解决方案
问题根源在于错误地监听了StateProvider的notifier实例,而非状态本身:
修改
build方法中获取状态的方式:
将:final data = ref.watch(helloWorldProvider.notifier).state;改为:
final data = ref.watch(helloWorldProvider);直接监听StateProvider的状态,这样状态更新时会自动触发Widget重建。
优化状态更新的代码(符合Riverpod最佳实践):
在_change方法中,使用ref.read而非ref.watch来获取notifier:ref.read(helloWorldProvider.notifier).update((state) => data);ref.watch会在回调中建立监听,可能导致不必要的重建,ref.read更适合在事件回调中使用。
修改后的核心代码片段:
void _change() { print("change"); final rawString = generateRandomString(5); Data data = Data(data: rawString); ref.read(helloWorldProvider.notifier).update((state) => data); print(ref.read(helloWorldProvider.notifier).state?.data); } @override Widget build(BuildContext context) { print('Rebuild'); final data = ref.watch(helloWorldProvider); final dataText = data?.data ?? 'No text'; print(dataText); return MaterialApp( title: 'Google Docs Clone', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, ), home: Scaffold( body: Center( child: Column(children: [ Text(dataText) ] ) ), floatingActionButton: FloatingActionButton( onPressed: _change, tooltip: 'Change', child: const Icon(Icons.add), ), )); }
原理说明
ref.watch(helloWorldProvider.notifier)监听的是notifier实例的变化,但StateProvider的notifier实例不会随状态更新而改变,因此状态更新时不会触发Widget重建。ref.watch(helloWorldProvider)直接监听状态值的变化,当通过notifier更新状态后,Provider会通知所有监听该状态的Widget进行重建。
内容的提问来源于stack exchange,提问作者Minh Tiến Đào
相关产品推荐
相关产品推荐

