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

Flutter中LocaleResolutionCallback内使用Riverpod报错的解决方案咨询

Riverpod在localeResolutionCallback中修改状态的报错解决方案

我在Flutter项目中使用riverpod_hook做依赖注入,需要根据移动设备的语言设置同步修改应用语言,于是在MaterialApp的localeResolutionCallback中通过ref更新localeCodeProvider的状态,但运行时触发报错:Tried to modify a provider while the widget tree was building。要求必须使用localeResolutionCallback实现全局语言适配,求正确结合Provider的解决方案。

原代码

app.dart

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class Application extends ConsumerWidget {
  const Application({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp(
      theme: defaultTheme,
      localizationsDelegates: Resource.localizationsDelegates,
      supportedLocales: Resource.supportedLocales,
      localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) {
        if (supportedLocales.any((element) => locale?.languageCode.contains(element.toString()) == true)) {
          ref.watch(localeCodeProvider.notifier).state = locale!.languageCode;
          return locale;
        }
        return const Locale('ko', '');
      },
      routes: {
        homePageRoutes: (_) => const HomePage(),
        detailPageRoutes: (_) => const DetailPage(),
      },
    );
  }
}

Provider代码

import 'package:hooks_riverpod/hooks_riverpod.dart';

final localeCodeProvider = StateProvider<String>((ref) => 'en');

报错信息

E/flutter ( 3747): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: Tried to modify a provider while the widget tree was building.
E/flutter ( 3747): If you are encountering this error, chances are you tried to modify a provider
E/flutter ( 3747): in a widget life-cycle, such as but not limited to:
E/flutter ( 3747): - build
E/flutter ( 3747): - initState
E/flutter ( 3747): - dispose
E/flutter ( 3747): - didUpdateWidget
E/flutter ( 3747): - didChangeDepedencies
E/flutter ( 3747):
E/flutter ( 3747): Modifying a provider inside those life-cycles is not allowed, as it could
E/flutter ( 3747): lead to an inconsistent UI state. For example, two widgets could listen to the
E/flutter ( 3747): same provider, but incorrectly receive different states.
E/flutter ( 3747):
E/flutter ( 3747):
E/flutter ( 3747): To fix this problem, you have one of two solutions:
E/flutter ( 3747): - (preferred) Move the logic for modifying your provider outside of a widget
E/flutter ( 3747): life-cycle. For example, maybe you could update your provider inside a button's
E/flutter ( 3747): onPressed instead.
E/flutter ( 3747):
E/flutter ( 3747): - Delay your modification, such as by encasuplating the modification
E/flutter ( 3747): in a Future(() {...}).
E/flutter ( 3747): This will perform your upddate after the widget tree is done building.

解决方案

报错原因是localeResolutionCallback在Widget树构建阶段执行,此时直接修改Provider状态会触发Riverpod的安全校验。针对必须使用localeResolutionCallback的场景,有两种可靠的修改方式:

方式一:使用Future.microtask延迟更新状态

将修改Provider状态的逻辑包裹在Future.microtask中,确保操作在当前构建周期结束后执行:

localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) {
  Locale resolvedLocale;
  if (supportedLocales.any((element) => locale?.languageCode == element.languageCode)) {
    resolvedLocale = locale!;
    // 延迟更新Provider状态
    Future.microtask(() {
      ref.read(localeCodeProvider.notifier).state = resolvedLocale.languageCode;
    });
  } else {
    resolvedLocale = const Locale('ko', '');
    Future.microtask(() {
      ref.read(localeCodeProvider.notifier).state = 'ko';
    });
  }
  return resolvedLocale;
},

注意:这里使用ref.read而非ref.watch,因为我们只是执行修改操作,不需要监听状态变化。

方式二:使用SchedulerBinding的addPostFrameCallback

通过Flutter的调度器,在Widget树绘制完成后执行状态更新:

import 'package:flutter/scheduler.dart';

// ...

localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) {
  Locale resolvedLocale;
  if (supportedLocales.any((element) => locale?.languageCode == element.languageCode)) {
    resolvedLocale = locale!;
    SchedulerBinding.instance.addPostFrameCallback((_) {
      ref.read(localeCodeProvider.notifier).state = resolvedLocale.languageCode;
    });
  } else {
    resolvedLocale = const Locale('ko', '');
    SchedulerBinding.instance.addPostFrameCallback((_) {
      ref.read(localeCodeProvider.notifier).state = 'ko';
    });
  }
  return resolvedLocale;
},

额外优化:修正语言匹配逻辑

原代码中的locale?.languageCode.contains(element.toString())存在逻辑问题,element.toString()会返回类似ko_KR的字符串,而languageCode是ko,直接对比languageCode更准确:

supportedLocales.any((element) => locale?.languageCode == element.languageCode)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 14:10:27