The MaterialApp.Router widget is supposed to rebuild whenever the theme is changed but it is not. When I do a manual hot reload after tapping a color the theme changes. Any ideas?

I'm using the following package versions:

dependencies:
  go_router: ^9.0.0
  flutter_riverpod: ^2.3.6
  riverpod_annotation: ^2.1.1
dev_dependencies:
  build_runner: ^2.4.6
  riverpod_generator: ^2.2.3

Here is my code:

main.dart

void main() {
  runApp(
    ProviderScope(
      child: const MyApp(),
    ),
  );
}

app.dart

class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp.router(
      title: "MyApp",
      routerConfig: goRouter,
      locale: Locale("en"),
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      theme: ref.watch(themeProvider).state,
    );
  }
}

theme_provider.dart

part 'theme_provider.g.dart';

@riverpod
ThemeState theme(ThemeRef ref) {
  return ThemeState(themeRepository: ThemeRepository());
}

class ThemeState extends StateNotifier<ThemeData> {
  IThemeRepository themeRepository;

  ThemeState({required IThemeRepository this.themeRepository})
      : super(AppTheme.gold.getThemeData) {
    _getStoredTheme();
  }

  void setCurrentTheme(AppTheme value) {
    state = value.getThemeData;
    _storeTheme(value);
  }

  Future<void> _getStoredTheme() async {
    String themeName = await themeRepository.GetTheme();
    state = AppTheme.values.byName(themeName).getThemeData;
  }

  Future<bool> _storeTheme(AppTheme appTheme) async {
    return await themeRepository.SetTheme(appTheme.name);
  }
}

app_theme.dart

enum AppTheme {
  gold,
  green,
  red,
  black;

  ThemeData get getThemeData => switch (this) {
        AppTheme.gold => Themes.goldTheme,
        AppTheme.green => Themes.greenTheme,
        AppTheme.red => Themes.redTheme,
        AppTheme.black => Themes.blackTheme,
      };
}

themes.dart

class Themes {
  static ThemeData get baseTheme => ThemeData(
        useMaterial3: true,
        brightness: Brightness.light,
      );
  static ThemeData get goldTheme => baseTheme.copyWith(
        primaryColor: Color(0xffB58A35),
        colorScheme: ColorScheme.fromSeed(seedColor: Color(0xffB58A35)),
      );
  static ThemeData get greenTheme => baseTheme.copyWith(
        primaryColor: Color(0xff347743),
        colorScheme: ColorScheme.fromSeed(seedColor: Color(0xff347743)),
      );
  static ThemeData get redTheme => baseTheme.copyWith(
        primaryColor: Color(0xffD83731),
        colorScheme: ColorScheme.fromSeed(seedColor: Color(0xffD83731)),
      );
  static ThemeData get blackTheme => baseTheme.copyWith(
        primaryColor: Color(0xff313438),
        colorScheme: ColorScheme.highContrastDark(
          primary: Color(0xff313438),
          secondary: Colors.white,
        ),
      );
}

settings_page.dart

Row(
  mainAxisAlignment:
  MainAxisAlignment.spaceEvenly,
  children: [
    GestureDetector(
      child: Container(
        width: 30,
        height: 30,
        decoration: BoxDecoration(
          color: AppTheme
              .gold.getThemeData.primaryColor,
          borderRadius:
          BorderRadius.circular(4),
        ),
      ),
      onTap: () {
        ref
            .read(themeProvider)
            .setCurrentTheme(AppTheme.gold);
      },
    ),
    SizedBox(width: 4),
    GestureDetector(
      child: Container(
        width: 30,
        height: 30,
        decoration: BoxDecoration(
          color: AppTheme
              .green.getThemeData.primaryColor,
          borderRadius:
          BorderRadius.circular(4),
        ),
      ),
      onTap: () {
        ref
            .read(themeProvider)
            .setCurrentTheme(AppTheme.green);
      },
    ),
    SizedBox(width: 4),
    GestureDetector(
      child: Container(
        width: 30,
        height: 30,
        decoration: BoxDecoration(
          color: AppTheme
              .red.getThemeData.primaryColor,
          borderRadius:
          BorderRadius.circular(4),
        ),
      ),
      onTap: () {
        ref
            .read(themeProvider)
            .setCurrentTheme(AppTheme.red);
      },
    ),
    SizedBox(width: 4),
    GestureDetector(
      child: Container(
        width: 30,
        height: 30,
        decoration: BoxDecoration(
          color: AppTheme
              .black.getThemeData.primaryColor,
          borderRadius:
          BorderRadius.circular(4),
        ),
      ),
      onTap: () {
        ref
            .read(themeProvider)
            .setCurrentTheme(AppTheme.black);
      },
    ),
  ],
);
1

There are 1 answers

0
Guillaume Roux On BEST ANSWER

You are using the code generator the wrong way, your ThemeState should be annotated with @riverpod too. Your current themeProvider is wrapping ThemeState with its own provider which means that it won't be notified of a change of state.

Your code should be something like this:

theme_provider.dart

@riverpod
IThemeRepository themeRepository(ThemeRepositoryRef ref) {
  return ThemeRepository();
}

@riverpod
class ThemeState extends _$ThemeState {
  @override
  ThemeData build() {
    _getStoredTheme();
    return AppTheme.gold.getThemeData;
  }

  void setCurrentTheme(AppTheme value) {
    state = value.getThemeData;
    _storeTheme(value);
  }

  Future<void> _getStoredTheme() async {
    final themeRepository = ref.read(themeRepositoryProvider);
    String themeName = await themeRepository.GetTheme();
    state = AppTheme.values.byName(themeName).getThemeData;
  }

  Future<bool> _storeTheme(AppTheme appTheme) async {
    final themeRepository = ref.read(themeRepositoryProvider);
    return await themeRepository.SetTheme(appTheme.name);
  }
}

app.dart

class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return MaterialApp.router(
      // ...
      theme: ref.watch(themeStateProvider),
    );
  }
}