Flutter: ChangeNotifierProvider不会监听变化

huangapple go评论76阅读模式
英文:

Flutter: ChangeNotifierProvider does not listen to Changes

问题

以下是你提供的代码的翻译部分:

我对Flutter非常新手。我已经为我的应用程序添加了本地化,并创建了语言切换器。我尝试使用ChangeNotifierProvider来提供语言切换器的更改。它在除底部导航栏之外的所有字符串上都有效。但在更改语言后,它在底部导航栏上表现出奇怪的行为。有时会立即更改导航栏中字符串的语言,有时只有在第二次点击下拉菜单中选择的语言后才会更改。只有在我点击了导航栏项目后,它才会更改导航栏项目的语言。

这是我的代码:

nav_bar.dart

```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: (_) {},
            ),
          ),
        ]
      ),
    );
  }
}
英文:

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}) =&gt; root
? context.findRootAncestorStateOfType&lt;_NavBarState&gt;()
: context.findAncestorStateOfType&lt;_NavBarState&gt;();
@override
State&lt;NavBarScreen&gt; createState() =&gt; _NavBarState();
}
class _NavBarState extends State&lt;NavBarScreen&gt; {
int _selectedTab = 0;
final List _pages = [
Dashboard(),
CodeScreen(),
ImageGeneration(),
TranslateScreen(),
IAP(),
LocalizationAppPage(),
];
_changeTab(int index) {
setState(() {
ChangeNotifierProvider&lt;LocaleProvider&gt;;
_selectedTab = index;
});
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider&lt;LocaleProvider&gt;(
create: (context) =&gt; LocaleProvider(),
builder: (context, child) {
final provider = Provider.of&lt;LocaleProvider&gt;(context);
return MaterialApp (
debugShowCheckedModeBanner: false,
title: &#39;home&#39;,
theme: ThemeData(
fontFamily: &#39;Raleway&#39;,
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) =&gt; _changeTab(index),
selectedItemColor: const Color(0xFFbfeb91),
unselectedItemColor: Colors.grey,
showUnselectedLabels: true,
items:  [
BottomNavigationBarItem(
icon: Icon(Icons.message),
label: &#39;Chat&#39;
),
BottomNavigationBarItem(
icon: Icon(Icons.code),
label: &#39;Code&#39;
),
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: &#39;Shop&#39;
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: &quot;Language&quot;
),
],
),
),
),
),
);
}
);
}
}

locale_provider.dart

class LocaleProvider extends ChangeNotifier {
Locale? _locale;
Locale? get locale =&gt; _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() =&gt; _LanguageWidgetState();
}
class _LanguageWidgetState extends State&lt;LanguageWidget&gt; {
@override
void initState() {
super.initState();
ChangeNotifierProvider&lt;LocaleProvider&gt;;
}
@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&lt;LocaleProvider&gt;(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: &#39;Raleway&#39;, fontSize: 32, color: Color(0xFFbfeb91))),
]
),
);
},
).toList(),
onChanged: (_) {},
),
),]
),
);
}
}

答案1

得分: 1

你需要将MaterialApp包装在一个Consumer Widget中,这样就可以消耗提供者的值的变化,并且语言环境应该会随之改变。

原因是,您注册提供者的上下文与您想要获取提供者的上下文相同。这样做可以避免出现ProviderNotFoundException异常。

更多详细信息请参阅:Consumer Widget

英文:

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

huangapple
  • 本文由 发表于 2023年6月29日 01:52:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76575627.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定