Feature/change progress colors (#6)

* Added Settings Actions
* Created App Settings and Repo + Entity
* Code cleanup Time Progress
* Created App Settings Middleware
* Has Progresses ad has Settings loaded
* Created Load and Update Settings reducers
* Added Settings store middleware to renamed store middleware
* Load Default Settings if not Saved. Use Redux AppState to showprogress colors.
Colors are not yet changeable.
* Added ColorPicker for Done and Left Color
Fixed Loading App Settings Bug
* Fixed Version Number
* Fixed Android App Logo
* Extracted Color Settings into Widget
* Fixed Home Settings Tab Layout and Color Settings Button now show Text in complementary color
This commit is contained in:
Andreas Fahrecker 2021-03-03 16:35:08 +01:00 committed by GitHub
parent c580e45361
commit b520d56d1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 545 additions and 150 deletions

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
@ -132,7 +132,6 @@
FF252FCCD702699EBF6FC287 /* Pods-Runner.release.xcconfig */,
F9B8D838B24E4D784CD9D717 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@ -354,7 +353,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@ -482,7 +484,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
@ -505,7 +510,10 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",

View File

@ -4,6 +4,8 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Time Progress Tracker</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>

View File

@ -1,7 +1,24 @@
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
class LoadSettingsAction {}
class AppSettingsLoadedActions {
final AppSettings appSettings;
AppSettingsLoadedActions(this.appSettings);
}
class UpdateAppSettingsActions {
final AppSettings appSettings;
UpdateAppSettingsActions(this.appSettings);
}
class AppSettingsNotLoadedAction {}
class LoadTimeProgressListAction {}
class TimeProgressListLoadedAction {
@ -32,7 +49,10 @@ class DeleteTimeProgressAction {
}
void loadTimeProgressListIfUnloaded(Store<AppState> store) {
if (!store.state.hasLoaded) {
if (!store.state.hasProgressesLoaded)
store.dispatch(LoadTimeProgressListAction());
}
}
void loadSettingsIfUnloaded(Store<AppState> store) {
if (!store.state.hasSettingsLoaded) store.dispatch(LoadSettingsAction());
}

View File

@ -1,23 +1,24 @@
import 'package:flutter/material.dart';
import 'package:package_info/package_info.dart';
import 'package:redux/redux.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:time_progress_tracker/app.dart';
import 'package:time_progress_tracker/middleware/store_time_progress_middleware.dart';
import 'package:time_progress_tracker/middleware/store_middleware.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/persistence/app_settings.dart';
import 'package:time_progress_tracker/persistence/time_progress_repository.dart';
import 'package:time_progress_tracker/reducers/app_state_reducer.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
runApp(TimeProgressTrackerApp(
store: Store<AppState>(
appStateReducer,
initialState: AppState.initial(),
middleware: createStoreTimeProgressListMiddleware(
TimeProgressRepository(await SharedPreferences.getInstance()),
),
middleware: createStoreMiddleware(
TimeProgressRepository(prefs), AppSettingsRepository(prefs)),
),
));
}

View File

@ -1,21 +1,28 @@
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/persistence/app_settings.dart';
import 'package:time_progress_tracker/persistence/time_progress_entity.dart';
import 'package:time_progress_tracker/persistence/time_progress_repository.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
List<Middleware<AppState>> createStoreTimeProgressListMiddleware(
TimeProgressRepository repository) {
final saveTimeProgressList = _createSaveTimeProgressList(repository);
final loadTimeProgressList = _createLoadTimeProgressList(repository);
List<Middleware<AppState>> createStoreMiddleware(
TimeProgressRepository progressRepo, AppSettingsRepository settingsRepo) {
final saveTimeProgressList = _createSaveTimeProgressList(progressRepo);
final loadTimeProgressList = _createLoadTimeProgressList(progressRepo);
final saveSettings = _createSaveAppSettings(settingsRepo);
final loadSettings = _createLoadAppSettings(settingsRepo);
return [
TypedMiddleware<AppState, LoadTimeProgressListAction>(loadTimeProgressList),
TypedMiddleware<AppState, AddTimeProgressAction>(saveTimeProgressList),
TypedMiddleware<AppState, UpdateTimeProgressAction>(saveTimeProgressList),
TypedMiddleware<AppState, DeleteTimeProgressAction>(saveTimeProgressList),
TypedMiddleware<AppState, LoadSettingsAction>(loadSettings),
TypedMiddleware<AppState, UpdateAppSettingsActions>(saveSettings)
];
}
@ -47,3 +54,17 @@ Middleware<AppState> _createLoadTimeProgressList(
}).catchError((_) => store.dispatch(TimeProgressListNotLoadedAction()));
};
}
Middleware<AppState> _createSaveAppSettings(AppSettingsRepository repo) =>
(Store<AppState> store, dynamic action, NextDispatcher next) {
next(action);
repo.saveAppSettings(store.state.appSettings.toEntity());
};
Middleware<AppState> _createLoadAppSettings(AppSettingsRepository repo) =>
(Store<AppState> store, dynamic action, NextDispatcher next) {
repo.loadAppSettings().then((appSettings) {
store.dispatch(
AppSettingsLoadedActions(AppSettings.fromEntity(appSettings)));
});
};

