Reputation: 107
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
Reputation: 389
According to the flutter docs - hot reload is not enabled for flutter web as of 16/07/21
Kia Kaha,
Mike Smith
Upvotes: 2
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()
orinitState()
.
Upvotes: 4