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:
parent
c580e45361
commit
b520d56d1a
@ -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",
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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)),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
@ -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)));
|
||||
});
|
||||
};
|
44
lib/models/app_settings.dart
Normal file
44
lib/models/app_settings.dart
Normal 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));
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
49
lib/persistence/app_settings.dart
Normal file
49
lib/persistence/app_settings.dart
Normal 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]);
|
||||
}
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -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 {
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
49
lib/widgets/home/tabs/settings/color_picker_btn.dart
Normal file
49
lib/widgets/home/tabs/settings/color_picker_btn.dart
Normal 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
55
lib/widgets/home/tabs/settings/color_settings_widget.dart
Normal file
55
lib/widgets/home/tabs/settings/color_settings_widget.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
39
lib/widgets/home/tabs/settings/direct_select_item.dart
Normal file
39
lib/widgets/home/tabs/settings/direct_select_item.dart
Normal 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),
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
14
pubspec.lock
14
pubspec.lock
@ -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:
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user