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

如何同时使用@freezed与@riverpod?遇ref未定义问题求解

问题解决思路及推荐方案

当前写法的核心问题

@freezed 生成的是纯数据模型类,它的本职就是封装状态数据,不该掺和依赖外部服务(比如SharedPreferences)的业务逻辑(像保存、读取这类操作)。而Riverpod的ref是Provider逻辑层的专属依赖,硬往数据类里塞肯定会出问题。

临时凑合用的解决方式(不推荐)

如果非要在现有结构里搞定,你可以把ref作为参数传入需要用到它的方法:

@freezed
class Settings with _$Settings {
  const factory Settings({required bool darkMode}) = _Settings;

  // 把ref作为参数传给save方法
  Future<void> save(WidgetRef ref) async {
    final prefs = await ref.watch(sharedPreferencesProvider.future);
    await prefs.setBool('dark_mode', darkMode);
  }

  // 从Provider里把ref传进来构建实例
  factory Settings.fromPrefs(WidgetRef ref) {
    final prefs = ref.watch(sharedPreferencesProvider);
    return Settings(darkMode: prefs.getBool('dark_mode') ?? false);
  }
}

// 对应的Provider
final settingsProvider = Provider<Settings>((ref) {
  return Settings.fromPrefs(ref);
});

但这种写法太别扭了,等于把本该属于Provider的逻辑硬甩给数据类,违背了单一职责原则,只能临时救急,不能长期用。

更合理的拆分方案(强烈推荐)

正确的姿势是把数据模型和状态管理逻辑彻底分开:

  1. 用@freezed定义纯数据类,只负责存数据
  2. 用Riverpod的Notifier类(比如AsyncNotifier、StateNotifier)来封装读取、保存等业务逻辑,这里就能正常访问ref

代码示例:

1. 纯数据模型(仅用@freezed)

@freezed
class Settings with _$Settings {
  const factory Settings({required bool darkMode}) = _Settings;

  // 只保留和数据序列化相关的方法,比如从Map转模型
  factory Settings.fromJson(Map<String, dynamic> json) => _$SettingsFromJson(json);
}

2. 用Riverpod的AsyncNotifierProvider管理状态和逻辑

final settingsProvider = AsyncNotifierProvider<SettingsNotifier, Settings>(() {
  return SettingsNotifier();
});

class SettingsNotifier extends AsyncNotifier<Settings> {
  @override
  Future<Settings> build() async {
    // 这里可以正常访问ref,读取SharedPreferences
    final prefs = await ref.watch(sharedPreferencesProvider.future);
    final darkMode = prefs.getBool('dark_mode') ?? false;
    return Settings(darkMode: darkMode);
  }

  // 封装save方法,同样能访问ref
  Future<void> save(Settings newSettings) async {
    state = const AsyncValue.loading();
    try {
      final prefs = await ref.watch(sharedPreferencesProvider.future);
      await prefs.setBool('dark_mode', newSettings.darkMode);
      state = AsyncValue.data(newSettings);
    } catch (e) {
      state = AsyncValue.error(e, StackTrace.current);
    }
  }
}

这么拆分后,数据模型只管数据结构,状态管理逻辑全在Notifier里,既符合Freezed和Riverpod的设计初衷,代码也更清晰好维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 12:30:22