Michael Feygenberg
Michael Feygenberg

Reputation: 1

Widget not updating with the Inherited widget, it is subscribed to

I have a User Settings Inherited widget, located right above App Theme Inherited widget in the widget tree. App Theme State is subscribed to the User Settings widget to know, exactly what theme to use in the app. But, after updating the AppUserSettingsInheritedWidget, the didChangeDependencies method in the AppThemeState doesn't get called. If i try to use the AppUserSettingsInheritedWidget directly in the App, it still won't work.

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!),
        )
      ),
    );
  }
}


It's been about 3 hours and i still can't understand what's wrong. Hope for your help :3

Upvotes: 0

Views: 38

Answers (0)

Related Questions