Widget не обновляется, будучи подписанным на обновляющийся Inherited Widget
У меня есть InheritedWidget AppUserSettingsInheritedWidget и его вспомогательные сущности, опрокидывающие данный виджет в дерево и позволяющие обращаться и изменять его. Так же у меня есть InheritedWidget AppThemeInheritedWidget, зависящий от виджета настроек для получения верной темы в нужных частях приложения. Проблема заключается в следующем: даже после вызова updateShouldNotify в AppUserSettingsInheritedWidget, ни метод didChangeDependencies, ни сам build не вызываются в AppThemeState, где мы собственно и подписывались на виджет с настройками. Такая же фигня и в самом приложении. В Дебаггере посмотрел на дерево виджетов и убедился в том что AppUserSettingsInheritedWidget обновляет свои поля, а вот любые виджеты, зависящие от него - нет. Вообще не понимаю, в чем проблема, надеюсь на помощь.
User Settings:
import 'package:flutter/material.dart';
import 'package:helios/common/common.dart';
import 'package:hive/hive.dart';
class AppUserSettings extends StatefulWidget {
const AppUserSettings({
super.key,
required this.child,
});
final Widget child;
static UserSettings? of(BuildContext context, {bool listen = false}) =>
_AppUserSettingsInheritedWidget.of(context, listen: listen).userSettings;
static bool isBoxOpen(BuildContext context, {bool listen = false}) =>
_AppUserSettingsInheritedWidget.of(context, listen: listen).state.boxOpen;
static void update(BuildContext context, UserSettings userSettings) {
_AppUserSettingsInheritedWidget.of(context).state._update(userSettings);
}
static void changeTheme(BuildContext context, SelectedTheme theme) {
_AppUserSettingsInheritedWidget.of(context).state._changeTheme(theme);
}
static void changeSubscription(BuildContext context, SubscriptionType subscriptionType) {
_AppUserSettingsInheritedWidget.of(context).state._changeSubscription(subscriptionType);
}
@override
State<AppUserSettings> createState() => _AppUserSettingsState();
}
class _AppUserSettingsState extends State<AppUserSettings> with WidgetsBindingObserver {
UserSettings? userSettings;
bool boxOpen = false;
void _update(UserSettings userSettings) {
setState(() {
this.userSettings = userSettings;
});
if (Hive.isBoxOpen("UserSettings")) Hive.box("UserSettings").put("userSettings", userSettings);
}
void _changeTheme(SelectedTheme? selectedTheme) {
setState(() {
userSettings?.selectedTheme = selectedTheme;
});
if (Hive.isBoxOpen("UserSettings")) Hive.box("UserSettings").put("userSettings", userSettings);
}
void _changeSubscription(SubscriptionType? subscriptionType) {
setState(() {
userSettings?.subscriptionType = subscriptionType;
});
if (Hive.isBoxOpen("UserSettings")) Hive.box("UserSettings").put("userSettings", userSettings);
}
@override
void initState() {
super.initState();
Hive.openBox("UserSettings")
.then(
(value) {
boxOpen = true;
setState(() {
userSettings = value.get(
"userSettings",
defaultValue: UserSettingsImpl(),
);
});
}
);
}
@override
void dispose() {
super.dispose();
try {
if (Hive.isBoxOpen("UserSettings")) {
Hive.box("UserSettings")
..put("userSettings", userSettings)
..close();
}
} on HiveError catch(error) {
print(error.message);
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.detached) {
dispose();
}
}
@override
Widget build(BuildContext context) => _AppUserSettingsInheritedWidget(
state: this,
userSettings: userSettings,
child: widget.child,
);
}
class _AppUserSettingsInheritedWidget extends InheritedWidget {
const _AppUserSettingsInheritedWidget({
required this.state,
required this.userSettings,
required super.child,
});
final _AppUserSettingsState state;
final UserSettings? userSettings;
static _AppUserSettingsInheritedWidget? maybeof(BuildContext context, {bool listen = false}) => listen
?
context.dependOnInheritedWidgetOfExactType<_AppUserSettingsInheritedWidget>()
:
context.getInheritedWidgetOfExactType<_AppUserSettingsInheritedWidget>();
static _AppUserSettingsInheritedWidget of(BuildContext context, {bool listen = false}) => maybeof(
context,
listen: listen
)!;
@override
bool updateShouldNotify(_AppUserSettingsInheritedWidget oldWidget) =>
(userSettings?.selectedTheme != oldWidget.userSettings?.selectedTheme) || (userSettings?.subscriptionType != oldWidget.userSettings?.subscriptionType);
}
App Theme:
import 'package:flutter/material.dart';
import 'package:helios/common/common.dart';
class AppTheme extends StatefulWidget {
const AppTheme({
super.key,
required this.child
});
final Widget child;
static MyTheme of(BuildContext context, {bool listen = false}) =>
_AppThemeInheritedWidget.of(context, listen: listen).theme;
@override
State<AppTheme> createState() => _AppThemeState();
}
class _AppThemeState extends State<AppTheme> {
late MyTheme theme;
@override
void didChangeDependencies() {
super.didChangeDependencies();
var appSettings = AppUserSettings.of(context, listen: true);
print("AppTheme didChangeDependencies");
setState(() {
theme = getTheme((appSettings ?? UserSettingsImpl()).selectedTheme);
});
}
@override
Widget build(BuildContext context) => _AppThemeInheritedWidget(
theme: theme,
state: this,
child: widget.child
);
}
class _AppThemeInheritedWidget extends InheritedWidget {
const _AppThemeInheritedWidget({
super.key,
required this.theme,
required this.state,
required super.child,
});
final _AppThemeState state;
final MyTheme theme;
static _AppThemeInheritedWidget? maybeof(BuildContext context, {bool listen = false}) => listen
?
context.dependOnInheritedWidgetOfExactType<_AppThemeInheritedWidget>()
:
context.getInheritedWidgetOfExactType<_AppThemeInheritedWidget>();
static _AppThemeInheritedWidget of(BuildContext context, {bool listen = false}) =>
maybeof(
context,
listen: listen
)!;
@override
bool updateShouldNotify(_AppThemeInheritedWidget oldWidget) =>
theme.isLight != oldWidget.theme.isLight;
}
App:
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:helios/common/common.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) =>
const AppUser(
child: AppUserSettings(
child: AppTheme(
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
),
)
)
);
}
class SplashScreen extends StatelessWidget {
const SplashScreen({super.key});
@override
Widget build(BuildContext context) {
bool isSettingsBoxOpen = AppUserSettings.isBoxOpen(context, listen: true);
if(isSettingsBoxOpen) {
WidgetsBinding.instance.addPostFrameCallback((_) => Navigator.of(context).pushReplacement(MaterialPageRoute(builder:
(context) => const HomeScreen()
)));
}
return const Scaffold(
backgroundColor: Colors.black,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Welcome",),
CircularProgressIndicator.adaptive()
],
)
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
late MyTheme theme;
@override
Widget build(BuildContext context) {
theme = AppTheme.of(context, listen: true);
return Scaffold(
backgroundColor: theme.themeData.colorScheme.background,
body: Center(
child: DropdownMenu<SelectedTheme>(
dropdownMenuEntries: SelectedTheme.values.map<DropdownMenuEntry<SelectedTheme>>(
(SelectedTheme selectedTheme) {
return DropdownMenuEntry<SelectedTheme>(
value: selectedTheme,
label: selectedTheme.name,
);
}
).toList(),
initialSelection: (AppUserSettings.of(context) ?? UserSettingsImpl()).selectedTheme,
onSelected: (value) => AppUserSettings.changeTheme(context, value!),
)
),
);
}
}
Уже часа три долблюсь головой над этой проблемой и понятия не имею что может быть не так. Очень надеюсь на вашу помощь :3
Ответы (1 шт):
Проблема заключалась в том, что метод updateShouldNotify
в AppUserSettingsInheritedWidget
все-таки не вызывался, так как метод _changeTheme
изменял userSettings
не только в state
, но и в oldWidget
, в результате чего что в новом, что в старом виджетах находились одинаковые данные.
Исправленные методы:
void _changeTheme(SelectedTheme? selectedTheme) {
setState(() {
UserSettings newUserSettings = UserSettingsImpl();
newUserSettings.selectedTheme = selectedTheme;
newUserSettings.subscriptionType = userSettings?.subscriptionType;
userSettings = newUserSettings;
});
if (Hive.isBoxOpen("UserSettings")) Hive.box("UserSettings").put("userSettings", userSettings);
}
void _changeSubscription(SubscriptionType? subscriptionType) {
setState(() {
UserSettings newUserSettings = UserSettingsImpl();
newUserSettings.selectedTheme = userSettings?.selectedTheme;
newUserSettings.subscriptionType = subscriptionType;
userSettings = newUserSettings;
});
if (Hive.isBoxOpen("UserSettings")) Hive.box("UserSettings").put("userSettings", userSettings);
}