View File

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:time_progress_tracker/persistence/app_settings.dart';
@immutable
class AppSettings {
final Color doneColor;
final Color leftColor;
final Duration duration;
AppSettings({
this.doneColor,
this.leftColor,
this.duration,
});
factory AppSettings.defaults() =>
AppSettings(doneColor: Colors.green, leftColor: Colors.red);
AppSettings copyWith({
Color doneColor,
Color leftColor,
}) =>
AppSettings(
doneColor: doneColor ?? this.doneColor,
leftColor: leftColor ?? this.leftColor);
@override
int get hashCode => doneColor.hashCode ^ leftColor.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppSettings &&
runtimeType == other.runtimeType &&
doneColor == other.doneColor &&
leftColor == other.leftColor;
AppSettingsEntity toEntity() =>
AppSettingsEntity(doneColor.value, leftColor.value);
static AppSettings fromEntity(AppSettingsEntity entity) => AppSettings(
doneColor: Color(entity.doneColorValue),
leftColor: Color(entity.leftColorValue));
}

View File

@ -1,36 +1,40 @@
import 'package:meta/meta.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
@immutable
class AppState {
final bool hasLoaded;
final bool hasProgressesLoaded, hasSettingsLoaded;
final List<TimeProgress> timeProgressList;
final AppSettings appSettings;
AppState({
this.hasLoaded = false,
AppState(
{this.hasProgressesLoaded = false,
this.hasSettingsLoaded = false,
this.timeProgressList = const [],
});
this.appSettings});
factory AppState.initial() => AppState(hasLoaded: false);
factory AppState.initial() =>
AppState(hasProgressesLoaded: false, appSettings: AppSettings.defaults());
AppState copyWith({
bool hasLoaded,
List<TimeProgress> timeProgressList,
}) {
return AppState(
hasLoaded: hasLoaded ?? this.hasLoaded,
hasProgressesLoaded: hasLoaded ?? this.hasProgressesLoaded,
timeProgressList: timeProgressList ?? this.timeProgressList,
);
}
@override
int get hashCode => hasLoaded.hashCode ^ timeProgressList.hashCode;
int get hashCode => hasProgressesLoaded.hashCode ^ timeProgressList.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppState &&
runtimeType == other.runtimeType &&
hasLoaded == other.hasLoaded &&
hasProgressesLoaded == other.hasProgressesLoaded &&
timeProgressList == other.timeProgressList;
}

View File

