You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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实例,而非状态本身:

  1. 修改build方法中获取状态的方式:
    将:

    final data = ref.watch(helloWorldProvider.notifier).state;
    

    改为:

    final data = ref.watch(helloWorldProvider);
    

    直接监听StateProvider的状态,这样状态更新时会自动触发Widget重建。

  2. 优化状态更新的代码(符合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 15:00:49