user14757651
user14757651

Reputation: 107

Hot reload doesn't work and need to Hot Restart Flutter

I'm making an app, which has a theme manager, light mode, dark mode, and system mode.

I think that the way I manage the state with redux, prevent me to reload the app with the hot reload, I explain: If I have a scaffold with a background, from the themedata, if the thememode is currently dark, and the background color is Color(0xFF1F1F1F) (a shade of black), and I change it to Color(0xFFFFFFFF) (Pure white), then I use hot reload, it doesn't work, and I need to restart the whole app with hot restart to see changes. The same with my theme manager, I use radiobuttons to manage it, if I change the theme to light or dark, it works fine.

if I change the mode to system, it works fine, but with the system theme mode in which the app were opened, if I change the system theme mode of my phone, it doesn't work. in this case StoreProvider.dispatch doesn't work, store.dispatch neither, I have to use the hot restart. Here the code that can cause the problem. main.dart:

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';

import 'package:async_redux/async_redux.dart';
import 'package:hive/hive.dart';

import 'package:app_name/Redux/States/AppState.dart';
import 'package:app_name/UI/Screens/AppHome.dart';
import 'package:app_name/Utilities/Themes.dart';
import 'package:app_name/Utilities/functions.dart';
import 'package:app_name/Utilities/extensions.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  Box box = await initHive();
  SettingsState settingsState = SettingsState(
    themeMode: box.get("theme").toString().themeModeFromString,
  );

  Store<AppState> store = Store(
    initialState: AppState(settings: settingsState),
  );
  runApp(App(store: store));
}

class App extends StatelessWidget {
  final Store<AppState> store;

  App({
    required this.store,
  });

  Widget build(BuildContext context) {
    return StoreProvider<AppState>(
      store: store,
      child: StoreConnector<AppState, SettingsState>(
        converter: (Store<AppState> store) => store.state.settings,
        builder: (BuildContext context, SettingsState settings) {
         
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            title: 'Carleo',
            theme: lightTheme,
            darkTheme: darkTheme,
            themeMode: settings.themeMode,
            home: Focus(
              onFocusChange: (hasFocus) =>
                  SystemChrome.setEnabledSystemUIOverlays([]),
              autofocus: true,
              descendantsAreFocusable: true,
              child: AppHome(),
            ),
          );
        },
      ),
    );
  }
}

Here the file AppHome.dart:

import 'package:app_name/UI/Widgets/Radio%20buttons.dart';
import 'package:flutter/material.dart';

import 'package:carleo/UI/Screens/SelectAccess.dart';
import 'package:carleo/UI/Screens/Home.dart';
import 'package:carleo/UI/Widgets/CircularIndicator.dart';
import 'package:carleo/Utilities/Database Utilities.dart';
import 'package:app_name/Utilities/Themes.dart';

class AppHome extends StatelessWidget {
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: ThemeGetter.primary(context),
      body: ThemeRadioButton(),
    );
  }
}

Here Radio Buttons.dart:

import 'package:flutter/material.dart';

import 'package:async_redux/async_redux.dart';

import 'package:app_name/Redux/States/AppState.dart';
import 'package:app_name/Redux/Actions/Actions.dart';

class ThemeRadioButton extends StatelessWidget {
  Widget build(BuildContext context) {
    return StoreConnector<AppState, SettingsState>(
      converter: (Store<AppState> store) => store.state.settings,
      builder: (BuildContext context, SettingsState settings) {
        return Wrap(
          children: [
            ListTile(
              leading: Radio<ThemeMode>(
                value: ThemeMode.light,
                fillColor: MaterialStateProperty.all(Colors.black),
                groupValue: settings.themeMode,
                onChanged: (ThemeMode? mode) {
                  StoreProvider.dispatch<AppState>(
                    context,
                    ThemeChanger(
                      payload: mode ?? ThemeMode.light,
                    ),
                  );
                },
              ),
              title: Text("Light"),
            ),
            ListTile(
              leading: Radio<ThemeMode>(
                value: ThemeMode.dark,
                fillColor: MaterialStateProperty.all(Colors.black),
                groupValue: settings.themeMode,
                onChanged: (ThemeMode? mode) {
                  StoreProvider.dispatch<AppState>(
                    context,
                    ThemeChanger(
                      payload: mode ?? ThemeMode.dark,
                    ),
                  );
                },
              ),
              title: Text("Dark"),
            ),
            ListTile(
              leading: Radio<ThemeMode>(
                value: ThemeMode.system,
                fillColor: MaterialStateProperty.all(Colors.black),
                groupValue: settings.themeMode,
                onChanged: (ThemeMode? mode) {
                  StoreProvider.dispatch<AppState>(
                    context,
                    ThemeChanger(
                      payload: mode ?? ThemeMode.system,
                    ),
                  );
                },
              ),
              title: Text("System"),
            ),
          ],
        );
      },
    );
  }
}

