Remote Config Device Language Changes in Flutter

4,812

Solution 1

Device language changes can be detected using a WidgetsBindingObserver.

It is the simplest to use it with a StatefulWidget in your State (with WidgetsBindingObserver):

class _MyWidgetState extends State<MyWidget> with WidgetsBindingObserver {
  @override
  void didChangeLocales(List<Locale> locale) {
    // The device language was changed when this is called.
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
  ...
}

This means that you can now reload your RemoteConfig in didChangeLocales:

@override
void didChangeLocales(List<Locale> locale) {
  _updateRemoteConfig();
}

Future<void> _updateRemoteConfig() async {
  final remoteConfig = await RemoteConfig.instance;
  await remoteConfig.activateFetched(); // This will apply the new locale.
}

Solution 2

No there's no Builder for Locale.

Instead, there's an InheritedWidget which you can subscribe to using Localizations.of.

Since it is an InheritedWidget, all widgets that call Localizations.of will automatically refresh on locale change.

EDIT :

A example on how to live reload text using Flutter Locale system :

Let's assume you have the following class that holds translations :

class MyData {
  String title;

  MyData({this.title});
}

You'd then have a LocalizationsDelegate that contains such data. A dumb implementation would be the following :

class MyLocale extends LocalizationsDelegate<MyData> {
  MyData data;

  MyLocale(this.data);

  @override
  bool isSupported(Locale locale) {
    return true;
  }

  @override
  Future<MyData> load(Locale locale) async {
    return data;
  }

  @override
  bool shouldReload(MyLocale old) {
    return old.data != data;
  }
}

To use it simply pass it to MaterialApp.localizationsDelegates (be sure to add flutter_localizations to your pubspec.yaml) :

LocalizationsDelegate myLocale = MyLocale(MyData(title: "Foo"));
...


MaterialApp(
  localizationsDelegates: [
    myLocale,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
  ],
);

You can then freely live reload your translations by replacing myLocale with a new MyLocale instance.

Here's a full example of a click counter app. But where the current count is instead stored inside Locale (because why not ?)

import 'dart:async';

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

class MyCount {
  String count;

  MyCount({this.count});
}

class MyCountLocale extends LocalizationsDelegate<MyCount> {
  MyCount data;

  MyCountLocale(this.data);

  @override
  bool isSupported(Locale locale) {
    return true;
  }

  @override
  Future<MyCount> load(Locale locale) async {
    return data;
  }

  @override
  bool shouldReload(MyCountLocale old) {
    return old.data != data;
  }
}

Future<void> main() async {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ValueNotifier<int> count = ValueNotifier<int>(0);
  LocalizationsDelegate myLocale;

  @override
  void initState() {
    count.addListener(() {
      setState(() {
        myLocale = MyCountLocale(MyCount(count: count.value.toString()));
      });
    });
    myLocale = MyCountLocale(MyCount(count: count.value.toString()));

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        myLocale,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      home: MyHomePage(count: count),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final ValueNotifier<int> count;

  MyHomePage({this.count});

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      primary: true,
      appBar: AppBar(),
      body: Column(
        children: <Widget>[
          FloatingActionButton(
            onPressed: () => widget.count.value++,
            child: Icon(Icons.plus_one),
          ),
          ListTile(
            title: Text(Localizations.of<MyCount>(context, MyCount).count),
          ),
        ],
      ),
    );
  }
}
Share:
4,812
creativecreatorormaybenot
Author by

creativecreatorormaybenot

Updated on December 05, 2022

Comments

  • creativecreatorormaybenot
    creativecreatorormaybenot over 1 year

    I am encountering a problem, where localization works fine, but the applications needs to be restarted in order for the changes to propagate.

    Orientation changes

    I know about OrientationBuilder, which will call its builder whenever it detects a change in the device's orientation, which in e.g. Android would be considered as a configuration change, just like device language changes.

    Language changes

    Is there something like LanguageBuilder? I could not find anything on my own and not on flutter.io nor on pub. I have read this tutorial and know about Locale, but I do not see a Stream for Locale.

    My problem is that changing the language in iOS and Android native is really smooth. It gets handled automatically and perfectly integrates with services like Firebase Remote Config.

    I really wonder if there is some method that will allow me to refresh my localization.

    Question

    So I am asking how I can refresh my Remote Config when the device language changes.

    • user482594
      user482594 over 4 years
      Hello, did you get a chance to solve this problem?
    • creativecreatorormaybenot
      creativecreatorormaybenot over 4 years
      @user482594 Yes, see my new answer.
  • Rémi Rousselet
    Rémi Rousselet about 6 years
    When you call Localization.of(context, type), this method will make that context rebuild whenever the associated locale change
  • Rémi Rousselet
    Rémi Rousselet about 6 years
    If you don't know what InheritedWidget are, they are typically used to bind a BuildContext to a data and force it to reload on change. Where InheritedWidget is the holder of that data.
  • Bostrot
    Bostrot about 6 years
    You may want to have a look into this. It includes a nice example on how to use the InheritedWidget with updateShouldNotify.
  • Rémi Rousselet
    Rémi Rousselet almost 6 years
    Added an example. Go take a look :)
  • Rémi Rousselet
    Rémi Rousselet almost 6 years
    Here I made a dumb example where your Locale handle all languages at once. Although you could have multiple myLocale each handling a different language using isSupported method (and load your confs using load method)
  • Rémi Rousselet
    Rémi Rousselet almost 6 years
    Maybe make a question just your actual remote config setup. Dunno what can I add here.
  • creativecreatorormaybenot
    creativecreatorormaybenot over 4 years
    Taking a look at my new answer, you might see why I thought that your answer was not a good answer to the question.
  • Fatima ayaa
    Fatima ayaa about 3 years
    is it detectable when I change the language outside the app means in the phone settings ?