Search code examples
flutterdartflutter-localizationsflutter-change-notifier-provider

Flutter: ChangeNotifierProvider does not listen to Changes


I am very new to flutter. I have added localization to my app and I made a Language Switcher. I am trying to provide the changes of the Language Switcher witch ChangeNotifierProvider. It works on all Strings except the Bottom Navigation Bar. It has a strange behavior after changing language. Sometimes it changes immediately the language of the strings in the NavBar and sometimes only after the second time i tap the chosen language in the Dropdownmenu. It only changes the language of the Navbar Items after i tap a NavBar Item.

Here is my code:

nav_bar.dart

    class NavBarScreen extends StatefulWidget {
  static of(BuildContext context, {bool root = false}) => root
      ? context.findRootAncestorStateOfType<_NavBarState>()
      : context.findAncestorStateOfType<_NavBarState>();
  @override
  State<NavBarScreen> createState() => _NavBarState();
}

class _NavBarState extends State<NavBarScreen> {
  int _selectedTab = 0;

  final List _pages = [
    Dashboard(),
    CodeScreen(),
    ImageGeneration(),
    TranslateScreen(),
    IAP(),
    LocalizationAppPage(),
  ];

  _changeTab(int index) {
    setState(() {
      ChangeNotifierProvider<LocaleProvider>;
      _selectedTab = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<LocaleProvider>(
        create: (context) => LocaleProvider(),
        builder: (context, child) {
          final provider = Provider.of<LocaleProvider>(context);

          return MaterialApp (
              debugShowCheckedModeBanner: false,
              title: 'home',
              theme: ThemeData(
              fontFamily: 'Raleway',
              scaffoldBackgroundColor: Colors.deepPurple.shade100,
              primaryColor: Colors.deepPurpleAccent,
          ),
              locale: provider.locale,
              supportedLocales: L10n.all,
              localizationsDelegates:  [
              S.delegate,
              AppLocalizations.delegate,
              GlobalMaterialLocalizations.delegate,
              GlobalCupertinoLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
          ],
              home: Scaffold(
                backgroundColor: Colors.black,
              body: _pages[_selectedTab],
              bottomNavigationBar:
              Localizations.override(
              context: context,
              locale: provider.locale,
                child: Theme(
                data: ThemeData(
                canvasColor: Colors.black,
                ),
              child:
              BottomNavigationBar(
                backgroundColor: Colors.black,
              currentIndex: _selectedTab,
              onTap: (index) => _changeTab(index),
              selectedItemColor: const Color(0xFFbfeb91),
              unselectedItemColor: Colors.grey,
              showUnselectedLabels: true,
              items:  [
                BottomNavigationBarItem(
                    icon: Icon(Icons.message),
                    label: 'Chat'
                ),
                BottomNavigationBarItem(
                    icon: Icon(Icons.code),
                    label: 'Code'
                ),
                BottomNavigationBarItem(
                    icon: Icon(Icons.image),
                    label: S.of(context).image
                ),
                BottomNavigationBarItem(
                    icon: Icon(Icons.language),
                    label: S.of(context).translate
                ),
                BottomNavigationBarItem(
                    icon: Icon(Icons.shopping_cart),
                    label: 'Shop'
                ),
                BottomNavigationBarItem(
                    icon: Icon(Icons.settings),
                    label: "Language"
                ),
              ],
            ),
          ),
          ),
          ),
          );
        }
    );
  }
}

locale_provider.dart

class LocaleProvider extends ChangeNotifier {
  Locale? _locale;

  Locale? get locale => _locale;

  void setLocale(Locale locale) {
    if (!L10n.all.contains(locale)) return;

    _locale = locale;
    notifyListeners();
  }

  void clearLocale() {
    _locale = null;
    notifyListeners();
  }
}

language_switcher_widget.dart

class LanguageWidget extends StatefulWidget {
   LanguageWidget({super.key});

  @override
  _LanguageWidgetState createState() => _LanguageWidgetState();
}

class _LanguageWidgetState extends State<LanguageWidget> {

@override
  void initState() {
    super.initState();
    ChangeNotifierProvider<LocaleProvider>;
  }

  @override
  Widget build(BuildContext context) {
    var locale = Localizations.localeOf(context);

    return Center(
      child:Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        DropdownButtonHideUnderline(
        child: DropdownButton(
          dropdownColor: Colors.black,
          value: locale,
          icon: Container(color: Colors.black, width: 44),
          items: L10n.all.map(
                (locale) {
              final flag = L10n.getFlag(locale.languageCode);
              final circleFlag = L10n.getCountryCode(locale.languageCode);

              return DropdownMenuItem(
                value: locale,
                onTap: () async {
                  var provider =
                  Provider.of<LocaleProvider>(context, listen: false);
                  provider.setLocale(locale);
                },
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: [
                      CircleFlag(circleFlag, size: 60),
                      const SizedBox(width: 12,),
                      Text(flag, style: const TextStyle(fontFamily: 'Raleway', fontSize: 32, color: Color(0xFFbfeb91))),
                    ]
                ),
              );
            },
          ).toList(),
          onChanged: (_) {},
        ),
      ),]
    ),
    );
  }
}

Solution

  • you have to wrap the MaterialApp with a Consumer Widget then the changes of the providers value are consumed and the locale should be changing.

    The reason of this is, the context where you register the provider is the same where you want to get the provider. It wonders that you don't get a ProviderNotFoundException.

    more details here: Consumer Widget