@ -20,26 +20,19 @@ class TimeProgress {
}
TimeProgress copyWith(
{String id, String name, DateTime startTime, DateTime endTime}) {
return TimeProgress(
{String id, String name, DateTime startTime, DateTime endTime}) =>
TimeProgress(
name ?? this.name,
startTime ?? this.startTime,
endTime ?? this.endTime,
id: id ?? this.id,
);
}
int daysBehind() {
return DateTime.now().difference(startTime).inDays;
}
int daysBehind() => DateTime.now().difference(startTime).inDays;
int daysLeft() {
return endTime.difference(DateTime.now()).inDays;
}
int daysLeft() => endTime.difference(DateTime.now()).inDays;
int allDays() {
return endTime.difference(startTime).inDays;
}
int allDays() => endTime.difference(startTime).inDays;
double percentDone() {
double percent = this.daysBehind() / (this.allDays() / 100) / 100;
@ -48,15 +41,11 @@ class TimeProgress {
return percent;
}
bool hasStarted() {
return DateTime.now().millisecondsSinceEpoch >
startTime.millisecondsSinceEpoch;
}
bool hasStarted() =>
DateTime.now().millisecondsSinceEpoch > startTime.millisecondsSinceEpoch;
bool hasEnded() {
return DateTime.now().millisecondsSinceEpoch >
endTime.millisecondsSinceEpoch;
}
bool hasEnded() =>
DateTime.now().millisecondsSinceEpoch > endTime.millisecondsSinceEpoch;
@override
int get hashCode =>
@ -73,9 +62,8 @@ class TimeProgress {
endTime == other.endTime;
@override
String toString() {
return "TimeProgress{id: $id, name: $name, startTime: $startTime, endTime: $endTime}";
}
String toString() =>
"TimeProgress{id: $id, name: $name, startTime: $startTime, endTime: $endTime}";
TimeProgressEntity toEntity() {
if (!TimeProgress.isNameValid(name))
@ -86,25 +74,17 @@ class TimeProgress {
return TimeProgressEntity(id, name, startTime, endTime);
}
static TimeProgress fromEntity(TimeProgressEntity entity) {
return TimeProgress(
entity.name,
entity.startTime,
entity.endTime,
id: entity.id ?? Uuid().generateV4(),
);
}
static TimeProgress fromEntity(TimeProgressEntity entity) =>
TimeProgress(entity.name, entity.startTime, entity.endTime,
id: entity.id ?? Uuid().generateV4());
static bool isValid(TimeProgress tp) {
return TimeProgress.isNameValid(tp.name) &&
static bool isValid(TimeProgress tp) =>
TimeProgress.isNameValid(tp.name) &&
TimeProgress.areTimesValid(tp.startTime, tp.endTime);
}
static bool isNameValid(String name) {
return name != null && name != "" && name.length > 2 && name.length < 21;
}
static bool isNameValid(String name) =>
name != null && name != "" && name.length > 2 && name.length < 21;
static bool areTimesValid(DateTime startTime, DateTime endTime) {
return startTime.isBefore(endTime);
}
static bool areTimesValid(DateTime startTime, DateTime endTime) =>
startTime.isBefore(endTime);
}

View File

@ -0,0 +1,49 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
class AppSettingsRepository {
static const String _key = "app_settings";
final SharedPreferences prefs;
final JsonCodec codec;
AppSettingsRepository(this.prefs, {this.codec = json});
Future<AppSettingsEntity> loadAppSettings() {
final String jsonString = this.prefs.getString(_key);
if (jsonString == null)
return Future<AppSettingsEntity>.value(AppSettingsEntity.defaults());
return Future<AppSettingsEntity>.value(
AppSettingsEntity.fromJson(codec.decode(jsonString)));
}
Future<bool> saveAppSettings(AppSettingsEntity appSettings) =>
this.prefs.setString(_key, codec.encode(appSettings));
}
class AppSettingsEntity {
static const String _doneKey = "doneColorValue", _leftKey = "leftColorValue";
final int doneColorValue, leftColorValue;
AppSettingsEntity(this.doneColorValue, this.leftColorValue);
factory AppSettingsEntity.defaults() => AppSettings.defaults().toEntity();
@override
int get hashCode => doneColorValue.hashCode ^ leftColorValue.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppSettingsEntity &&
runtimeType == other.runtimeType &&
doneColorValue == other.doneColorValue &&
leftColorValue == other.leftColorValue;
Map<String, Object> toJson() =>
{_doneKey: doneColorValue, _leftKey: leftColorValue};
static AppSettingsEntity fromJson(Map<String, Object> json) =>
AppSettingsEntity(json[_doneKey], json[_leftKey]);
}

View File

@ -2,8 +2,6 @@ import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:time_progress_tracker/persistence/time_progress_entity.dart';
import 'dart:developer' as developer;
class TimeProgressRepository {
static const String _key = "time_progress_repo";
final SharedPreferences prefs;

View File

@ -1,10 +1,32 @@
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/reducers/has_loaded_reducer.dart';
import 'package:time_progress_tracker/reducers/time_progress_list_reducer.dart';
AppState appStateReducer(AppState state, dynamic action) {
return AppState(
hasLoaded: hasLoadedReducer(state.hasLoaded, action),
hasProgressesLoaded: hasLoadedReducer(state.hasProgressesLoaded, action),
timeProgressList: timeProgressListReducer(state.timeProgressList, action),
appSettings: appSettingsReducers(state.appSettings, action),
);
}
final appSettingsReducers = combineReducers<AppSettings>([
TypedReducer<AppSettings, AppSettingsLoadedActions>(_loadAppSettings),
TypedReducer<AppSettings, UpdateAppSettingsActions>(_updateAppSettings),
TypedReducer<AppSettings, AppSettingsNotLoadedAction>(_setDefaultSettings)
]);
AppSettings _loadAppSettings(
AppSettings appSettings, AppSettingsLoadedActions nS) =>
nS.appSettings;
AppSettings _setDefaultSettings(
AppSettings appSettings, AppSettingsNotLoadedAction action) =>
AppSettings.defaults();
AppSettings _updateAppSettings(
AppSettings appSettings, UpdateAppSettingsActions nS) =>
nS.appSettings;

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/screens/home_screen.dart';
@ -76,8 +77,17 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
List<Widget> columnChildren = [
Expanded(
child: ProgressViewWidget(
timeProgress: _editMode ? _editedProgress : timeProgress),
child: StoreConnector<AppState, AppSettings>(
onInit: loadSettingsIfUnloaded,
converter: (store) => appSettingsSelector(store.state),
builder: (BuildContext context, AppSettings settings) {
return ProgressViewWidget(
timeProgress: _editMode ? _editedProgress : timeProgress,
doneColor: settings.doneColor,
leftColor: settings.leftColor,
);
},
),
)
];
if (_editMode)

View File

@ -1,3 +1,6 @@
import 'dart:ui';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
@ -47,3 +50,9 @@ TimeProgress timeProgressByIdSelector(AppState state, String id) {
return state.timeProgressList
.firstWhere((timeProgress) => timeProgress.id == id, orElse: () => null);
}
AppSettings appSettingsSelector(AppState state) {
return state.appSettings;
}
Color doneColorSelector(AppState state) => state.appSettings.doneColor;

View File

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
@ -6,21 +5,27 @@ import 'package:time_progress_tracker/screens/progress_detail_screen.dart';
class HomeProgressListTile extends StatelessWidget {
final TimeProgress timeProgress;
final Color doneColor;
final Color leftColor;
HomeProgressListTile({
Key key,
@required this.timeProgress,
}) : super(key: key);
@required this.doneColor,
@required this.leftColor,
});
@override
Widget build(BuildContext context) {
Widget listTileSubTitle;
if (timeProgress.hasStarted() && !timeProgress.hasEnded())
listTileSubTitle = LinearPercentIndicator(
center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
center: Text(
"${(timeProgress.percentDone() * 100).floor()} %",
style: TextStyle(color: Colors.white),
),
percent: timeProgress.percentDone(),
progressColor: Colors.green,
backgroundColor: Colors.red,
progressColor: doneColor,
backgroundColor: leftColor,
lineHeight: 20,
);
if (!timeProgress.hasStarted())

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
import 'package:time_progress_tracker/widgets/home/home_progress_list_tile.dart';
@ -10,16 +12,21 @@ class HomeActiveProgressesTab extends StatelessWidget {
Widget build(BuildContext context) {
return StoreConnector(
onInit: loadTimeProgressListIfUnloaded,
converter: (store) => store.state.hasLoaded,
builder: (BuildContext context, dynamic hasLoaded) {
converter: (store) => store.state.hasProgressesLoaded,
builder: (context, hasLoaded) {
if (!(hasLoaded as bool))
return Center(
child: CircularProgressIndicator(),
);
return StoreConnector(
onInit: loadTimeProgressListIfUnloaded,
return StoreConnector<AppState, AppSettings>(
onInit: loadSettingsIfUnloaded,
converter: (store) => appSettingsSelector(store.state),
builder: (context, AppSettings settings) {
if (settings == null)
return Center(child: CircularProgressIndicator());
return StoreConnector<AppState, List<TimeProgress>>(
converter: (store) => activeTimeProgressesSelector(store.state),
builder: (BuildContext context, List<TimeProgress> timeProgresses) {
builder: (context, List<TimeProgress> timeProgresses) {
if (timeProgresses.length < 1)
return Container(
padding: EdgeInsets.all(16),
@ -33,6 +40,8 @@ class HomeActiveProgressesTab extends StatelessWidget {
children: timeProgresses
.map((timeProgress) => HomeProgressListTile(
timeProgress: timeProgress,
doneColor: settings.doneColor,
leftColor: settings.leftColor,
))
.toList(),
);
@ -40,5 +49,7 @@ class HomeActiveProgressesTab extends StatelessWidget {
);
},
);
},
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
import 'package:time_progress_tracker/widgets/home/home_progress_list_tile.dart';
@ -8,9 +9,13 @@ import 'package:time_progress_tracker/widgets/home/home_progress_list_tile.dart'
class HomeInactiveProgressesTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreConnector(
onInit: loadSettingsIfUnloaded,
converter: (store) => appSettingsSelector(store.state),
builder: (context, AppSettings settings) {
return StoreConnector(
onInit: loadTimeProgressListIfUnloaded,
converter: (store) => store.state.hasLoaded,
converter: (store) => store.state.hasProgressesLoaded,
builder: (BuildContext context, dynamic hasLoaded) {
if (!(hasLoaded as bool))
return Center(
@ -19,7 +24,8 @@ class HomeInactiveProgressesTab extends StatelessWidget {
return StoreConnector(
onInit: loadTimeProgressListIfUnloaded,
converter: (store) => inactiveTimeProgressesSelector(store.state),
builder: (BuildContext context, List<TimeProgress> timeProgresses) {
builder:
(BuildContext context, List<TimeProgress> timeProgresses) {
if (timeProgresses.length < 1)
return Container(
padding: EdgeInsets.all(16),
@ -31,13 +37,18 @@ class HomeInactiveProgressesTab extends StatelessWidget {
return ListView(
padding: EdgeInsets.all(8),
children: timeProgresses
.map((timeProgress) =>
HomeProgressListTile(timeProgress: timeProgress))
.map((timeProgress) => HomeProgressListTile(
timeProgress: timeProgress,
doneColor: settings.doneColor,
leftColor: settings.leftColor,
))
.toList(),
);
},
);
},
);
},
);
}
}

View File

@ -1,28 +1,62 @@
import 'package:flutter/material.dart';
import 'package:package_info/package_info.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/app.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
import 'package:time_progress_tracker/widgets/home/tabs/settings/color_settings_widget.dart';
class HomeSettingsTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, AppSettings>(
onInit: loadSettingsIfUnloaded,
converter: (store) => appSettingsSelector(store.state),
builder: (context, AppSettings settings) {
Store<AppState> store = StoreProvider.of<AppState>(context);
void updateDoneColor(Color newDoneColor) => store.dispatch(
UpdateAppSettingsActions(
settings.copyWith(doneColor: newDoneColor)),
);
void updateLeftColor(Color newLeftColor) => store.dispatch(
UpdateAppSettingsActions(
settings.copyWith(leftColor: newLeftColor)),
);
return Container(
padding: EdgeInsets.all(16),
child: Center(
child: Column(
children: [
Text("The Settings of this App are not yet implemented."),
FlatButton(
Expanded(
child: ColorSettingsWidget(
doneColor: settings.doneColor,
leftColor: settings.leftColor,
updateDoneColor: updateDoneColor,
updateLeftColor: updateLeftColor,
),
),
Spacer(),
Expanded(
child: TextButton(
onPressed: () {
showAboutDialog(
context: context,
applicationName: TimeProgressTrackerApp.name,
applicationVersion: "Beta",
applicationLegalese: '\u00a9Andreas Fahrecker 2020-2021');
applicationLegalese:
'\u00a9Andreas Fahrecker 2020-2021');
},
child: Text("About"))
child: Text("About"),
),
),
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
class ColorPickerButton extends StatelessWidget {
final String title, dialogTitle;
final Color selectedColor;
final void Function(Color) onColorPicked;
ColorPickerButton({
@required this.title,
@required this.dialogTitle,
@required this.selectedColor,
@required this.onColorPicked,
});
@override
Widget build(BuildContext context) {
Color getBtnPrimaryColor() => Color.fromARGB(
selectedColor.alpha,
selectedColor.alpha - selectedColor.red,
selectedColor.alpha - selectedColor.green,
selectedColor.alpha - selectedColor.blue,
);
return TextButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(dialogTitle),
content: SingleChildScrollView(
child: BlockPicker(
pickerColor: selectedColor,
onColorChanged: onColorPicked,
),
),
);
},
);
},
child: Text(title),
style: TextButton.styleFrom(
primary: getBtnPrimaryColor(),
backgroundColor: selectedColor,
),
);
}
}

View File

@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:time_progress_tracker/widgets/home/tabs/settings/color_picker_btn.dart';
class ColorSettingsWidget extends StatelessWidget {
final Color doneColor, leftColor;
final void Function(Color) updateDoneColor, updateLeftColor;
ColorSettingsWidget({
@required this.doneColor,
@required this.leftColor,
@required this.updateDoneColor,
@required this.updateLeftColor,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: Text(
"Color Settings",
style:
TextStyle(fontWeight: FontWeight.bold, color: Colors.black87),
),
),
Row(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(right: 5),
child: ColorPickerButton(
title: "Done Color",
dialogTitle: "Select Done Color",
selectedColor: doneColor,
onColorPicked: updateDoneColor,
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 5),
child: ColorPickerButton(
title: "Left Color",
dialogTitle: "Select Left Color",
selectedColor: leftColor,
onColorPicked: updateLeftColor,
),
),
),
],
)
],
);
}
}

View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
class DirectSelectItem extends StatelessWidget {
final String title;
final bool isForList;
DirectSelectItem({this.title, this.isForList});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 60,
child: isForList
? Padding(
child: _buildItem(context),
padding: EdgeInsets.all(10),
)
: Card(
margin: EdgeInsets.symmetric(horizontal: 10),
child: Stack(
children: [
_buildItem(context),
Align(
alignment: Alignment.centerRight,
child: Icon(Icons.arrow_drop_down),
)
],
),
));
}
Container _buildItem(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
child: Text(title),
);
}
}

View File

@ -5,9 +5,13 @@ import 'package:time_progress_tracker/models/time_progress.dart';
class ProgressViewWidget extends StatelessWidget {
final TimeProgress timeProgress;
final Color doneColor;
final Color leftColor;
ProgressViewWidget({
@required this.timeProgress,
@required this.doneColor,
@required this.leftColor,
});
@override
@ -33,8 +37,8 @@ class ProgressViewWidget extends StatelessWidget {
radius: 100,
lineWidth: 10,
percent: timeProgress.percentDone(),
progressColor: Colors.green,
backgroundColor: Colors.red,
progressColor: doneColor,
backgroundColor: leftColor,
center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
),
),
@ -43,10 +47,13 @@ class ProgressViewWidget extends StatelessWidget {
padding: EdgeInsets.symmetric(horizontal: 15),
percent: timeProgress.percentDone(),
leading: Text("${timeProgress.daysBehind()} Days"),
center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
center: Text(
"${(timeProgress.percentDone() * 100).floor()} %",
style: TextStyle(color: Colors.white),
),
trailing: Text("${timeProgress.daysLeft()} Days"),
progressColor: Colors.green,
backgroundColor: Colors.red,
progressColor: doneColor,
backgroundColor: leftColor,
lineHeight: 25,
),
),

View File

@ -78,6 +78,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
direct_select:
dependency: "direct main"
description:
name: direct_select
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
fake_async:
dependency: transitive
description:
@ -104,6 +111,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_colorpicker:
dependency: "direct main"
description:
name: flutter_colorpicker
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
flutter_launcher_icons:
dependency: "direct dev"
description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.0.14+14
version: 0.0.17+17
environment:
sdk: ">=2.7.0 <3.0.0"
@ -23,7 +23,9 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_colorpicker:
flutter_redux:
direct_select:
meta:
package_info:
percent_indicator: