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

Flutter Riverpod v1.0下StateNotifier监听List类型状态不更新问题

Riverpod 1.0 List类型状态不更新解决方法

根本原因

Riverpod 1.0 采用基于不可变值的状态比对逻辑,你之前直接修改List内部元素的属性,再执行state = state时,本质是同一个List对象的引用赋值,Riverpod检测不到值变化,因此不会触发状态更新。0.4x版本的状态检测逻辑更宽松,所以旧写法可以生效。

你当前的Freezed用法也存在错误:Freezed是用来生成不可变数据模型的工具,不需要绑定到StateNotifier类上,应该作用于你的CoverAlignment数据模型类。

正确实现步骤

1. 改造CoverAlignment为不可变Freezed模型

给CoverAlignment加上Freezed注解,自动生成copyWith方法,用来快速生成修改后的新实例:

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';

part 'cover_alignment.freezed.dart';

@freezed
class CoverAlignment with _$CoverAlignment {
  const factory CoverAlignment({
    required bool removeSpeechPadding,
    required double speechBalloonOpacity,
    required double x,
    required double y,
    required Curve curve,
    required double seconds,
    required int degrees,
    // 补充你的其他原有属性
  }) = _CoverAlignment;

  // 你的原有静态方法可以放在这里
  static double randomDouble() => /* 原有实现 */;
  static Curve getCurve() => /* 原有实现 */;
  static double randomSeconds() => /* 原有实现 */;
  static int randomIntInRangeWithMinimum({required int min, required int max}) => /* 原有实现 */;
}

2. 改造StateNotifier的状态修改逻辑

所有对状态的修改都必须生成全新的List实例,不能直接修改原state的内部元素:

final animateCoversStateNotifierProvider = StateNotifierProvider.autoDispose<
    AnimateCoversStateNotifier,
    List<CoverAlignment>>((ref) => AnimateCoversStateNotifier());

class AnimateCoversStateNotifier extends StateNotifier<List<CoverAlignment>> {
  AnimateCoversStateNotifier() : super([]);

  void addCover({
    required CoverAlignment alignment,
  }) {
    // 基于原有state生成新List,添加元素后赋值给state
    state = [...state, alignment];
  }

  void addCoverAt({
    required int index,
    required CoverAlignment alignment,
  }) {
    final newState = List<CoverAlignment>.from(state);
    newState.insert(index, alignment);
    state = newState;
  }

  void move({
    required int cover,
    bool? readyToOpen,
    bool? speechBalloonVisible,
    Duration? animatedOpacityDuration,
    bool? removeSpeechBalloonPadding,
  }) {
    debugPrint('move cover called');
    Future.delayed(const Duration(milliseconds: 200), () {
      if (!mounted) return;
      // 生成原List的深拷贝
      final newState = List<CoverAlignment>.from(state);
      // 用copyWith生成修改后的新元素,替换原位置的元素
      newState[cover] = newState[cover].copyWith(
        removeSpeechPadding: speechBalloonVisible != true,
        speechBalloonOpacity: speechBalloonVisible == true ? kMaxSpeechBalloonOpacity : 0.0,
        x: CoverAlignment.randomDouble(),
        y: CoverAlignment.randomDouble(),
        curve: CoverAlignment.getCurve(),
        seconds: CoverAlignment.randomSeconds(),
        degrees: CoverAlignment.randomIntInRangeWithMinimum(min: 0, max: 45),
      );
      // 新List赋值给state,触发更新
      state = newState;
    });
  }
}

注意事项

  • 所有List的增删改操作都不能直接操作原state对象,必须生成新的List实例
  • 不能直接修改数据模型的属性,必须用copyWith生成新的模型实例替换旧实例
  • 页面端的watch监听逻辑不需要修改,状态更新后会自动触发重建

内容的提问来源于stack exchange,提问作者Zelf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 17:54:04