首页 > 解决方案 > 如何使用 Provider 实现 Flutter 本地化?

问题描述

我想让我的应用程序以 2 种不同的语言工作。我想问题是我无法在 multiprovider 中处理Cosumer,可以吗?

我正在使用flutter_localization 包和flutter 提供程序包;

这是我的代码:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  AppLanguage appLanguage = AppLanguage();
  await appLanguage.fetchLocale();
  SharedPreferences.getInstance().then((prefs) {
    var darkModeOn = prefs.getBool('darkMode') ?? true;
    runApp(
      ChangeNotifierProvider<ThemeManager>(
        builder: (_) => ThemeManager(lightTheme),
        child: MyApp(appLanguage: appLanguage),
      ),
    );
  });
}

给我的应用上课

class MyApp extends StatelessWidget {
  final AppLanguage appLanguage;

  MyApp({this.appLanguage});
  @override
  Widget build(BuildContext context) {
    final themeNotifier = Provider.of<ThemeManager>(context);
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(value: ApiService()),
        ChangeNotifierProxyProvider<ApiService, StudentData>(
          builder: (ctx, auth, _) => StudentData(auth.token, auth.school),
        )
      ],
      child: Consumer<ApiService>(
        builder: (ctx, auth, _) => ChangeNotifierProvider<AppLanguage>(
          builder: (_) => appLanguage,
          child: Consumer<AppLanguage>(
            builder: (context, model, child) => MaterialApp(
              locale: appLanguage.appLocal,
              supportedLocales: [Locale('ru', 'RU'), Locale('uz', 'UZ')],
              localizationsDelegates: [
                AppLocalization.delegate,
                GlobalMaterialLocalizations.delegate,
                GlobalWidgetsLocalizations.delegate,
              ],
              debugShowCheckedModeBanner: false,
              title: "Socratus |Mobile",
              theme: themeNotifier.getTheme(),
              home: auth.isAuth
                  ? MainScreen()
                  : FutureBuilder(
                      future: auth.tryAutoLogin(),
                      builder: (ctx, authResultSnapshot) => LoginScreen()),
              routes: {
                MainScreen.routeName: (ctx) => MainScreen(),
                ProfilePage.routeName: (ctx) => ProfilePage(),
                SettingsPage.routeName: (ctx) => SettingsPage(
                      appLanguage: appLanguage,
                    ),
                ChangePassword.routeName: (ctx) => ChangePassword(),
                HomeworkScreen.routeName: (ctx) => HomeworkScreen(),
                HWDetails.routeName: (ctx) => HWDetails(),
                NewsPage.routeName: (ctx) => NewsPage(),
                QuestionAndAnswers.routeName: (ctx) => QuestionAndAnswers(),
                MyDownloads.routeName: (ctx) => MyDownloads(),
              },
            ),
          ),
        ),
      ),
    );
  }
}

这里我是如何尝试实现的

class AppLocalization {
  final Locale locale;

  AppLocalization(this.locale);

  static AppLocalization of(BuildContext context) {
    return Localizations.of<AppLocalization>(context, AppLocalization);
  }

  static const LocalizationsDelegate<AppLocalization> delegate =
      _AppLocalizationDelegate();

  Map<String, String> _localizedStrings;

  Future<bool> load() async {
    String jsonString = await rootBundle
        .loadString('assets/translations/${locale.languageCode}.json');
    Map<String, dynamic> jsonMap = json.decode(jsonString);

    _localizedStrings = jsonMap.map((key, value) {
      return MapEntry(key, value.toString());
    });

    return true;
  }

  String translate(String key) {
    return _localizedStrings[key];
  }
}

class _AppLocalizationDelegate extends LocalizationsDelegate<AppLocalization> {
  // This delegate instance will never change (it doesn't even have fields!)
  // It can provide a constant constructor.
  const _AppLocalizationDelegate();

  @override
  bool isSupported(Locale locale) {
    // Include all of your supported language codes here
    return ['ru', 'uz'].contains(locale.languageCode);
  }

  @override
  Future<AppLocalization> load(Locale locale) async {
    AppLocalization localizations = new AppLocalization(locale);
    await localizations.load();
    return localizations;
  }

  @override
  bool shouldReload(_AppLocalizationDelegate old) => false;
}

我的提供者:

class AppLanguage extends ChangeNotifier {
  Locale _appLocale = Locale('ru');

  Locale get appLocal => _appLocale ?? Locale("ru");
  fetchLocale() async {
    var prefs = await SharedPreferences.getInstance();
    if (prefs.getString('language_code') == null) {
      _appLocale = Locale('ru');
      return Null;
    }
    _appLocale = Locale(prefs.getString('language_code'));
    return Null;
  }

  void changeLanguage(Locale type) async {
    var prefs = await SharedPreferences.getInstance();
    if (_appLocale == type) {
      return;
    }
    if (type == Locale("uz")) {
      _appLocale = Locale("uz");
      await prefs.setString('language_code', 'uz');
      await prefs.setString('countryCode', 'UZ');
    } else {
      _appLocale = Locale("ru");
      await prefs.setString('language_code', 'ru');
      await prefs.setString('countryCode', 'RU');
    }
    notifyListeners();
  }
}

如果这不是正确的方法,我该如何实现这个功能?

标签: androidflutter

解决方案


本文解释了您尝试实现的方式,如果您想要一种不同的方法,我建议您从 pub.dev 中查看Easy Localization,它目前可以很好地处理所有Flutter 国际化功能


推荐阅读