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

使用Riverpod与SharedPreferences时Flutter Switch过渡卡顿问题

Riverpod + SharedPreferences 下 Switch 组件动画不流畅的解决方法

我在应用中有一个简单的Switch组件,希望更新其状态并保存到SharedPreferences中,目前使用Riverpod来读取SharedPreferences数据。发现仅用setState()不结合SharedPreferences时,Switch的切换过渡十分流畅,但通过Provider获取值时,点击Switch后过渡瞬间完成毫无动画,推测是状态处理方式有误。


有问题的代码(Riverpod + SharedPreferences)

class SwitchExample extends ConsumerStatefulWidget {
  const SwitchExample({super.key});

  @override
  ConsumerState<SwitchExample> createState() => _SwitchExampleState();
}

class _SwitchExampleState extends ConsumerState<SwitchExample> {
  @override
  Widget build(BuildContext context) {
    final settings = ref.watch(settingsProvider);
    return Switch(
      value: settings.isDynamicColorEnabled,
      activeColor: Colors.red,
      onChanged: (bool value) async {
        await ref.read(settingsProvider.notifier).setDynamicColorEnabled(value);
      },
    );
  }
}

正常的 setState 代码

class SwitchExample extends StatefulWidget {
  const SwitchExample({super.key});

  @override
  State<SwitchExample> createState() => _SwitchExampleState();
}

class _SwitchExampleState extends State<SwitchExample> {
  bool light = true;

  @override
  Widget build(BuildContext context) {
    return Switch(
      value: light,
      activeColor: Colors.red,
      onChanged: (bool value) {
        setState(() {
          light = value;
        });
      },
    );
  }
}

问题原因

Switch组件的过渡动画依赖即时的状态变更反馈:用setState时,点击后立刻更新本地状态并触发UI重建,动画自然流畅;但通过Provider异步更新时,SharedPreferences的写入是耗时操作,Provider状态更新延迟,导致Switch等待状态同步完成才刷新,跳过了过渡动画。

解决方案:本地状态优先更新,异步同步持久化

先通过setState更新组件本地状态,让Switch立刻播放动画,再在后台异步完成SharedPreferences和Provider的状态同步,兼顾动画流畅性和状态持久化。

修改后的代码:

class SwitchExample extends ConsumerStatefulWidget {
  const SwitchExample({super.key});

  @override
  ConsumerState<SwitchExample> createState() => _SwitchExampleState();
}

class _SwitchExampleState extends ConsumerState<SwitchExample> {
  late bool _localDynamicColorEnabled;

  @override
  void initState() {
    super.initState();
    // 从Provider初始化本地状态
    _localDynamicColorEnabled = ref.read(settingsProvider).isDynamicColorEnabled;
  }

  @override
  Widget build(BuildContext context) {
    return Switch(
      value: _localDynamicColorEnabled,
      activeColor: Colors.red,
      onChanged: (bool value) {
        // 1. 先更新本地状态,触发Switch动画
        setState(() {
          _localDynamicColorEnabled = value;
        });
        // 2. 后台异步更新SharedPreferences和Provider,无需await阻塞UI
        ref.read(settingsProvider.notifier).setDynamicColorEnabled(value);
      },
    );
  }
}

补充:Provider侧的正确实现

确保你的SettingsNotifier正确处理异步写入逻辑:

// 假设的Settings数据类
class Settings {
  final bool isDynamicColorEnabled;

  Settings({required this.isDynamicColorEnabled});

  Settings copyWith({bool? isDynamicColorEnabled}) {
    return Settings(
      isDynamicColorEnabled: isDynamicColorEnabled ?? this.isDynamicColorEnabled,
    );
  }
}

class SettingsNotifier extends StateNotifier<Settings> {
  SettingsNotifier(Settings state) : super(state);

  Future<void> setDynamicColorEnabled(bool value) async {
    // 写入SharedPreferences
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool('dynamic_color_enabled', value);
    // 更新Provider状态
    state = state.copyWith(isDynamicColorEnabled: value);
  }
}

final settingsProvider = StateNotifierProvider<SettingsNotifier, Settings>((ref) {
  // 初始化时从SharedPreferences读取数据(可优化为异步初始化,这里简化示例)
  return SettingsNotifier(Settings(isDynamicColorEnabled: false));
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:03:18