Enable dark mode
This commit is contained in:
parent
a8fd12bc67
commit
bdc8fe074b
|
|
@ -3,6 +3,12 @@ import 'package:flutter/material.dart';
|
|||
// This version must be updated in tandem with the pubspec version.
|
||||
const String kAppVersion = '0.1.2';
|
||||
|
||||
const String kSharedPrefEmail = 'email';
|
||||
const String kSharedPrefPassword = 'password';
|
||||
const String kSharedPrefUrl = 'url';
|
||||
const String kSharedPrefSecret = 'secret';
|
||||
const String kSharedPrefEnableDarkMode = 'enable_dark_mode';
|
||||
|
||||
const int kMinMajorAppVersion = 4;
|
||||
const int kMinMinorAppVersion = 5;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import 'package:invoiceninja_flutter/ui/settings/settings_screen.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:redux_logging/redux_logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/settings_screen.dart';
|
||||
import 'package:invoiceninja_flutter/ui/auth/init_screen.dart';
|
||||
import 'package:invoiceninja_flutter/ui/client/client_screen.dart';
|
||||
import 'package:invoiceninja_flutter/ui/client/edit/client_edit_vm.dart';
|
||||
|
|
@ -14,7 +17,6 @@ import 'package:invoiceninja_flutter/ui/client/view/client_view_vm.dart';
|
|||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/product/edit/product_edit_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/dashboard/dashboard_screen.dart';
|
||||
import 'package:invoiceninja_flutter/ui/product/product_screen.dart';
|
||||
|
|
@ -27,9 +29,9 @@ import 'package:invoiceninja_flutter/redux/product/product_actions.dart';
|
|||
import 'package:invoiceninja_flutter/redux/product/product_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/invoice_screen.dart';
|
||||
import 'package:redux_logging/redux_logging.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
final store = Store<AppState>(appReducer,
|
||||
initialState: AppState(),
|
||||
middleware: []
|
||||
|
|
@ -43,19 +45,23 @@ void main() {
|
|||
LoggingMiddleware<dynamic>.printer(),
|
||||
]));
|
||||
|
||||
runApp(InvoiceNinjaApp(store: store));
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final enableDarkMode = prefs.getBool(kSharedPrefEnableDarkMode);
|
||||
|
||||
runApp(InvoiceNinjaApp(store: store, enableDarkMode: enableDarkMode));
|
||||
}
|
||||
|
||||
class InvoiceNinjaApp extends StatefulWidget {
|
||||
final Store<AppState> store;
|
||||
|
||||
const InvoiceNinjaApp({Key key, this.store}) : super(key: key);
|
||||
final bool enableDarkMode;
|
||||
const InvoiceNinjaApp({Key key, this.store, this.enableDarkMode})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_InvoiceNinjaAppState createState() => _InvoiceNinjaAppState();
|
||||
InvoiceNinjaAppState createState() => InvoiceNinjaAppState();
|
||||
}
|
||||
|
||||
class _InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||
class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -69,7 +75,12 @@ class _InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
|||
],
|
||||
|
||||
// light theme
|
||||
theme: ThemeData().copyWith(
|
||||
theme: widget.enableDarkMode
|
||||
? ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
accentColor: Colors.lightBlueAccent,
|
||||
)
|
||||
: ThemeData().copyWith(
|
||||
//accentColor: Colors.lightBlueAccent,
|
||||
primaryColor: const Color(0xFF117cc1),
|
||||
primaryColorLight: const Color(0xFF5dabf4),
|
||||
|
|
@ -80,14 +91,6 @@ class _InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
|
|||
buttonColor: const Color(0xFF0D5D91),
|
||||
),
|
||||
|
||||
//dark theme
|
||||
/*
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
accentColor: Colors.lightBlueAccent,
|
||||
),
|
||||
*/
|
||||
|
||||
title: 'Invoice Ninja',
|
||||
routes: {
|
||||
InitScreen.route: (context) => InitScreen(),
|
||||
|
|
|
|||
|
|
@ -14,3 +14,9 @@ class LoadStaticSuccess {
|
|||
|
||||
LoadStaticSuccess(this.data);
|
||||
}
|
||||
|
||||
class UserSettingsChanged implements PersistUI {
|
||||
final bool enableDarkMode;
|
||||
|
||||
UserSettingsChanged({this.enableDarkMode});
|
||||
}
|
||||
|
|
@ -105,6 +105,6 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
|
|||
String toString() {
|
||||
//return 'Is Loading: ${this.isLoading}, Invoice: ${this.invoiceUIState.selected}';
|
||||
//return 'Date Formats: ${staticState.dateFormatMap}';
|
||||
return 'Route: ${uiState.currentRoute}';
|
||||
return 'Route: ${uiState.currentRoute}, Dark Mode: ${uiState.enableDarkMode}';
|
||||
}
|
||||
}
|
||||
|
|
@ -24,26 +24,29 @@ List<Middleware<AppState>> createStoreAuthMiddleware([
|
|||
|
||||
void _saveAuthLocal(dynamic action) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setString('email', action.email);
|
||||
prefs.setString('url', action.url);
|
||||
prefs.setString(kSharedPrefEmail, action.email);
|
||||
prefs.setString(kSharedPrefUrl, action.url);
|
||||
|
||||
if (action.password == 'password') {
|
||||
prefs.setString('password', action.password);
|
||||
prefs.setString(kSharedPrefPassword, action.password);
|
||||
}
|
||||
if (action.secret == 'secret') {
|
||||
prefs.setString('secret', action.secret);
|
||||
prefs.setString(kSharedPrefSecret, action.secret);
|
||||
}
|
||||
}
|
||||
|
||||
void _loadAuthLocal(Store<AppState> store, dynamic action) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
final String email = prefs.getString('email') ?? Config.LOGIN_EMAIL;
|
||||
final String password = prefs.getString('password') ?? Config.LOGIN_PASSWORD;
|
||||
final String url = prefs.getString('url') ?? Config.LOGIN_URL;
|
||||
final String secret = prefs.getString('secret') ?? Config.LOGIN_SECRET;
|
||||
|
||||
final String email = prefs.getString(kSharedPrefEmail) ?? Config.LOGIN_EMAIL;
|
||||
final String password = prefs.getString(kSharedPrefPassword) ?? Config.LOGIN_PASSWORD;
|
||||
final String url = prefs.getString(kSharedPrefUrl) ?? Config.LOGIN_URL;
|
||||
final String secret = prefs.getString(kSharedPrefSecret) ?? Config.LOGIN_SECRET;
|
||||
store.dispatch(UserLoginLoaded(email, password, url, secret));
|
||||
|
||||
final bool enableDarkMode = prefs.getBool(kSharedPrefEnableDarkMode) ?? false;
|
||||
store.dispatch(UserSettingsChanged(enableDarkMode: enableDarkMode));
|
||||
|
||||
Navigator.of(action.context).pushReplacementNamed(LoginScreen.route);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_reducer.dart';
|
||||
import 'package:invoiceninja_flutter/redux/company/company_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
|
|
@ -11,12 +12,21 @@ UIState uiReducer(UIState state, dynamic action) {
|
|||
return state.rebuild((b) => b
|
||||
..selectedCompanyIndex = selectedCompanyIndexReducer(state.selectedCompanyIndex, action)
|
||||
..currentRoute = currentRouteReducer(state.currentRoute, action)
|
||||
..enableDarkMode = darkModeReducer(state.enableDarkMode, action)
|
||||
..productUIState.replace(productUIReducer(state.productUIState, action))
|
||||
..clientUIState.replace(clientUIReducer(state.clientUIState, action))
|
||||
..invoiceUIState.replace(invoiceUIReducer(state.invoiceUIState, action))
|
||||
);
|
||||
}
|
||||
|
||||
Reducer<bool> darkModeReducer = combineReducers([
|
||||
TypedReducer<bool, UserSettingsChanged>(updateDarkModeReducer),
|
||||
]);
|
||||
|
||||
bool updateDarkModeReducer(bool enableDarkMode, UserSettingsChanged action) {
|
||||
return action.enableDarkMode;
|
||||
}
|
||||
|
||||
Reducer<String> currentRouteReducer = combineReducers([
|
||||
TypedReducer<String, UpdateCurrentRoute>(updateCurrentRouteReducer),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
|
|||
return _$UIState._(
|
||||
selectedCompanyIndex: 0,
|
||||
currentRoute: LoginScreen.route,
|
||||
enableDarkMode: false,
|
||||
productUIState: ProductUIState(),
|
||||
clientUIState: ClientUIState(),
|
||||
invoiceUIState: InvoiceUIState(),
|
||||
|
|
@ -22,6 +23,7 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
|
|||
|
||||
int get selectedCompanyIndex;
|
||||
String get currentRoute;
|
||||
bool get enableDarkMode;
|
||||
ProductUIState get productUIState;
|
||||
ClientUIState get clientUIState;
|
||||
InvoiceUIState get invoiceUIState;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
|
|||
'currentRoute',
|
||||
serializers.serialize(object.currentRoute,
|
||||
specifiedType: const FullType(String)),
|
||||
'enableDarkMode',
|
||||
serializers.serialize(object.enableDarkMode,
|
||||
specifiedType: const FullType(bool)),
|
||||
'productUIState',
|
||||
serializers.serialize(object.productUIState,
|
||||
specifiedType: const FullType(ProductUIState)),
|
||||
|
|
@ -65,6 +68,10 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
|
|||
result.currentRoute = serializers.deserialize(value,
|
||||
specifiedType: const FullType(String)) as String;
|
||||
break;
|
||||
case 'enableDarkMode':
|
||||
result.enableDarkMode = serializers.deserialize(value,
|
||||
specifiedType: const FullType(bool)) as bool;
|
||||
break;
|
||||
case 'productUIState':
|
||||
result.productUIState.replace(serializers.deserialize(value,
|
||||
specifiedType: const FullType(ProductUIState)) as ProductUIState);
|
||||
|
|
@ -90,6 +97,8 @@ class _$UIState extends UIState {
|
|||
@override
|
||||
final String currentRoute;
|
||||
@override
|
||||
final bool enableDarkMode;
|
||||
@override
|
||||
final ProductUIState productUIState;
|
||||
@override
|
||||
final ClientUIState clientUIState;
|
||||
|
|
@ -102,6 +111,7 @@ class _$UIState extends UIState {
|
|||
_$UIState._(
|
||||
{this.selectedCompanyIndex,
|
||||
this.currentRoute,
|
||||
this.enableDarkMode,
|
||||
this.productUIState,
|
||||
this.clientUIState,
|
||||
this.invoiceUIState})
|
||||
|
|
@ -110,6 +120,8 @@ class _$UIState extends UIState {
|
|||
throw new BuiltValueNullFieldError('UIState', 'selectedCompanyIndex');
|
||||
if (currentRoute == null)
|
||||
throw new BuiltValueNullFieldError('UIState', 'currentRoute');
|
||||
if (enableDarkMode == null)
|
||||
throw new BuiltValueNullFieldError('UIState', 'enableDarkMode');
|
||||
if (productUIState == null)
|
||||
throw new BuiltValueNullFieldError('UIState', 'productUIState');
|
||||
if (clientUIState == null)
|
||||
|
|
@ -131,6 +143,7 @@ class _$UIState extends UIState {
|
|||
if (other is! UIState) return false;
|
||||
return selectedCompanyIndex == other.selectedCompanyIndex &&
|
||||
currentRoute == other.currentRoute &&
|
||||
enableDarkMode == other.enableDarkMode &&
|
||||
productUIState == other.productUIState &&
|
||||
clientUIState == other.clientUIState &&
|
||||
invoiceUIState == other.invoiceUIState;
|
||||
|
|
@ -139,10 +152,12 @@ class _$UIState extends UIState {
|
|||
@override
|
||||
int get hashCode {
|
||||
return $jf($jc(
|
||||
$jc(
|
||||
$jc(
|
||||
$jc(
|
||||
$jc($jc(0, selectedCompanyIndex.hashCode),
|
||||
currentRoute.hashCode),
|
||||
enableDarkMode.hashCode),
|
||||
productUIState.hashCode),
|
||||
clientUIState.hashCode),
|
||||
invoiceUIState.hashCode));
|
||||
|
|
@ -153,6 +168,7 @@ class _$UIState extends UIState {
|
|||
return (newBuiltValueToStringHelper('UIState')
|
||||
..add('selectedCompanyIndex', selectedCompanyIndex)
|
||||
..add('currentRoute', currentRoute)
|
||||
..add('enableDarkMode', enableDarkMode)
|
||||
..add('productUIState', productUIState)
|
||||
..add('clientUIState', clientUIState)
|
||||
..add('invoiceUIState', invoiceUIState))
|
||||
|
|
@ -172,6 +188,11 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
|
|||
String get currentRoute => _$this._currentRoute;
|
||||
set currentRoute(String currentRoute) => _$this._currentRoute = currentRoute;
|
||||
|
||||
bool _enableDarkMode;
|
||||
bool get enableDarkMode => _$this._enableDarkMode;
|
||||
set enableDarkMode(bool enableDarkMode) =>
|
||||
_$this._enableDarkMode = enableDarkMode;
|
||||
|
||||
ProductUIStateBuilder _productUIState;
|
||||
ProductUIStateBuilder get productUIState =>
|
||||
_$this._productUIState ??= new ProductUIStateBuilder();
|
||||
|
|
@ -196,6 +217,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
|
|||
if (_$v != null) {
|
||||
_selectedCompanyIndex = _$v.selectedCompanyIndex;
|
||||
_currentRoute = _$v.currentRoute;
|
||||
_enableDarkMode = _$v.enableDarkMode;
|
||||
_productUIState = _$v.productUIState?.toBuilder();
|
||||
_clientUIState = _$v.clientUIState?.toBuilder();
|
||||
_invoiceUIState = _$v.invoiceUIState?.toBuilder();
|
||||
|
|
@ -223,6 +245,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
|
|||
new _$UIState._(
|
||||
selectedCompanyIndex: selectedCompanyIndex,
|
||||
currentRoute: currentRoute,
|
||||
enableDarkMode: enableDarkMode,
|
||||
productUIState: productUIState.build(),
|
||||
clientUIState: clientUIState.build(),
|
||||
invoiceUIState: invoiceUIState.build());
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/app_drawer.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/company/company_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/redux/company/company_actions.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
|
|
|
|||
|
|
@ -6,13 +6,9 @@ import 'package:invoiceninja_flutter/utils/localization.dart';
|
|||
|
||||
class SettingsList extends StatelessWidget {
|
||||
final SettingsListVM viewModel;
|
||||
final Function onThemeChange;
|
||||
final bool isDark;
|
||||
|
||||
const SettingsList({
|
||||
Key key,
|
||||
@required this.isDark,
|
||||
@required this.onThemeChange,
|
||||
@required this.viewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
|
|
@ -22,8 +18,8 @@ class SettingsList extends StatelessWidget {
|
|||
children: <Widget>[
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalization.of(context).darkMode),
|
||||
value: isDark ?? false,
|
||||
onChanged: (value) => viewModel.onDarkModeChanged(value),
|
||||
value: viewModel.enableDarkMode,
|
||||
onChanged: (value) => viewModel.onDarkModeChanged(context, value),
|
||||
secondary: Icon(Icons.color_lens),
|
||||
),
|
||||
ListTile(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/settings_list.dart';
|
||||
|
|
@ -8,10 +12,6 @@ import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart';
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SettingsListBuilder extends StatelessWidget {
|
||||
//final Function onThemeChange;
|
||||
//final bool isDark;
|
||||
//const SettingsListBuilder(this.onThemeChange, this.isDark, {Key key})
|
||||
// : super(key: key);
|
||||
|
||||
const SettingsListBuilder({Key key}) : super(key: key);
|
||||
|
||||
|
|
@ -28,27 +28,40 @@ class SettingsListBuilder extends StatelessWidget {
|
|||
|
||||
class SettingsListVM {
|
||||
final Function(BuildContext context) onLogoutTapped;
|
||||
final Function(bool value) onDarkModeChanged;
|
||||
final Function(BuildContext context, bool value) onDarkModeChanged;
|
||||
final bool enableDarkMode;
|
||||
|
||||
SettingsListVM({
|
||||
@required this.onLogoutTapped,
|
||||
@required this.onDarkModeChanged,
|
||||
@required this.enableDarkMode,
|
||||
});
|
||||
|
||||
static SettingsListVM fromStore(Store<AppState> store) {
|
||||
void _warnRestart(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
showDialog<AlertDialog>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
semanticLabel: localization.restartAppToApplyChange,
|
||||
title: Text(localization.restartAppToApplyChange),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: Text(localization.ok.toUpperCase()),
|
||||
onPressed: () => Navigator.pop(context))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SettingsListVM(onLogoutTapped: (BuildContext context) {
|
||||
Navigator.popUntil(context, ModalRoute.withName(LoginScreen.route));
|
||||
/*
|
||||
while (Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
Navigator.of(context).pushReplacementNamed(LoginScreen.route);
|
||||
*/
|
||||
store.dispatch(UserLogout());
|
||||
}, onDarkModeChanged: (bool value) async {
|
||||
print('value: $value');
|
||||
}, onDarkModeChanged: (BuildContext context, bool value) async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool('darkMode', value);
|
||||
});
|
||||
prefs.setBool(kSharedPrefEnableDarkMode, value);
|
||||
store.dispatch(UserSettingsChanged(enableDarkMode: value));
|
||||
_warnRestart(context);
|
||||
}, enableDarkMode: store.state.uiState.enableDarkMode);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ class AppLocalization {
|
|||
'done': 'Done',
|
||||
'please_enter_a_client_or_contact_name': 'Please enter a client or contact name',
|
||||
'dark_mode': 'Dark Mode',
|
||||
'restart_app_to_apply_change': 'Restart the app to apply the change',
|
||||
|
||||
'payment': 'Payment',
|
||||
'payments': 'Payments',
|
||||
|
|
@ -354,6 +355,7 @@ class AppLocalization {
|
|||
String get done => _localizedValues[locale.languageCode]['done'];
|
||||
String get pleaseEnterAClientOrContactName => _localizedValues[locale.languageCode]['please_enter_a_client_or_contact_name'];
|
||||
String get darkMode => _localizedValues[locale.languageCode]['dark_mode'];
|
||||
String get restartAppToApplyChange => _localizedValues[locale.languageCode]['restart_app_to_apply_change'];
|
||||
|
||||
String get payment => _localizedValues[locale.languageCode]['payment'];
|
||||
String get payments => _localizedValues[locale.languageCode]['payments'];
|
||||
|
|
|
|||
Loading…
Reference in New Issue