Here any other thing that can help:

ThemeData darkTheme = ThemeData(
  backgroundColor: Color(0xFF1F1F1F),
  accentColor: Color(0xFF101217),
  primaryColor: Color(0xFFFFFFFF),
  buttonColor: Color(0xFF0D47A1),
  brightness: Brightness.dark,
);
ThemeData lightTheme = ThemeData(
  backgroundColor: Color(0xFFFFFFFF),
  accentColor: Color(0xFFFFFFFF),
  brightness: Brightness.light,
);

class ThemeGetter {
  static Color primary(BuildContext context) {
    return Theme.of(context).backgroundColor;
  }

  static Color accent(BuildContext context) {
    return Theme.of(context).accentColor;
  }

  static Color contrast(BuildContext context) {
    return Theme.of(context).primaryColor;
  }

  static Color secondary(BuildContext context) {
    return Theme.of(context).buttonColor;
  }
}

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' show ThemeMode;

extension StringEnumExtension on String {
  ThemeMode get themeModeFromString => ThemeMode.values.firstWhere(
        (e) => describeEnum(e) == this,
        orElse: () => ThemeMode.system,
      );
}

extension ThemeModeExtensions on ThemeMode {
  String get name => describeEnum(this);
}

import 'package:flutter/material.dart' show ThemeMode;

class AppState {
  final SettingsState settings;

  AppState({SettingsState? settings})
      : this.settings = settings ?? SettingsState();

  AppState.copy({
    required AppState state,
  }) : this.settings = state.settings;

  AppState copyWith({int? counter, SettingsState? settings}) => AppState(
        settings: settings ?? this.settings,
      );

  @override
  operator ==(Object another) =>
      identical(this, another) ||
      (another is AppState && this.settings == another.settings);

  @override
  int get hashCode => super.hashCode;
}

class SettingsState {
  final ThemeMode themeMode;

  SettingsState({
    ThemeMode? themeMode,
  }) : this.themeMode = themeMode ?? ThemeMode.system;

  SettingsState.copy({
    required SettingsState state,
  }) : this.themeMode = state.themeMode;

  SettingsState copyWith({ThemeMode? themeMode, ThemeMode? radioValue}) =>
      SettingsState(
        themeMode: themeMode ?? this.themeMode,
      );

  @override
  operator ==(Object another) =>
      identical(this, another) &&
      (another is SettingsState && another.themeMode == this.themeMode);

  @override
  int get hashCode => super.hashCode;
}

import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:hive/hive.dart';

import 'package:carleo/Redux/States/AppState.dart';
import 'package:carleo/Utilities/extensions.dart';

class ThemeChanger extends ReduxAction<AppState> {
  final ThemeMode payload;
  ThemeChanger({
    required this.payload,
  });
  @override
  Future<AppState> reduce() async {
    Box box = await Hive.openBox("Settings");
    box.put("theme", payload.name);
    return state.copyWith(
      settings: state.settings.copyWith(
        themeMode: payload,
      ),
    );
  }
}


Any help will be accepted. and thanks in advance.

EDIT: I've noticed that, even if settings.themeMode is ThemeMode.system, and, in the store connector (the one which builds the MaterialApp), the themeData values are correct, if I use later the same color with Theme.of(context) (like I do in the background of the scaffold in AppHome), the color printed is not the correct one, is the color of the last hot restart. I put this in the StoreConnector builder to notice that:

Brightness brightness =
              SchedulerBinding.instance!.window.platformBrightness;
bool darkModeOn = brightness == Brightness.dark;
print(settings.themeMode);
switch (settings.themeMode) {
    case ThemeMode.light:
        print(lightTheme.primaryColor);
        break;
    case ThemeMode.dark:
        print(darkTheme.primaryColor);
        break;
    case ThemeMode.system:
        if (darkModeOn)
            print(darkTheme.primaryColor);
        else
            print(lightTheme.primaryColor);
}

and simply this code to notice that the colors are different in different widgets:

print(Theme.of(context).backgroundColor);

Upvotes: 2

Views: 3244

Answers (2)

MikeSmith
MikeSmith

Reputation: 389

According to the flutter docs - hot reload is not enabled for flutter web as of 16/07/21

Flutter hot reload doc

Kia Kaha,

Mike Smith

Upvotes: 2

Swaminathan V
Swaminathan V

Reputation: 4781

Some code changes to the app’s main() or initState() methods might not be visible in the refreshed UI on hot-reload.

As a general rule, if the modified code is downstream of the root widget’s build() method, then hot reload behaves as expected. However, if the modified code won’t be re-executed as a result of rebuilding the widget tree, then you won’t see its effects after hot reload.

This may be the reason for the change to theme data not reflected after hot-reload since it is defined in the app's main().

So from Flutter documentation

Hot reload loads code changes into the VM and re-builds the widget tree, preserving the app state; it doesn’t rerun main() or initState().

Upvotes: 4

Related Questions