dcm fix --only-rules=prefer-trailing-comma lib

This commit is contained in:
Dieter Plaetinck
2024-06-07 14:22:37 +03:00
parent 2ff90a0e9c
commit 7f74bff32c
54 changed files with 1632 additions and 1081 deletions

View File

@@ -44,13 +44,16 @@ void showErrorDialog(dynamic exception, BuildContext context) {
onPressed: () {
Navigator.of(ctx).pop();
},
)
),
],
),
);
}
void showHttpExceptionErrorDialog(WgerHttpException exception, BuildContext context) async {
void showHttpExceptionErrorDialog(
WgerHttpException exception,
BuildContext context,
) async {
log('showHttpExceptionErrorDialog: ');
log(exception.toString());
log('-------------------');
@@ -95,7 +98,7 @@ void showHttpExceptionErrorDialog(WgerHttpException exception, BuildContext cont
onPressed: () {
Navigator.of(ctx).pop();
},
)
),
],
),
);
@@ -113,42 +116,43 @@ dynamic showDeleteDialog(
Map<Exercise, List<Log>> exerciseData,
) async {
final res = await showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(confirmDeleteName),
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(confirmDeleteName),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
onPressed: () {
exerciseData[exercise]!.removeWhere((el) => el.id == log.id);
Provider.of<WorkoutPlansProvider>(context, listen: false).deleteLog(
log,
);
onPressed: () {
exerciseData[exercise]!.removeWhere((el) => el.id == log.id);
Provider.of<WorkoutPlansProvider>(context, listen: false).deleteLog(
log,
);
Navigator.of(contextDialog).pop();
Navigator.of(contextDialog).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
);
},
),
],
);
});
),
);
},
),
],
);
},
);
return res;
}

View File

@@ -78,7 +78,8 @@ class MyApp extends StatelessWidget {
),
ChangeNotifierProxyProvider<AuthProvider, ExercisesProvider>(
create: (context) => ExercisesProvider(
WgerBaseProvider(Provider.of<AuthProvider>(context, listen: false))),
WgerBaseProvider(Provider.of<AuthProvider>(context, listen: false)),
),
update: (context, base, previous) =>
previous ?? ExercisesProvider(WgerBaseProvider(base)),
),
@@ -120,8 +121,10 @@ class MyApp extends StatelessWidget {
previous ?? BodyWeightProvider(WgerBaseProvider(base)),
),
ChangeNotifierProxyProvider<AuthProvider, GalleryProvider>(
create: (context) =>
GalleryProvider(Provider.of<AuthProvider>(context, listen: false), []),
create: (context) => GalleryProvider(
Provider.of<AuthProvider>(context, listen: false),
[],
),
update: (context, auth, previous) => previous ?? GalleryProvider(auth, []),
),
ChangeNotifierProxyProvider<AuthProvider, AddExerciseProvider>(
@@ -130,7 +133,7 @@ class MyApp extends StatelessWidget {
),
update: (context, base, previous) =>
previous ?? AddExerciseProvider(WgerBaseProvider(base)),
)
),
],
child: Consumer<AuthProvider>(
builder: (ctx, auth, _) => MaterialApp(

View File

@@ -41,8 +41,10 @@ class MeasurementCategory extends Equatable {
}
MeasurementEntry findEntryById(entryId) {
return entries.firstWhere((entry) => entry.id == entryId,
orElse: () => throw NoSuchEntryException());
return entries.firstWhere(
(entry) => entry.id == entryId,
orElse: () => throw NoSuchEntryException(),
);
}
// Boilerplate

View File

@@ -42,22 +42,28 @@ class NutritionalGoals {
// infer values where we can
if (energy == null) {
if (protein != null && carbohydrates != null && fat != null) {
energy =
protein! * ENERGY_PROTEIN + carbohydrates! * ENERGY_CARBOHYDRATES + fat! * ENERGY_FAT;
energy = protein! * ENERGY_PROTEIN +
carbohydrates! * ENERGY_CARBOHYDRATES +
fat! * ENERGY_FAT;
}
return;
}
// TODO: input validation when the user modifies/creates the plan, to assure energy is high enough
if (protein == null && carbohydrates != null && fat != null) {
protein =
(energy! - carbohydrates! * ENERGY_CARBOHYDRATES - fat! * ENERGY_FAT) / ENERGY_PROTEIN;
protein = (energy! -
carbohydrates! * ENERGY_CARBOHYDRATES -
fat! * ENERGY_FAT) /
ENERGY_PROTEIN;
assert(protein! > 0);
} else if (carbohydrates == null && protein != null && fat != null) {
carbohydrates =
(energy! - protein! * ENERGY_PROTEIN - fat! * ENERGY_FAT) / ENERGY_CARBOHYDRATES;
(energy! - protein! * ENERGY_PROTEIN - fat! * ENERGY_FAT) /
ENERGY_CARBOHYDRATES;
assert(carbohydrates! > 0);
} else if (fat == null && protein != null && carbohydrates != null) {
fat = (energy! - protein! * ENERGY_PROTEIN - carbohydrates! * ENERGY_CARBOHYDRATES) /
fat = (energy! -
protein! * ENERGY_PROTEIN -
carbohydrates! * ENERGY_CARBOHYDRATES) /
ENERGY_FAT;
assert(fat! > 0);
}
@@ -68,7 +74,8 @@ class NutritionalGoals {
energy: energy != null ? energy! / v : null,
protein: protein != null ? protein! / v : null,
carbohydrates: carbohydrates != null ? carbohydrates! / v : null,
carbohydratesSugar: carbohydratesSugar != null ? carbohydratesSugar! / v : null,
carbohydratesSugar:
carbohydratesSugar != null ? carbohydratesSugar! / v : null,
fat: fat != null ? fat! / v : null,
fatSaturated: fatSaturated != null ? fatSaturated! / v : null,
fiber: fiber != null ? fiber! / v : null,
@@ -77,7 +84,10 @@ class NutritionalGoals {
}
bool isComplete() {
return energy != null && protein != null && carbohydrates != null && fat != null;
return energy != null &&
protein != null &&
carbohydrates != null &&
fat != null;
}
/// Convert goals into values.
@@ -110,7 +120,8 @@ class NutritionalGoals {
goals.protein = (100 * protein! * ENERGY_PROTEIN) / energy!;
}
if (carbohydrates != null) {
goals.carbohydrates = (100 * carbohydrates! * ENERGY_CARBOHYDRATES) / energy!;
goals.carbohydrates =
(100 * carbohydrates! * ENERGY_CARBOHYDRATES) / energy!;
}
if (fat != null) {
goals.fat = (100 * fat! * ENERGY_FAT) / energy!;
@@ -140,7 +151,15 @@ class NutritionalGoals {
@override
//ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hash(
energy, protein, carbohydrates, carbohydratesSugar, fat, fatSaturated, fiber, sodium);
energy,
protein,
carbohydrates,
carbohydratesSugar,
fat,
fatSaturated,
fiber,
sodium,
);
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes

View File

@@ -134,7 +134,10 @@ class NutritionalPlan {
return NutritionalGoals();
}
// otherwise, add up all the nutritional values of the meals and use that as goals
final sumValues = meals.fold(NutritionalValues(), (a, b) => a + b.plannedNutritionalValues);
final sumValues = meals.fold(
NutritionalValues(),
(a, b) => a + b.plannedNutritionalValues,
);
return NutritionalGoals(
energy: sumValues.energy,
fat: sumValues.fat,
@@ -209,7 +212,8 @@ class NutritionalPlan {
for (final meal in meals) {
for (final mealItem in meal.mealItems) {
final found = out.firstWhereOrNull(
(e) => e.amount == mealItem.amount && e.ingredientId == mealItem.ingredientId);
(e) => e.amount == mealItem.amount && e.ingredientId == mealItem.ingredientId,
);
if (found == null) {
out.add(mealItem);
@@ -224,8 +228,9 @@ class NutritionalPlan {
List<Log> get dedupDiaryEntries {
final out = <Log>[];
for (final log in diaryEntries) {
final found =
out.firstWhereOrNull((e) => e.amount == log.amount && e.ingredientId == log.ingredientId);
final found = out.firstWhereOrNull(
(e) => e.amount == log.amount && e.ingredientId == log.ingredientId,
);
if (found == null) {
out.add(log);
}

View File

@@ -117,5 +117,13 @@ class NutritionalValues {
@override
//ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hash(
energy, protein, carbohydrates, carbohydratesSugar, fat, fatSaturated, fiber, sodium);
energy,
protein,
carbohydrates,
carbohydratesSugar,
fat,
fatSaturated,
fiber,
sodium,
);
}

View File

@@ -64,11 +64,13 @@ class AuthProvider with ChangeNotifier {
} else if (Platform.isLinux || Platform.isMacOS) {
metadata = {
MANIFEST_KEY_CHECK_UPDATE: Platform.environment[MANIFEST_KEY_CHECK_UPDATE] ?? '',
MANIFEST_KEY_API: Platform.environment[MANIFEST_KEY_API] ?? ''
MANIFEST_KEY_API: Platform.environment[MANIFEST_KEY_API] ?? '',
};
}
} on PlatformException {
throw Exception('An error occurred reading the metadata from AndroidManifest');
throw Exception(
'An error occurred reading the metadata from AndroidManifest',
);
} catch (error) {}
}
}
@@ -98,7 +100,10 @@ class AuthProvider with ChangeNotifier {
}
/// Checking if there is a new version of the application.
Future<bool> applicationUpdateRequired([String? version, Map<String, String>? metadata]) async {
Future<bool> applicationUpdateRequired([
String? version,
Map<String, String>? metadata,
]) async {
metadata ??= this.metadata;
if (!metadata.containsKey(MANIFEST_KEY_CHECK_UPDATE) ||
metadata[MANIFEST_KEY_CHECK_UPDATE] == 'false') {
@@ -122,7 +127,10 @@ class AuthProvider with ChangeNotifier {
String locale = 'en',
}) async {
// Register
final Map<String, String> data = {'username': username, 'password': password};
final Map<String, String> data = {
'username': username,
'password': password,
};
if (email != '') {
data['email'] = email;
}
@@ -132,7 +140,7 @@ class AuthProvider with ChangeNotifier {
HttpHeaders.contentTypeHeader: 'application/json; charset=UTF-8',
HttpHeaders.authorizationHeader: 'Token ${metadata[MANIFEST_KEY_API]}',
HttpHeaders.userAgentHeader: getAppNameHeader(),
HttpHeaders.acceptLanguageHeader: locale
HttpHeaders.acceptLanguageHeader: locale,
},
body: json.encode(data),
);

View File

@@ -192,7 +192,10 @@ class ExercisesProvider with ChangeNotifier {
/// returned exercises. Since this is typically called by one exercise, we are
/// not interested in seeing that same exercise returned in the list of variations.
/// If this parameter is not passed, all exercises are returned.
List<Exercise> findExercisesByVariationId(int id, {int? exerciseBaseIdToExclude}) {
List<Exercise> findExercisesByVariationId(
int id, {
int? exerciseBaseIdToExclude,
}) {
var out = exercises.where((base) => base.variationId == id).toList();
if (exerciseBaseIdToExclude != null) {
@@ -290,7 +293,10 @@ class ExercisesProvider with ChangeNotifier {
/// -> yes: Do we need to re-fetch?
/// -> no: just return what we have in the DB
/// -> yes: fetch data and update if necessary
Future<Exercise> handleUpdateExerciseFromApi(ExerciseDatabase database, int exerciseId) async {
Future<Exercise> handleUpdateExerciseFromApi(
ExerciseDatabase database,
int exerciseId,
) async {
Exercise exercise;
final exerciseDb = await (database.select(database.exercises)
..where((e) => e.id.equals(exerciseId)))
@@ -336,10 +342,11 @@ class ExercisesProvider with ChangeNotifier {
if (exerciseDb == null) {
await database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: exercise.id!,
data: jsonEncode(baseData),
lastUpdate: exercise.lastUpdateGlobal!,
lastFetched: DateTime.now()),
id: exercise.id!,
data: jsonEncode(baseData),
lastUpdate: exercise.lastUpdateGlobal!,
lastFetched: DateTime.now(),
),
);
}
}
@@ -438,8 +445,10 @@ class ExercisesProvider with ChangeNotifier {
}
/// Set the available exercises as available in the db
Future<void> setExercisesFromDatabase(ExerciseDatabase database,
{bool forceDeleteCache = false}) async {
Future<void> setExercisesFromDatabase(
ExerciseDatabase database, {
bool forceDeleteCache = false,
}) async {
if (forceDeleteCache) {
await database.delete(database.exercises).go();
}
@@ -469,10 +478,11 @@ class ExercisesProvider with ChangeNotifier {
if (exercise == null) {
database.into(database.exercises).insert(
ExercisesCompanion.insert(
id: exerciseData['id'],
data: jsonEncode(exerciseData),
lastUpdate: DateTime.parse(exerciseData['last_update_global']),
lastFetched: DateTime.now()),
id: exerciseData['id'],
data: jsonEncode(exerciseData),
lastUpdate: DateTime.parse(exerciseData['last_update_global']),
lastFetched: DateTime.now(),
),
);
}
@@ -481,7 +491,8 @@ class ExercisesProvider with ChangeNotifier {
if (exercise != null && lastUpdateApi.isAfter(exercise.lastUpdate)) {
// TODO: timezones 🥳
print(
'Exercise ${exercise.id}: update API $lastUpdateApi | Update DB: ${exercise.lastUpdate}');
'Exercise ${exercise.id}: update API $lastUpdateApi | Update DB: ${exercise.lastUpdate}',
);
(database.update(database.exercises)..where((e) => e.id.equals(exerciseData['id']))).write(
ExercisesCompanion(
id: Value(exerciseData['id']),
@@ -524,7 +535,10 @@ class ExercisesProvider with ChangeNotifier {
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_MUSCLES, validTill.toIso8601String());
await prefs.setString(
PREFS_LAST_UPDATED_MUSCLES,
validTill.toIso8601String(),
);
log('Wrote ${_muscles.length} muscles from cache. Valid till $validTill');
}
@@ -558,7 +572,10 @@ class ExercisesProvider with ChangeNotifier {
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_CATEGORIES, validTill.toIso8601String());
await prefs.setString(
PREFS_LAST_UPDATED_CATEGORIES,
validTill.toIso8601String(),
);
}
/// Fetches and sets the available languages
@@ -590,7 +607,10 @@ class ExercisesProvider with ChangeNotifier {
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_LANGUAGES, validTill.toIso8601String());
await prefs.setString(
PREFS_LAST_UPDATED_LANGUAGES,
validTill.toIso8601String(),
);
}
/// Fetches and sets the available equipment
@@ -623,15 +643,21 @@ class ExercisesProvider with ChangeNotifier {
);
});
validTill = DateTime.now().add(const Duration(days: EXERCISE_CACHE_DAYS));
await prefs.setString(PREFS_LAST_UPDATED_EQUIPMENT, validTill.toIso8601String());
await prefs.setString(
PREFS_LAST_UPDATED_EQUIPMENT,
validTill.toIso8601String(),
);
}
/// Searches for an exercise
///
/// We could do this locally, but the server has better text searching capabilities
/// with postgresql.
Future<List<Exercise>> searchExercise(String name,
{String languageCode = LANGUAGE_SHORT_ENGLISH, bool searchEnglish = false}) async {
Future<List<Exercise>> searchExercise(
String name, {
String languageCode = LANGUAGE_SHORT_ENGLISH,
bool searchEnglish = false,
}) async {
if (name.length <= 1) {
return [];
}
@@ -651,7 +677,9 @@ class ExercisesProvider with ChangeNotifier {
// Load the ingredients
final results = ExerciseApiSearch.fromJson(result);
return Future.wait(results.suggestions.map((e) => fetchAndSetExercise(e.data.exerciseId)));
return Future.wait(
results.suggestions.map((e) => fetchAndSetExercise(e.data.exerciseId)),
);
}
}

View File

@@ -92,8 +92,9 @@ class NutritionPlansProvider with ChangeNotifier {
/// Fetches and sets all plans sparsely, i.e. only with the data on the plan
/// object itself and no child attributes
Future<void> fetchAndSetAllPlansSparse() async {
final data = await baseProvider
.fetchPaginated(baseProvider.makeUrl(_nutritionalPlansPath, query: {'limit': '1000'}));
final data = await baseProvider.fetchPaginated(
baseProvider.makeUrl(_nutritionalPlansPath, query: {'limit': '1000'}),
);
_plans = [];
for (final planData in data) {
final plan = NutritionalPlan.fromJson(planData);
@@ -253,7 +254,10 @@ class NutritionPlansProvider with ChangeNotifier {
/// Adds a meal item to a meal
Future<MealItem> addMealItem(MealItem mealItem, Meal meal) async {
final data = await baseProvider.post(mealItem.toJson(), baseProvider.makeUrl(_mealItemPath));
final data = await baseProvider.post(
mealItem.toJson(),
baseProvider.makeUrl(_mealItemPath),
);
mealItem = MealItem.fromJson(data);
mealItem.ingredient = await fetchIngredient(mealItem.ingredientId);
@@ -326,7 +330,7 @@ class NutritionPlansProvider with ChangeNotifier {
final ingredientData = {
'date': DateTime.now().toIso8601String(),
'expiresIn': DateTime.now().add(const Duration(days: DAYS_TO_CACHE)).toIso8601String(),
'ingredients': []
'ingredients': [],
};
prefs.setString(PREFS_INGREDIENTS, json.encode(ingredientData));
return;
@@ -349,8 +353,10 @@ class NutritionPlansProvider with ChangeNotifier {
// Send the request
final response = await baseProvider.fetch(
baseProvider
.makeUrl(_ingredientSearchPath, query: {'term': name, 'language': languages.join(',')}),
baseProvider.makeUrl(
_ingredientSearchPath,
query: {'term': name, 'language': languages.join(',')},
),
);
// Process the response
@@ -392,12 +398,19 @@ class NutritionPlansProvider with ChangeNotifier {
}
/// Log custom ingredient to nutrition diary
Future<void> logIngredientToDiary(MealItem mealItem, int planId, [DateTime? dateTime]) async {
Future<void> logIngredientToDiary(
MealItem mealItem,
int planId, [
DateTime? dateTime,
]) async {
final plan = findById(planId);
mealItem.ingredient = await fetchIngredient(mealItem.ingredientId);
final Log log = Log.fromMealItem(mealItem, plan.id!, null, dateTime);
final data = await baseProvider.post(log.toJson(), baseProvider.makeUrl(_nutritionDiaryPath));
final data = await baseProvider.post(
log.toJson(),
baseProvider.makeUrl(_nutritionDiaryPath),
);
log.id = data['id'];
plan.diaryEntries.add(log);
notifyListeners();
@@ -417,7 +430,11 @@ class NutritionPlansProvider with ChangeNotifier {
final data = await baseProvider.fetchPaginated(
baseProvider.makeUrl(
_nutritionDiaryPath,
query: {'plan': plan.id.toString(), 'limit': '999', 'ordering': 'datetime'},
query: {
'plan': plan.id.toString(),
'limit': '999',
'ordering': 'datetime',
},
),
);

View File

@@ -53,8 +53,11 @@ class WorkoutPlansProvider with ChangeNotifier {
List<WeightUnit> _weightUnits = [];
List<RepetitionUnit> _repetitionUnit = [];
WorkoutPlansProvider(this.baseProvider, ExercisesProvider exercises, List<WorkoutPlan> entries)
: _exercises = exercises,
WorkoutPlansProvider(
this.baseProvider,
ExercisesProvider exercises,
List<WorkoutPlan> entries,
) : _exercises = exercises,
_workoutPlans = entries;
List<WorkoutPlan> get items {
@@ -207,7 +210,6 @@ class WorkoutPlansProvider with ChangeNotifier {
fetchComputedSettings(workoutSet); // request!
// Settings
final List<Setting> settings = [];
final settingData = allSettingsData['results'].where((s) => s['set'] == workoutSet.id);
@@ -261,8 +263,10 @@ class WorkoutPlansProvider with ChangeNotifier {
}
Future<WorkoutPlan> addWorkout(WorkoutPlan workout) async {
final data =
await baseProvider.post(workout.toJson(), baseProvider.makeUrl(_workoutPlansUrlPath));
final data = await baseProvider.post(
workout.toJson(),
baseProvider.makeUrl(_workoutPlansUrlPath),
);
final plan = WorkoutPlan.fromJson(data);
_workoutPlans.insert(0, plan);
notifyListeners();
@@ -271,7 +275,9 @@ class WorkoutPlansProvider with ChangeNotifier {
Future<void> editWorkout(WorkoutPlan workout) async {
await baseProvider.patch(
workout.toJson(), baseProvider.makeUrl(_workoutPlansUrlPath, id: workout.id));
workout.toJson(),
baseProvider.makeUrl(_workoutPlansUrlPath, id: workout.id),
);
notifyListeners();
}
@@ -290,7 +296,10 @@ class WorkoutPlansProvider with ChangeNotifier {
}
}
Future<Map<String, dynamic>> fetchLogData(WorkoutPlan workout, Exercise base) async {
Future<Map<String, dynamic>> fetchLogData(
WorkoutPlan workout,
Exercise base,
) async {
final data = await baseProvider.fetch(
baseProvider.makeUrl(
_workoutPlansUrlPath,
@@ -331,7 +340,9 @@ class WorkoutPlansProvider with ChangeNotifier {
unitData['weightUnit'].forEach(
(e) => _weightUnits.add(WeightUnit.fromJson(e)),
);
dev.log("Read workout units data from cache. Valid till ${unitData['expiresIn']}");
dev.log(
"Read workout units data from cache. Valid till ${unitData['expiresIn']}",
);
return;
}
}
@@ -359,7 +370,10 @@ class WorkoutPlansProvider with ChangeNotifier {
* Saves a new day instance to the DB and adds it to the given workout
*/
day.workoutId = workout.id!;
final data = await baseProvider.post(day.toJson(), baseProvider.makeUrl(_daysUrlPath));
final data = await baseProvider.post(
day.toJson(),
baseProvider.makeUrl(_daysUrlPath),
);
day = Day.fromJson(data);
day.sets = [];
workout.days.insert(0, day);
@@ -368,7 +382,10 @@ class WorkoutPlansProvider with ChangeNotifier {
}
Future<void> editDay(Day day) async {
await baseProvider.patch(day.toJson(), baseProvider.makeUrl(_daysUrlPath, id: day.id));
await baseProvider.patch(
day.toJson(),
baseProvider.makeUrl(_daysUrlPath, id: day.id),
);
notifyListeners();
}
@@ -487,7 +504,10 @@ class WorkoutPlansProvider with ChangeNotifier {
}
Future<WorkoutSession> addSession(WorkoutSession session) async {
final data = await baseProvider.post(session.toJson(), baseProvider.makeUrl(_sessionUrlPath));
final data = await baseProvider.post(
session.toJson(),
baseProvider.makeUrl(_sessionUrlPath),
);
final newSession = WorkoutSession.fromJson(data);
notifyListeners();
return newSession;
@@ -497,7 +517,10 @@ class WorkoutPlansProvider with ChangeNotifier {
* Logs
*/
Future<Log> addLog(Log log) async {
final data = await baseProvider.post(log.toJson(), baseProvider.makeUrl(_logsUrlPath));
final data = await baseProvider.post(
log.toJson(),
baseProvider.makeUrl(_logsUrlPath),
);
final newLog = Log.fromJson(data);
log.id = newLog.id;

View File

@@ -48,7 +48,7 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>()
GlobalKey<FormState>(),
];
Widget _controlsBuilder(BuildContext context, ControlsDetails details) {
@@ -75,8 +75,11 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
final baseId = await addExerciseProvider.addExercise();
final base = await exerciseProvider.fetchAndSetExercise(baseId);
final name =
base.getExercise(Localizations.localeOf(context).languageCode).name;
final name = base
.getExercise(
Localizations.localeOf(context).languageCode,
)
.name;
setState(() {
_isLoading = false;
@@ -88,15 +91,19 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
builder: (BuildContext context) {
return AlertDialog(
title: Text(AppLocalizations.of(context).success),
content: Text(AppLocalizations.of(context).cacheWarning),
content: Text(
AppLocalizations.of(context).cacheWarning,
),
actions: [
TextButton(
child: Text(name),
onPressed: () {
Navigator.of(context).pop();
Navigator.pushReplacementNamed(
context, ExerciseDetailScreen.routeName,
arguments: base);
context,
ExerciseDetailScreen.routeName,
arguments: base,
);
},
),
],
@@ -153,8 +160,13 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
],
currentStep: _currentStep,
onStepContinue: () {
if (_keys[_currentStep].currentState?.validate() ?? false) {
_keys[_currentStep].currentState?.save();
if (_keys[_currentStep]
.currentState
?.validate() ??
false) {
_keys[_currentStep]
.currentState
?.save();
if (_currentStep != lastStepIndex) {
setState(() {
@@ -203,7 +215,7 @@ class EmailNotVerified extends StatelessWidget {
leading: const Icon(Icons.warning),
title: Text(AppLocalizations.of(context).unVerifiedEmail),
subtitle: Text(AppLocalizations.of(context)
.contributeExerciseWarning(MIN_ACCOUNT_AGE.toString())),
.contributeExerciseWarning(MIN_ACCOUNT_AGE.toString())),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,

View File

@@ -69,7 +69,10 @@ class AuthScreen extends StatelessWidget {
),
Container(
margin: const EdgeInsets.only(bottom: 20.0),
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 94.0),
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 94.0,
),
child: const Text(
'wger',
style: TextStyle(
@@ -125,8 +128,9 @@ class _AuthCardState extends State<AuthCard> {
final _passwordController = TextEditingController();
final _password2Controller = TextEditingController();
final _emailController = TextEditingController();
final _serverUrlController =
TextEditingController(text: kDebugMode ? DEFAULT_SERVER_TEST : DEFAULT_SERVER_PROD);
final _serverUrlController = TextEditingController(
text: kDebugMode ? DEFAULT_SERVER_TEST : DEFAULT_SERVER_PROD,
);
@override
void initState() {
@@ -253,7 +257,10 @@ class _AuthCardState extends State<AuthCard> {
elevation: 8.0,
child: Container(
width: deviceSize.width * 0.9,
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 0.025 * deviceSize.height),
padding: EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 0.025 * deviceSize.height,
),
child: Form(
key: _formKey,
child: SingleChildScrollView(
@@ -263,9 +270,10 @@ class _AuthCardState extends State<AuthCard> {
TextFormField(
key: const Key('inputUsername'),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).username,
errorMaxLines: 2,
prefixIcon: const Icon(Icons.account_circle)),
labelText: AppLocalizations.of(context).username,
errorMaxLines: 2,
prefixIcon: const Icon(Icons.account_circle),
),
autofillHints: const [AutofillHints.username],
controller: _usernameController,
textInputAction: TextInputAction.next,
@@ -280,7 +288,9 @@ class _AuthCardState extends State<AuthCard> {
return null;
},
inputFormatters: [FilteringTextInputFormatter.deny(RegExp(r'\s\b|\b\s'))],
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r'\s\b|\b\s')),
],
onSaved: (value) {
_authData['username'] = value!;
},
@@ -376,9 +386,10 @@ class _AuthCardState extends State<AuthCard> {
child: TextFormField(
key: const Key('inputServer'),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).customServerUrl,
helperText: AppLocalizations.of(context).customServerHint,
helperMaxLines: 4),
labelText: AppLocalizations.of(context).customServerUrl,
helperText: AppLocalizations.of(context).customServerHint,
helperMaxLines: 4,
),
controller: _serverUrlController,
validator: (value) {
if (Uri.tryParse(value!) == null) {
@@ -412,7 +423,7 @@ class _AuthCardState extends State<AuthCard> {
kDebugMode ? DEFAULT_SERVER_TEST : DEFAULT_SERVER_PROD;
},
),
Text(AppLocalizations.of(context).reset)
Text(AppLocalizations.of(context).reset),
],
),
],
@@ -473,12 +484,15 @@ class _AuthCardState extends State<AuthCard> {
text.substring(0, text.lastIndexOf('?') + 1),
),
Text(
text.substring(text.lastIndexOf('?') + 1, text.length),
text.substring(
text.lastIndexOf('?') + 1,
text.length,
),
style: const TextStyle(
//color: wgerPrimaryColor,
fontWeight: FontWeight.w700,
),
)
),
],
),
),

View File

@@ -45,9 +45,10 @@ class _LogMealScreenState extends State<LogMealScreen> {
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as LogMealArguments;
final meal = args.meal.copyWith(
mealItems: args.meal.mealItems
.map((mealItem) => mealItem.copyWith(amount: mealItem.amount * portionPct / 100))
.toList());
mealItems: args.meal.mealItems
.map((mealItem) => mealItem.copyWith(amount: mealItem.amount * portionPct / 100))
.toList(),
);
return Scaffold(
appBar: AppBar(
@@ -62,7 +63,10 @@ class _LogMealScreenState extends State<LogMealScreen> {
),
child: Column(
children: [
Text(meal.name, style: Theme.of(context).textTheme.headlineSmall),
Text(
meal.name,
style: Theme.of(context).textTheme.headlineSmall,
),
if (meal.mealItems.isEmpty)
const ListTile(title: Text('No ingredients defined yet'))
else
@@ -70,7 +74,7 @@ class _LogMealScreenState extends State<LogMealScreen> {
children: [
const NutritionDiaryheader(),
...meal.mealItems
.map((item) => MealItemWidget(item, viewMode.withAllDetails, false)),
.map((item) => MealItemWidget(item, viewMode.withAllDetails, false)),
const SizedBox(height: 32),
Text(
'Portion: ${portionPct.round()} %',
@@ -92,8 +96,10 @@ class _LogMealScreenState extends State<LogMealScreen> {
TextButton(
child: const Text('Log'),
onPressed: () async {
await Provider.of<NutritionPlansProvider>(context, listen: false)
.logMealToDiary(meal);
await Provider.of<NutritionPlansProvider>(
context,
listen: false,
).logMealToDiary(meal);
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@@ -111,7 +117,9 @@ class _LogMealScreenState extends State<LogMealScreen> {
},
),
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
child: Text(
MaterialLocalizations.of(context).cancelButtonLabel,
),
onPressed: () => Navigator.of(context).pop(),
),
],

View File

@@ -33,20 +33,22 @@ class _LogMealsScreenState extends State<LogMealsScreen> {
@override
Widget build(BuildContext context) {
final nutritionalPlan = ModalRoute.of(context)!.settings.arguments as NutritionalPlan;
final nutritionalPlan =
ModalRoute.of(context)!.settings.arguments as NutritionalPlan;
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).selectMealToLog),
),
body: ListView.builder(
itemCount: nutritionalPlan.meals.length,
itemBuilder: (context, index) => MealWidget(
nutritionalPlan.meals[index],
nutritionalPlan.dedupMealItems,
true,
true,
)),
itemCount: nutritionalPlan.meals.length,
itemBuilder: (context, index) => MealWidget(
nutritionalPlan.meals[index],
nutritionalPlan.dedupMealItems,
true,
true,
),
),
);
}
}

View File

@@ -58,44 +58,49 @@ class MeasurementEntriesScreen extends StatelessWidget {
case MeasurementOptions.delete:
showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(category.name),
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(category.name),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).colorScheme.error),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
onPressed: () {
// Confirmed, delete the workout
Provider.of<MeasurementProvider>(context, listen: false)
.deleteCategory(category.id!);
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
),
],
);
});
onPressed: () {
// Confirmed, delete the workout
Provider.of<MeasurementProvider>(
context,
listen: false,
).deleteCategory(category.id!);
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
),
],
);
},
);
break;
}
},

View File

@@ -75,7 +75,10 @@ class NutritionalPlanScreen extends StatelessWidget {
heroTag: null,
tooltip: AppLocalizations.of(context).logMeal,
onPressed: () {
Navigator.of(context).pushNamed(LogMealsScreen.routeName, arguments: nutritionalPlan);
Navigator.of(context).pushNamed(
LogMealsScreen.routeName,
arguments: nutritionalPlan,
);
},
child: const SvgIcon(
icon: SvgIconData('assets/icons/meal-diary.svg'),
@@ -122,7 +125,7 @@ class NutritionalPlanScreen extends StatelessWidget {
);
} else if (value == NutritionalPlanOptions.delete) {
Provider.of<NutritionPlansProvider>(context, listen: false)
.deletePlan(nutritionalPlan.id!);
.deletePlan(nutritionalPlan.id!);
Navigator.of(context).pop();
}
},
@@ -131,15 +134,17 @@ class NutritionalPlanScreen extends StatelessWidget {
PopupMenuItem<NutritionalPlanOptions>(
value: NutritionalPlanOptions.edit,
child: ListTile(
leading: const Icon(Icons.edit),
title: Text(AppLocalizations.of(context).edit)),
leading: const Icon(Icons.edit),
title: Text(AppLocalizations.of(context).edit),
),
),
const PopupMenuDivider(),
PopupMenuItem<NutritionalPlanOptions>(
value: NutritionalPlanOptions.delete,
child: ListTile(
leading: const Icon(Icons.delete),
title: Text(AppLocalizations.of(context).delete)),
leading: const Icon(Icons.delete),
title: Text(AppLocalizations.of(context).delete),
),
),
];
},
@@ -149,7 +154,10 @@ class NutritionalPlanScreen extends StatelessWidget {
titlePadding: const EdgeInsets.fromLTRB(56, 0, 56, 16),
title: Text(
nutritionalPlan.getLabel(context),
style: Theme.of(context).textTheme.titleLarge?.copyWith(color: appBarForeground),
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: appBarForeground),
),
),
),
@@ -165,13 +173,14 @@ class NutritionalPlanScreen extends StatelessWidget {
child: Center(
child: CircularProgressIndicator(),
),
)
),
],
),
)
: Consumer<NutritionPlansProvider>(
builder: (context, value, child) =>
NutritionalPlanDetailWidget(nutritionalPlan)),
NutritionalPlanDetailWidget(nutritionalPlan),
),
),
],
),

View File

@@ -71,7 +71,8 @@ class _WorkoutPlanScreenState extends State<WorkoutPlanScreen> {
@override
Widget build(BuildContext context) {
const appBarForeground = Colors.white;
final workoutPlan = ModalRoute.of(context)!.settings.arguments as WorkoutPlan;
final workoutPlan =
ModalRoute.of(context)!.settings.arguments as WorkoutPlan;
return Scaffold(
body: CustomScrollView(
@@ -84,7 +85,10 @@ class _WorkoutPlanScreenState extends State<WorkoutPlanScreen> {
titlePadding: const EdgeInsets.fromLTRB(56, 0, 56, 16),
title: Text(
workoutPlan.name,
style: Theme.of(context).textTheme.titleLarge?.copyWith(color: appBarForeground),
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: appBarForeground),
),
),
actions: [
@@ -129,7 +133,8 @@ class _WorkoutPlanScreenState extends State<WorkoutPlanScreen> {
),
FutureBuilder(
future: _loadFullWorkout(context, workoutPlan.id!),
builder: (context, AsyncSnapshot<WorkoutPlan> snapshot) => SliverList(
builder: (context, AsyncSnapshot<WorkoutPlan> snapshot) =>
SliverList(
delegate: SliverChildListDelegate(
[
if (snapshot.connectionState == ConnectionState.waiting)
@@ -146,7 +151,7 @@ class _WorkoutPlanScreenState extends State<WorkoutPlanScreen> {
],
),
),
)
),
],
),
);

View File

@@ -9,20 +9,23 @@ class AddExerciseMultiselectButton<T> extends StatefulWidget {
final FormFieldSetter<List<T?>?>? onSaved;
final Function displayName;
const AddExerciseMultiselectButton(
{super.key,
required this.items,
required this.title,
required this.onChange,
this.initialItems = const [],
this.onSaved,
required this.displayName});
const AddExerciseMultiselectButton({
super.key,
required this.items,
required this.title,
required this.onChange,
this.initialItems = const [],
this.onSaved,
required this.displayName,
});
@override
_AddExerciseMultiselectButtonState createState() => _AddExerciseMultiselectButtonState<T>();
_AddExerciseMultiselectButtonState createState() =>
_AddExerciseMultiselectButtonState<T>();
}
class _AddExerciseMultiselectButtonState<T> extends State<AddExerciseMultiselectButton> {
class _AddExerciseMultiselectButtonState<T>
extends State<AddExerciseMultiselectButton> {
List<T> _selectedItems = [];
@override
@@ -32,8 +35,9 @@ class _AddExerciseMultiselectButtonState<T> extends State<AddExerciseMultiselect
child: MultiSelectDialogField(
initialValue: widget.initialItems,
onSaved: widget.onSaved,
items:
widget.items.map((item) => MultiSelectItem<T>(item, widget.displayName(item))).toList(),
items: widget.items
.map((item) => MultiSelectItem<T>(item, widget.displayName(item)))
.toList(),
onConfirm: (value) {
setState(() {
_selectedItems = value.cast<T>();

View File

@@ -6,7 +6,8 @@ import 'package:provider/provider.dart';
import 'package:wger/providers/add_exercise.dart';
import 'mixins/image_picker_mixin.dart';
class PreviewExerciseImages extends StatelessWidget with ExerciseImagePickerMixin {
class PreviewExerciseImages extends StatelessWidget
with ExerciseImagePickerMixin {
PreviewExerciseImages({
super.key,
required this.selectedImages,
@@ -34,12 +35,14 @@ class PreviewExerciseImages extends StatelessWidget with ExerciseImagePickerMixi
child: Container(
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.5),
borderRadius: const BorderRadius.all(Radius.circular(20)),
borderRadius:
const BorderRadius.all(Radius.circular(20)),
),
child: IconButton(
iconSize: 20,
onPressed: () =>
context.read<AddExerciseProvider>().removeExercise(file.path),
onPressed: () => context
.read<AddExerciseProvider>()
.removeExercise(file.path),
color: Colors.white,
icon: const Icon(Icons.delete),
),
@@ -67,7 +70,7 @@ class PreviewExerciseImages extends StatelessWidget with ExerciseImagePickerMixi
),
),
),
)
),
]),
);
}

View File

@@ -40,7 +40,9 @@ class Step2Variations extends StatelessWidget {
...exerciseProvider.exerciseBasesByVariation[key]!.map(
(base) => Text(
base
.getExercise(Localizations.localeOf(context).languageCode)
.getExercise(
Localizations.localeOf(context).languageCode,
)
.name,
overflow: TextOverflow.ellipsis,
),
@@ -51,41 +53,46 @@ class Step2Variations extends StatelessWidget {
),
Consumer<AddExerciseProvider>(
builder: (ctx, provider, __) => Switch(
value: provider.variationId == key,
onChanged: (state) => provider.variationId = key),
value: provider.variationId == key,
onChanged: (state) => provider.variationId = key,
),
),
],
),
),
// Exercise bases without variations
...exerciseProvider.exercises.where((b) => b.variationId == null).map(
(base) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Text(
base
.getExercise(Localizations.localeOf(context).languageCode)
.name,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 20),
],
),
...exerciseProvider.exercises
.where((b) => b.variationId == null)
.map(
(base) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Text(
base
.getExercise(
Localizations.localeOf(context).languageCode,
)
.name,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 20),
],
),
Consumer<AddExerciseProvider>(
builder: (ctx, provider, __) => Switch(
value: provider.newVariationForExercise == base.id,
onChanged: (state) => provider.newVariationForExercise = base.id,
),
),
Consumer<AddExerciseProvider>(
builder: (ctx, provider, __) => Switch(
value: provider.newVariationForExercise == base.id,
onChanged: (state) => provider.newVariationForExercise = base.id,
),
],
),
),
],
),
),
],
),
),

View File

@@ -13,7 +13,8 @@ class Step5Images extends StatefulWidget {
State<Step5Images> createState() => _Step5ImagesState();
}
class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin {
class _Step5ImagesState extends State<Step5Images>
with ExerciseImagePickerMixin {
@override
Widget build(BuildContext context) {
return Form(
@@ -33,7 +34,8 @@ class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () => pickImages(context, pickFromCamera: true),
onPressed: () =>
pickImages(context, pickFromCamera: true),
icon: const Icon(Icons.camera_alt),
),
IconButton(
@@ -46,7 +48,7 @@ class _Step5ImagesState extends State<Step5Images> with ExerciseImagePickerMixin
Text(
'Only JPEG, PNG and WEBP files below 20 MB are supported',
style: Theme.of(context).textTheme.bodySmall,
)
),
],
),
);

View File

@@ -44,64 +44,81 @@ class MainAppBar extends StatelessWidget implements PreferredSizeWidget {
icon: const Icon(Icons.settings),
onPressed: () async {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(AppLocalizations.of(context).optionsLabel),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
onPressed: () => Navigator.of(context).pop(),
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(AppLocalizations.of(context).optionsLabel),
actions: [
TextButton(
child: Text(
MaterialLocalizations.of(context).closeButtonLabel,
),
],
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
//dense: true,
leading: const Icon(Icons.person),
title: Text(AppLocalizations.of(context).userProfile),
onTap: () => Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).userProfile,
UserProfileForm(context.read<UserProvider>().profile!),
onPressed: () => Navigator.of(context).pop(),
),
],
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
//dense: true,
leading: const Icon(Icons.person),
title: Text(AppLocalizations.of(context).userProfile),
onTap: () => Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).userProfile,
UserProfileForm(
context.read<UserProvider>().profile!,
),
),
),
ListTile(
leading: const Icon(Icons.settings),
onTap: () => Navigator.of(context).pushNamed(SettingsPage.routeName),
title: Text(AppLocalizations.of(context).settingsTitle),
),
ListTile(
leading: const Icon(Icons.info),
onTap: () => Navigator.of(context).pushNamed(AboutPage.routeName),
title: Text(AppLocalizations.of(context).aboutPageTitle),
),
const Divider(),
ListTile(
//dense: true,
leading: const Icon(Icons.exit_to_app),
title: Text(AppLocalizations.of(context).logout),
onTap: () {
context.read<AuthProvider>().logout();
context.read<WorkoutPlansProvider>().clear();
context.read<NutritionPlansProvider>().clear();
context.read<BodyWeightProvider>().clear();
context.read<GalleryProvider>().clear();
context.read<UserProvider>().clear();
),
ListTile(
leading: const Icon(Icons.settings),
onTap: () => Navigator.of(context).pushNamed(SettingsPage.routeName),
title: Text(AppLocalizations.of(context).settingsTitle),
),
ListTile(
leading: const Icon(Icons.info),
onTap: () => Navigator.of(context).pushNamed(AboutPage.routeName),
title: Text(AppLocalizations.of(context).aboutPageTitle),
),
const Divider(),
ListTile(
//dense: true,
leading: const Icon(Icons.exit_to_app),
title: Text(AppLocalizations.of(context).logout),
onTap: () {
context
.read<AuthProvider>()
.logout();
context
.read<WorkoutPlansProvider>()
.clear();
context
.read<NutritionPlansProvider>()
.clear();
context
.read<BodyWeightProvider>()
.clear();
context
.read<GalleryProvider>()
.clear();
context
.read<UserProvider>()
.clear();
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/');
},
),
],
),
);
});
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed('/');
},
),
],
),
);
},
);
},
),
],

View File

@@ -34,37 +34,41 @@ class SettingsPage extends StatefulWidget {
class _SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
final exerciseProvider = Provider.of<ExercisesProvider>(context, listen: false);
final exerciseProvider =
Provider.of<ExercisesProvider>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).settingsTitle),
),
body: ListView(
children: [
ListTile(
// leading: const Icon(Icons.cached),
title: Text(AppLocalizations.of(context).settingsCacheTitle),
subtitle: Text(AppLocalizations.of(context).settingsCacheDescription),
trailing: IconButton(
key: const ValueKey('cacheIcon'),
icon: const Icon(Icons.delete),
onPressed: () async {
await exerciseProvider.clearAllCachesAndPrefs();
appBar: AppBar(
title: Text(AppLocalizations.of(context).settingsTitle),
),
body: ListView(
children: [
ListTile(
// leading: const Icon(Icons.cached),
title: Text(AppLocalizations.of(context).settingsCacheTitle),
subtitle:
Text(AppLocalizations.of(context).settingsCacheDescription),
trailing: IconButton(
key: const ValueKey('cacheIcon'),
icon: const Icon(Icons.delete),
onPressed: () async {
await exerciseProvider.clearAllCachesAndPrefs();
if (context.mounted) {
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context).settingsCacheDeletedSnackbar),
);
if (context.mounted) {
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context)
.settingsCacheDeletedSnackbar),
);
// Find the ScaffoldMessenger in the widget tree
// and use it to show a SnackBar.
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
},
),
)
],
));
// Find the ScaffoldMessenger in the widget tree
// and use it to show a SnackBar.
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
},
),
),
],
),
);
}
}

View File

@@ -108,8 +108,10 @@ class _DashboardCalendarWidgetState extends State<DashboardCalendarWidget>
_events[date] = [];
}
_events[date]!
.add(Event(EventType.measurement, '${category.name}: ${entry.value} ${category.unit}'));
_events[date]!.add(Event(
EventType.measurement,
'${category.name}: ${entry.value} ${category.unit}',
));
}
}
@@ -268,7 +270,7 @@ class _DashboardCalendarWidgetState extends State<DashboardCalendarWidget>
})()),
subtitle: Text(event.description),
//onTap: () => print('$event tapped!'),
))
)),
],
),
),

View File

@@ -87,7 +87,10 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
//textAlign: TextAlign.left,
),
),
MutedText(getShortNutritionValues(meal.plannedNutritionalValues, context)),
MutedText(getShortNutritionValues(
meal.plannedNutritionalValues,
context,
)),
IconButton(
icon: const SvgIcon(
icon: SvgIconData('assets/icons/meal-diary.svg'),
@@ -99,7 +102,7 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
arguments: LogMealArguments(meal, false),
);
},
)
),
],
),
);
@@ -120,7 +123,9 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
),
),
const SizedBox(width: 5),
Text('${item.amount.toStringAsFixed(0)} ${AppLocalizations.of(context).g}'),
Text(
'${item.amount.toStringAsFixed(0)} ${AppLocalizations.of(context).g}',
),
],
),
],
@@ -149,7 +154,7 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
subtitle: Text(
_hasContent
? DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(_plan!.creationDate)
.format(_plan!.creationDate)
: '',
),
leading: Icon(
@@ -163,7 +168,8 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
viewMode.base => const Icon(Icons.info_outline),
viewMode.withMeals => const Icon(Icons.info),
viewMode.withMealsDetails => const Icon(Icons.info),
})
},
)
: const SizedBox(),
onTap: () {
setState(() {
@@ -177,16 +183,17 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
),
if (_hasContent)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
...getContent(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 15),
child: FlNutritionalPlanGoalWidget(nutritionalPlan: _plan!),
),
],
))
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
...getContent(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 15),
child: FlNutritionalPlanGoalWidget(nutritionalPlan: _plan!),
),
],
),
)
else
NothingFound(
AppLocalizations.of(context).noNutritionalPlans,
@@ -200,13 +207,17 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
TextButton(
child: Text(AppLocalizations.of(context).goToDetailPage),
onPressed: () {
Navigator.of(context)
.pushNamed(NutritionalPlanScreen.routeName, arguments: _plan);
Navigator.of(context).pushNamed(
NutritionalPlanScreen.routeName,
arguments: _plan,
);
},
),
Expanded(child: Container()),
IconButton(
icon: const SvgIcon(icon: SvgIconData('assets/icons/ingredient-diary.svg')),
icon: const SvgIcon(
icon: SvgIconData('assets/icons/ingredient-diary.svg'),
),
tooltip: AppLocalizations.of(context).logIngredient,
onPressed: () {
Navigator.pushNamed(
@@ -221,7 +232,9 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
},
),
IconButton(
icon: const SvgIcon(icon: SvgIconData('assets/icons/meal-diary.svg')),
icon: const SvgIcon(
icon: SvgIconData('assets/icons/meal-diary.svg'),
),
tooltip: AppLocalizations.of(context).logMeal,
onPressed: () {
Navigator.of(context).pushNamed(LogMealsScreen.routeName, arguments: _plan);
@@ -269,21 +282,25 @@ class _DashboardWeightWidgetState extends State<DashboardWeightWidget> {
SizedBox(
height: 200,
child: MeasurementChartWidgetFl(
weightProvider.items
.map((e) => MeasurementChartEntry(e.weight, e.date))
.toList(),
unit: profile!.isMetric
? AppLocalizations.of(context).kg
: AppLocalizations.of(context).lb),
weightProvider.items
.map((e) => MeasurementChartEntry(e.weight, e.date))
.toList(),
unit: profile!.isMetric
? AppLocalizations.of(context).kg
: AppLocalizations.of(context).lb,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
TextButton(
child: Text(AppLocalizations.of(context).goToDetailPage),
onPressed: () {
Navigator.of(context).pushNamed(WeightScreen.routeName);
}),
child: Text(
AppLocalizations.of(context).goToDetailPage,
),
onPressed: () {
Navigator.of(context).pushNamed(WeightScreen.routeName);
},
),
IconButton(
icon: const Icon(Icons.add),
onPressed: () async {
@@ -292,7 +309,9 @@ class _DashboardWeightWidgetState extends State<DashboardWeightWidget> {
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).newEntry,
WeightForm(weightProvider.getNewestEntry()?.copyWith(id: null)),
WeightForm(weightProvider
.getNewestEntry()
?.copyWith(id: null)),
),
);
},
@@ -330,13 +349,13 @@ class _DashboardMeasurementWidgetState extends State<DashboardMeasurementWidget>
final provider = Provider.of<MeasurementProvider>(context, listen: false);
final items = provider.categories
.map<Widget>(
(item) => CategoriesCard(
item,
elevation: 0,
),
)
.toList();
.map<Widget>(
(item) => CategoriesCard(
item,
elevation: 0,
),
)
.toList();
if (items.isNotEmpty) {
items.add(
NothingFound(
@@ -393,20 +412,28 @@ class _DashboardMeasurementWidgetState extends State<DashboardMeasurementWidget>
padding: const EdgeInsets.only(bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: items.asMap().entries.map((entry) {
children: items
.asMap()
.entries
.map((entry) {
return GestureDetector(
onTap: () => _controller.animateToPage(entry.key),
child: Container(
width: 12.0,
height: 12.0,
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
margin: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 4.0,
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.textTheme
.headlineSmall!
.color!
.withOpacity(_current == entry.key ? 0.9 : 0.4),
.textTheme
.headlineSmall!
.color!
.withOpacity(
_current == entry.key ? 0.9 : 0.4,
),
),
),
);
@@ -501,7 +528,9 @@ class _DashboardWorkoutWidgetState extends State<DashboardWorkoutWidget> {
.getExercise(Localizations.localeOf(context).languageCode)
.name),
const SizedBox(width: 10),
MutedText(set.getSmartRepr(s.exerciseObj).join('\n')),
MutedText(
set.getSmartRepr(s.exerciseObj).join('\n'),
),
],
),
const SizedBox(height: 10),
@@ -532,7 +561,7 @@ class _DashboardWorkoutWidgetState extends State<DashboardWorkoutWidget> {
subtitle: Text(
_hasContent
? DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(_workoutPlan!.creationDate)
.format(_workoutPlan!.creationDate)
: '',
),
leading: Icon(
@@ -546,7 +575,8 @@ class _DashboardWorkoutWidgetState extends State<DashboardWorkoutWidget> {
? const Icon(
Icons.info,
)
: const Icon(Icons.info_outline))
: const Icon(Icons.info_outline),
)
: const SizedBox(),
onTap: () {
setState(() {
@@ -576,12 +606,14 @@ class _DashboardWorkoutWidgetState extends State<DashboardWorkoutWidget> {
TextButton(
child: Text(AppLocalizations.of(context).goToDetailPage),
onPressed: () {
Navigator.of(context)
.pushNamed(WorkoutPlanScreen.routeName, arguments: _workoutPlan);
Navigator.of(context).pushNamed(
WorkoutPlanScreen.routeName,
arguments: _workoutPlan,
);
},
),
],
)
),
],
),
);

View File

@@ -71,7 +71,7 @@ class ExerciseDetail extends StatelessWidget {
...getMuscles(context),
// Variants
...getVariations(context)
...getVariations(context),
],
),
);
@@ -217,9 +217,10 @@ class ExerciseDetail extends StatelessWidget {
.map((e) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Chip(
label: Text(getTranslation(e.name, context)),
padding: EdgeInsets.zero,
backgroundColor: theme.splashColor),
label: Text(getTranslation(e.name, context)),
padding: EdgeInsets.zero,
backgroundColor: theme.splashColor,
),
))
.forEach((element) => out.add(element));
}
@@ -244,8 +245,9 @@ class ExerciseDetail extends StatelessWidget {
final List<Widget> out = [];
if (_exercise.aliases.isNotEmpty) {
out.add(MutedText(
AppLocalizations.of(context)
.alsoKnownAs(_exercise.aliases.map((e) => e.alias).toList().join(', ')),
AppLocalizations.of(context).alsoKnownAs(
_exercise.aliases.map((e) => e.alias).toList().join(', '),
),
));
out.add(const SizedBox(height: PADDING));
}
@@ -287,7 +289,10 @@ class MuscleRowWidget extends StatelessWidget {
final List<Muscle> muscles;
final List<Muscle> musclesSecondary;
const MuscleRowWidget({required this.muscles, required this.musclesSecondary});
const MuscleRowWidget({
required this.muscles,
required this.musclesSecondary,
});
@override
Widget build(BuildContext context) {
@@ -342,8 +347,9 @@ class MuscleWidget extends StatelessWidget {
children: [
SvgPicture.asset('assets/images/muscles/$background.svg'),
...muscles.map((m) => SvgPicture.asset('assets/images/muscles/main/muscle-${m.id}.svg')),
...musclesSecondary
.map((m) => SvgPicture.asset('assets/images/muscles/secondary/muscle-${m.id}.svg')),
...musclesSecondary.map((m) => SvgPicture.asset(
'assets/images/muscles/secondary/muscle-${m.id}.svg',
)),
],
);
}

View File

@@ -41,10 +41,11 @@ class _FilterRowState extends State<FilterRow> {
_exerciseNameController = TextEditingController()
..addListener(
() {
final provider = Provider.of<ExercisesProvider>(context, listen: false);
final provider =
Provider.of<ExercisesProvider>(context, listen: false);
if (provider.filters!.searchTerm != _exerciseNameController.text) {
provider
.setFilters(provider.filters!.copyWith(searchTerm: _exerciseNameController.text));
provider.setFilters(provider.filters!
.copyWith(searchTerm: _exerciseNameController.text));
}
},
);
@@ -90,8 +91,9 @@ class _FilterRowState extends State<FilterRow> {
return [
PopupMenuItem<ExerciseMoreOption>(
value: ExerciseMoreOption.ADD_EXERCISE,
child: Text(AppLocalizations.of(context).contributeExercise),
)
child:
Text(AppLocalizations.of(context).contributeExercise),
),
];
},
shape: const RoundedRectangleBorder(
@@ -100,14 +102,15 @@ class _FilterRowState extends State<FilterRow> {
onSelected: (ExerciseMoreOption selectedOption) {
switch (selectedOption) {
case ExerciseMoreOption.ADD_EXERCISE:
Navigator.of(context).pushNamed(AddExerciseScreen.routeName);
Navigator.of(context)
.pushNamed(AddExerciseScreen.routeName);
break;
}
},
icon: const Icon(Icons.more_vert),
)
),
],
)
),
],
),
);

View File

@@ -37,6 +37,7 @@ class ExerciseImageWidget extends StatelessWidget {
: const Image(
image: AssetImage('assets/images/placeholder.png'),
color: Color.fromRGBO(255, 255, 255, 0.3),
colorBlendMode: BlendMode.modulate);
colorBlendMode: BlendMode.modulate,
);
}
}

View File

@@ -59,7 +59,8 @@ class _ExerciseVideoWidgetState extends State<ExerciseVideoWidget> {
VideoPlayer(_controller),
_ControlsOverlay(controller: _controller),
VideoProgressIndicator(_controller, allowScrubbing: true),
]))
]),
)
: Container();
}
}
@@ -118,7 +119,7 @@ class _ControlsOverlay extends StatelessWidget {
PopupMenuItem(
value: speed,
child: Text('${speed}x'),
)
),
];
},
child: Padding(

View File

@@ -116,12 +116,15 @@ class _ImageFormState extends State<ImageForm> {
title: Text(AppLocalizations.of(context).takePicture),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
_showPicker(ImageSource.gallery);
},
leading: const Icon(Icons.photo_library),
title: Text(AppLocalizations.of(context).chooseFromLibrary))
onTap: () {
Navigator.of(context).pop();
_showPicker(ImageSource.gallery);
},
leading: const Icon(Icons.photo_library),
title: Text(
AppLocalizations.of(context).chooseFromLibrary,
),
),
],
),
);
@@ -166,7 +169,9 @@ class _ImageFormState extends State<ImageForm> {
),
TextFormField(
key: const Key('field-description'),
decoration: InputDecoration(labelText: AppLocalizations.of(context).description),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).description,
),
minLines: 3,
maxLines: 10,
controller: descriptionController,
@@ -187,11 +192,11 @@ class _ImageFormState extends State<ImageForm> {
if (widget._image.id == null) {
Provider.of<GalleryProvider>(context, listen: false)
.addImage(widget._image, _file!);
.addImage(widget._image, _file!);
Navigator.of(context).pop();
} else {
Provider.of<GalleryProvider>(context, listen: false)
.editImage(widget._image, _file);
.editImage(widget._image, _file);
Navigator.of(context).pop();
}
},

View File

@@ -59,7 +59,7 @@ class Gallery extends StatelessWidget {
children: [
Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentImage.date),
.format(currentImage.date),
style: Theme.of(context).textTheme.headlineSmall,
),
Expanded(
@@ -73,12 +73,15 @@ class Gallery extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
Provider.of<GalleryProvider>(context, listen: false)
.deleteImage(currentImage);
Navigator.of(context).pop();
}),
icon: const Icon(Icons.delete),
onPressed: () {
Provider.of<GalleryProvider>(
context,
listen: false,
).deleteImage(currentImage);
Navigator.of(context).pop();
},
),
if (!isDesktop)
IconButton(
icon: const Icon(Icons.edit),
@@ -95,7 +98,7 @@ class Gallery extends StatelessWidget {
},
),
],
)
),
],
),
),

View File

@@ -31,7 +31,9 @@ class CategoriesCard extends StatelessWidget {
padding: const EdgeInsets.all(10),
height: 220,
child: MeasurementChartWidgetFl(
currentCategory.entries.map((e) => MeasurementChartEntry(e.value, e.date)).toList(),
currentCategory.entries
.map((e) => MeasurementChartEntry(e.value, e.date))
.toList(),
unit: currentCategory.unit,
),
),
@@ -40,14 +42,15 @@ class CategoriesCard extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
TextButton(
child: Text(AppLocalizations.of(context).goToDetailPage),
onPressed: () {
Navigator.pushNamed(
context,
MeasurementEntriesScreen.routeName,
arguments: currentCategory.id,
);
}),
child: Text(AppLocalizations.of(context).goToDetailPage),
onPressed: () {
Navigator.pushNamed(
context,
MeasurementEntriesScreen.routeName,
arguments: currentCategory.id,
);
},
),
IconButton(
onPressed: () async {
await Navigator.pushNamed(

View File

@@ -51,14 +51,16 @@ class _MeasurementChartWidgetFlState extends State<MeasurementChartWidgetFl> {
}
LineTouchData tooltipData() {
return LineTouchData(touchTooltipData: LineTouchTooltipData(getTooltipItems: (touchedSpots) {
return touchedSpots.map((touchedSpot) {
return LineTooltipItem(
'${touchedSpot.y} kg',
const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
);
}).toList();
}));
return LineTouchData(
touchTooltipData: LineTouchTooltipData(getTooltipItems: (touchedSpots) {
return touchedSpots.map((touchedSpot) {
return LineTooltipItem(
'${touchedSpot.y} kg',
const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
);
}).toList();
}),
);
}
LineChartData mainData() {
@@ -105,7 +107,10 @@ class _MeasurementChartWidgetFlState extends State<MeasurementChartWidgetFl> {
);
},
interval: widget._entries.isNotEmpty
? chartGetInterval(widget._entries.last.date, widget._entries.first.date)
? chartGetInterval(
widget._entries.last.date,
widget._entries.first.date,
)
: 1000,
),
),
@@ -128,8 +133,10 @@ class _MeasurementChartWidgetFlState extends State<MeasurementChartWidgetFl> {
lineBarsData: [
LineChartBarData(
spots: [
...widget._entries
.map((e) => FlSpot(e.date.millisecondsSinceEpoch.toDouble(), e.value.toDouble()))
...widget._entries.map((e) => FlSpot(
e.date.millisecondsSinceEpoch.toDouble(),
e.value.toDouble(),
)),
],
isCurved: false,
color: Theme.of(context).colorScheme.secondary,

View File

@@ -39,7 +39,9 @@ class EntriesList extends StatelessWidget {
padding: const EdgeInsets.all(10),
height: 220,
child: MeasurementChartWidgetFl(
_category.entries.map((e) => MeasurementChartEntry(e.value, e.date)).toList(),
_category.entries
.map((e) => MeasurementChartEntry(e.value, e.date))
.toList(),
unit: _category.unit,
),
),
@@ -56,39 +58,47 @@ class EntriesList extends StatelessWidget {
title: Text('${currentEntry.value} ${_category.unit}'),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentEntry.date),
.format(currentEntry.date),
),
trailing: PopupMenuButton(
itemBuilder: (BuildContext context) {
return [
PopupMenuItem(
child: Text(AppLocalizations.of(context).edit),
onTap: () => Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
MeasurementEntryForm(currentEntry.category, currentEntry),
),
)),
child: Text(AppLocalizations.of(context).edit),
onTap: () => Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
MeasurementEntryForm(
currentEntry.category,
currentEntry,
),
),
),
),
PopupMenuItem(
child: Text(AppLocalizations.of(context).delete),
onTap: () async {
// Delete entry from DB
await provider.deleteEntry(currentEntry.id!, currentEntry.category);
child: Text(AppLocalizations.of(context).delete),
onTap: () async {
// Delete entry from DB
await provider.deleteEntry(
currentEntry.id!,
currentEntry.category,
);
// and inform the user
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
// and inform the user
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
);
}
})
),
);
}
},
),
];
},
),

View File

@@ -31,7 +31,11 @@ class MeasurementCategoryForm extends StatelessWidget {
final nameController = TextEditingController();
final unitController = TextEditingController();
final Map<String, dynamic> categoryData = {'id': null, 'name': '', 'unit': ''};
final Map<String, dynamic> categoryData = {
'id': null,
'name': '',
'unit': '',
};
MeasurementCategoryForm([MeasurementCategory? category]) {
//this._category = category ?? MeasurementCategory();
@@ -99,15 +103,24 @@ class MeasurementCategoryForm extends StatelessWidget {
// Save the entry on the server
try {
categoryData['id'] == null
? await Provider.of<MeasurementProvider>(context, listen: false).addCategory(
? await Provider.of<MeasurementProvider>(
context,
listen: false,
).addCategory(
MeasurementCategory(
id: categoryData['id'],
name: categoryData['name'],
unit: categoryData['unit'],
),
)
: await Provider.of<MeasurementProvider>(context, listen: false).editCategory(
categoryData['id'], categoryData['name'], categoryData['unit']);
: await Provider.of<MeasurementProvider>(
context,
listen: false,
).editCategory(
categoryData['id'],
categoryData['name'],
categoryData['unit'],
);
} on WgerHttpException catch (error) {
if (context.mounted) {
showHttpExceptionErrorDialog(error, context);
@@ -261,15 +274,20 @@ class MeasurementEntryForm extends StatelessWidget {
// Save the entry on the server
try {
_entryData['id'] == null
? await Provider.of<MeasurementProvider>(context, listen: false)
.addEntry(MeasurementEntry(
? await Provider.of<MeasurementProvider>(
context,
listen: false,
).addEntry(MeasurementEntry(
id: _entryData['id'],
category: _entryData['category'],
date: _entryData['date'],
value: _entryData['value'],
notes: _entryData['notes'],
))
: await Provider.of<MeasurementProvider>(context, listen: false).editEntry(
: await Provider.of<MeasurementProvider>(
context,
listen: false,
).editEntry(
_entryData['id'],
_entryData['category'],
_entryData['value'],

View File

@@ -52,12 +52,20 @@ class FlNutritionalPlanGoalWidgetState extends State<FlNutritionalPlanGoalWidget
// why don't we just handle this inside this function? because it might be
// *another* gauge that's in surplus and we want to have consistent widths
// between all gauges
Widget _diyGauge(BuildContext context, double normWidth, double? plan, double val) {
Widget _diyGauge(
BuildContext context,
double normWidth,
double? plan,
double val,
) {
Container segment(double width, Color color) {
return Container(
height: 16,
width: width,
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(15.0)),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(15.0),
),
);
}
@@ -112,31 +120,56 @@ class FlNutritionalPlanGoalWidgetState extends State<FlNutritionalPlanGoalWidget
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(fmtMacro(AppLocalizations.of(context).energy, today.energy, goals.energy,
AppLocalizations.of(context).kcal)),
Text(fmtMacro(
AppLocalizations.of(context).energy,
today.energy,
goals.energy,
AppLocalizations.of(context).kcal,
)),
const SizedBox(height: 2),
_diyGauge(context, normWidth, goals.energy, today.energy),
const SizedBox(height: 8),
Text(fmtMacro(AppLocalizations.of(context).protein, today.protein, goals.protein,
AppLocalizations.of(context).g)),
Text(fmtMacro(
AppLocalizations.of(context).protein,
today.protein,
goals.protein,
AppLocalizations.of(context).g,
)),
const SizedBox(height: 2),
_diyGauge(context, normWidth, goals.protein, today.protein),
const SizedBox(height: 8),
Text(fmtMacro(AppLocalizations.of(context).carbohydrates, today.carbohydrates,
goals.carbohydrates, AppLocalizations.of(context).g)),
Text(fmtMacro(
AppLocalizations.of(context).carbohydrates,
today.carbohydrates,
goals.carbohydrates,
AppLocalizations.of(context).g,
)),
const SizedBox(height: 2),
_diyGauge(context, normWidth, goals.carbohydrates, today.carbohydrates),
_diyGauge(
context,
normWidth,
goals.carbohydrates,
today.carbohydrates,
),
const SizedBox(height: 8),
Text(fmtMacro(AppLocalizations.of(context).fat, today.fat, goals.fat,
AppLocalizations.of(context).g)),
Text(fmtMacro(
AppLocalizations.of(context).fat,
today.fat,
goals.fat,
AppLocalizations.of(context).g,
)),
const SizedBox(height: 2),
_diyGauge(context, normWidth, goals.fat, today.fat),
// optionally display the advanced macro goals:
if (goals.fiber != null)
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
const SizedBox(height: 8),
Text(fmtMacro(AppLocalizations.of(context).fiber, today.fiber, goals.fiber,
AppLocalizations.of(context).g)),
Text(fmtMacro(
AppLocalizations.of(context).fiber,
today.fiber,
goals.fiber,
AppLocalizations.of(context).g,
)),
const SizedBox(height: 2),
_diyGauge(context, normWidth, goals.fiber, today.fiber),
]),
@@ -206,17 +239,17 @@ class FlNutritionalPlanPieChartState extends State<FlNutritionalPlanPieChartWidg
children: [
(AppLocalizations.of(context).protein, LIST_OF_COLORS3[1]),
(AppLocalizations.of(context).carbohydrates, LIST_OF_COLORS3[0]),
(AppLocalizations.of(context).fat, LIST_OF_COLORS3[2])
(AppLocalizations.of(context).fat, LIST_OF_COLORS3[2]),
]
.map((e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Indicator(
color: e.$2,
text: e.$1,
isSquare: true,
),
))
.toList(),
.map((e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Indicator(
color: e.$2,
text: e.$1,
isSquare: true,
),
))
.toList(),
),
const SizedBox(
width: 28,
@@ -229,7 +262,7 @@ class FlNutritionalPlanPieChartState extends State<FlNutritionalPlanPieChartWidg
return [
(0, LIST_OF_COLORS3[1], widget.nutritionalValues.protein),
(1, LIST_OF_COLORS3[0], widget.nutritionalValues.carbohydrates),
(2, LIST_OF_COLORS3[2], widget.nutritionalValues.fat)
(2, LIST_OF_COLORS3[2], widget.nutritionalValues.fat),
].map((e) {
final isTouched = e.$1 == touchedIndex;
final radius = isTouched ? 92.0 : 80.0;
@@ -297,7 +330,12 @@ class NutritionalDiaryChartWidgetFlState extends State<NutritionalDiaryChartWidg
final [colorPlanned, colorLoggedToday, colorLogged7Day] = LIST_OF_COLORS3;
BarChartGroupData barchartGroup(int x, double barsSpace, double barsWidth, String prop) {
BarChartGroupData barchartGroup(
int x,
double barsSpace,
double barsWidth,
String prop,
) {
final plan = planned.prop(prop);
BarChartRodData barChartRodData(double? plan, double val, Color color) {
@@ -397,7 +435,12 @@ class NutritionalDiaryChartWidgetFlState extends State<NutritionalDiaryChartWidg
barGroups: [
barchartGroup(0, barsSpace, barsWidth, 'protein'),
barchartGroup(1, barsSpace, barsWidth, 'carbohydrates'),
barchartGroup(2, barsSpace, barsWidth, 'carbohydratesSugar'),
barchartGroup(
2,
barsSpace,
barsWidth,
'carbohydratesSugar',
),
barchartGroup(3, barsSpace, barsWidth, 'fat'),
barchartGroup(4, barsSpace, barsWidth, 'fatSaturated'),
if (widget._nutritionalPlan.nutritionalGoals.fiber != null)
@@ -414,17 +457,17 @@ class NutritionalDiaryChartWidgetFlState extends State<NutritionalDiaryChartWidg
(AppLocalizations.of(context).deficit, colorPlanned),
(AppLocalizations.of(context).surplus, COLOR_SURPLUS),
(AppLocalizations.of(context).today, colorLoggedToday),
(AppLocalizations.of(context).weekAverage, colorLogged7Day)
(AppLocalizations.of(context).weekAverage, colorLogged7Day),
]
.map(
(e) => Indicator(
color: e.$2,
text: e.$1,
isSquare: true,
marginRight: 0,
),
)
.toList(),
.map(
(e) => Indicator(
color: e.$2,
text: e.$1,
isSquare: true,
marginRight: 0,
),
)
.toList(),
),
),
],

View File

@@ -98,9 +98,14 @@ class MealForm extends StatelessWidget {
try {
_meal.id == null
? Provider.of<NutritionPlansProvider>(context, listen: false)
.addMeal(_meal, _planId)
: Provider.of<NutritionPlansProvider>(context, listen: false).editMeal(_meal);
? Provider.of<NutritionPlansProvider>(
context,
listen: false,
).addMeal(_meal, _planId)
: Provider.of<NutritionPlansProvider>(
context,
listen: false,
).editMeal(_meal);
} on WgerHttpException catch (error) {
showHttpExceptionErrorDialog(error, context);
} catch (error) {
@@ -116,35 +121,42 @@ class MealForm extends StatelessWidget {
}
}
Widget MealItemForm(Meal meal, List<MealItem> recent, [String? barcode, bool? test]) {
Widget MealItemForm(
Meal meal,
List<MealItem> recent, [
String? barcode,
bool? test,
]) {
return IngredientForm(
// TODO we use planId 0 here cause we don't have one and we don't need it I think?
recent: recent.map((e) => Log.fromMealItem(e, 0, e.mealId)).toList(),
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
mealItem.mealId = meal.id!;
Provider.of<NutritionPlansProvider>(context, listen: false).addMealItem(mealItem, meal);
},
barcode: barcode ?? '',
test: test ?? false,
withDate: false);
// TODO we use planId 0 here cause we don't have one and we don't need it I think?
recent: recent.map((e) => Log.fromMealItem(e, 0, e.mealId)).toList(),
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
mealItem.mealId = meal.id!;
Provider.of<NutritionPlansProvider>(context, listen: false).addMealItem(mealItem, meal);
},
barcode: barcode ?? '',
test: test ?? false,
withDate: false,
);
}
Widget IngredientLogForm(NutritionalPlan plan) {
return IngredientForm(
recent: plan.dedupDiaryEntries,
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
Provider.of<NutritionPlansProvider>(context, listen: false)
.logIngredientToDiary(mealItem, plan.id!, dt);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).ingredientLogged,
textAlign: TextAlign.center,
),
recent: plan.dedupDiaryEntries,
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
Provider.of<NutritionPlansProvider>(context, listen: false)
.logIngredientToDiary(mealItem, plan.id!, dt);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).ingredientLogged,
textAlign: TextAlign.center,
),
);
},
withDate: true);
),
);
},
withDate: true,
);
}
/// IngredientForm is a form that lets the user pick an ingredient (and amount) to
@@ -202,7 +214,7 @@ class IngredientFormState extends State<IngredientForm> {
});
}
// note: does not reset text search and amount inputs
// note: does not reset text search and amount inputs
void unSelectIngredient() {
setState(() {
_mealItem.ingredientId = 0;
@@ -220,8 +232,9 @@ class IngredientFormState extends State<IngredientForm> {
Widget build(BuildContext context) {
final String unit = AppLocalizations.of(context).g;
final queryLower = _searchQuery.toLowerCase();
final suggestions =
widget.recent.where((e) => e.ingredient.name.toLowerCase().contains(queryLower)).toList();
final suggestions = widget.recent
.where((e) => e.ingredient.name.toLowerCase().contains(queryLower))
.toList();
return Container(
margin: const EdgeInsets.all(20),
child: Form(
@@ -242,7 +255,9 @@ class IngredientFormState extends State<IngredientForm> {
Expanded(
child: TextFormField(
key: const Key('field-weight'), // needed ?
decoration: InputDecoration(labelText: AppLocalizations.of(context).weight),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).weight,
),
controller: _amountController,
keyboardType: TextInputType.number,
onFieldSubmitted: (_) {},
@@ -336,9 +351,14 @@ class IngredientFormState extends State<IngredientForm> {
style: Theme.of(context).textTheme.titleMedium,
),
FutureBuilder<Ingredient>(
future: Provider.of<NutritionPlansProvider>(context, listen: false)
.fetchIngredient(_mealItem.ingredientId),
builder: (BuildContext context, AsyncSnapshot<Ingredient> snapshot) {
future: Provider.of<NutritionPlansProvider>(
context,
listen: false,
).fetchIngredient(_mealItem.ingredientId),
builder: (
BuildContext context,
AsyncSnapshot<Ingredient> snapshot,
) {
if (snapshot.hasData) {
_mealItem.ingredient = snapshot.data!;
return MealItemTile(
@@ -378,7 +398,13 @@ class IngredientFormState extends State<IngredientForm> {
try {
var date = DateTime.parse(_dateController.text);
final tod = stringToTime(_timeController.text);
date = DateTime(date.year, date.month, date.day, tod.hour, tod.minute);
date = DateTime(
date.year,
date.month,
date.day,
tod.hour,
tod.minute,
);
widget.onSave(context, _mealItem, date);
} on WgerHttpException catch (error) {
showHttpExceptionErrorDialog(error, context);
@@ -402,18 +428,25 @@ class IngredientFormState extends State<IngredientForm> {
child: ListTile(
onTap: () {
final ingredient = suggestions[index].ingredient;
selectIngredient(ingredient.id, ingredient.name, suggestions[index].amount);
selectIngredient(
ingredient.id,
ingredient.name,
suggestions[index].amount,
);
},
title: Text(
'${suggestions[index].ingredient.name} (${suggestions[index].amount.toStringAsFixed(0)}$unit)'),
'${suggestions[index].ingredient.name} (${suggestions[index].amount.toStringAsFixed(0)}$unit)',
),
subtitle: Text(getShortNutritionValues(
suggestions[index].ingredient.nutritionalValues, context)),
suggestions[index].ingredient.nutritionalValues,
context,
)),
trailing: const Icon(Icons.copy),
),
);
},
),
)
),
],
),
),
@@ -476,7 +509,9 @@ class _PlanFormState extends State<PlanForm> {
// Description
TextFormField(
key: const Key('field-description'),
decoration: InputDecoration(labelText: AppLocalizations.of(context).description),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).description,
),
controller: _descriptionController,
onFieldSubmitted: (_) {},
onSaved: (newValue) {
@@ -505,10 +540,13 @@ class _PlanFormState extends State<PlanForm> {
child: DropdownButtonFormField<GoalType>(
value: _goalType,
items: GoalType.values
.map(
(e) => DropdownMenuItem<GoalType>(value: e, child: Text(e.label)),
)
.toList(),
.map(
(e) => DropdownMenuItem<GoalType>(
value: e,
child: Text(e.label),
),
)
.toList(),
onChanged: (GoalType? g) {
setState(() {
if (g == null) {
@@ -590,14 +628,18 @@ class _PlanFormState extends State<PlanForm> {
// Save to DB
try {
if (widget._plan.id != null) {
await Provider.of<NutritionPlansProvider>(context, listen: false)
.editPlan(widget._plan);
await Provider.of<NutritionPlansProvider>(
context,
listen: false,
).editPlan(widget._plan);
if (context.mounted) {
Navigator.of(context).pop();
}
} else {
widget._plan = await Provider.of<NutritionPlansProvider>(context, listen: false)
.addPlan(widget._plan);
widget._plan = await Provider.of<NutritionPlansProvider>(
context,
listen: false,
).addPlan(widget._plan);
if (context.mounted) {
Navigator.of(context).pushReplacementNamed(
NutritionalPlanScreen.routeName,

View File

@@ -102,59 +102,63 @@ class _MealWidgetState extends State<MealWidget> {
),
if (_editing)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Wrap(
spacing: 8,
children: [
TextButton.icon(
icon: const Icon(Icons.add),
label: Text(AppLocalizations.of(context).addIngredient),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).addIngredient,
MealItemForm(widget._meal, widget._recentMealItems),
hasListView: true,
),
);
},
),
TextButton.icon(
label: Text(AppLocalizations.of(context).edit),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
MealForm(widget._meal.planId, widget._meal),
),
);
},
icon: const Icon(Icons.timer),
),
TextButton.icon(
onPressed: () {
// Delete the meal
Provider.of<NutritionPlansProvider>(context, listen: false)
.deleteMeal(widget._meal);
padding: const EdgeInsets.symmetric(vertical: 8),
child: Wrap(
spacing: 8,
children: [
TextButton.icon(
icon: const Icon(Icons.add),
label: Text(AppLocalizations.of(context).addIngredient),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).addIngredient,
MealItemForm(widget._meal, widget._recentMealItems),
hasListView: true,
),
);
},
),
TextButton.icon(
label: Text(AppLocalizations.of(context).edit),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
MealForm(widget._meal.planId, widget._meal),
),
);
},
icon: const Icon(Icons.timer),
),
TextButton.icon(
onPressed: () {
// Delete the meal
Provider.of<NutritionPlansProvider>(
context,
listen: false,
).deleteMeal(widget._meal);
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
label: Text(AppLocalizations.of(context).delete),
icon: const Icon(Icons.delete)),
],
)),
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
label: Text(AppLocalizations.of(context).delete),
icon: const Icon(Icons.delete),
),
],
),
),
if (_viewMode == viewMode.withIngredients || _viewMode == viewMode.withAllDetails)
const Divider(),
if (_viewMode == viewMode.withAllDetails) const NutritionDiaryheader(),
@@ -223,7 +227,7 @@ class MealItemWidget extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
if (_viewMode == viewMode.withAllDetails) ...getMutedNutritionalValues(values, context)
if (_viewMode == viewMode.withAllDetails) ...getMutedNutritionalValues(values, context),
],
),
trailing: _editing
@@ -238,10 +242,11 @@ class MealItemWidget extends StatelessWidget {
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
)),
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
)
@@ -314,20 +319,24 @@ class MealHeader extends StatelessWidget {
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
title: Row(children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
((_meal.time != null) ? '${_meal.time!.format(context)} ' : '') + _meal.name,
style: Theme.of(context).textTheme.titleMedium,
),
if (_meal.isRealMeal)
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
getShortNutritionValues(_meal.plannedNutritionalValues, context),
style: Theme.of(context).textTheme.titleSmall,
((_meal.time != null) ? '${_meal.time!.format(context)} ' : '') + _meal.name,
style: Theme.of(context).textTheme.titleMedium,
),
],
)),
if (_meal.isRealMeal)
Text(
getShortNutritionValues(
_meal.plannedNutritionalValues,
context,
),
style: Theme.of(context).textTheme.titleSmall,
),
],
),
),
]),
trailing: Row(
mainAxisSize: MainAxisSize.min,

View File

@@ -65,7 +65,10 @@ class NutritionalDiaryDetailWidget extends StatelessWidget {
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
NutritionDiaryEntry(diaryEntry: e, nutritionalPlan: _nutritionalPlan),
NutritionDiaryEntry(
diaryEntry: e,
nutritionalPlan: _nutritionalPlan,
),
],
),
)),
@@ -153,11 +156,15 @@ class NutritionDiaryTable extends StatelessWidget {
TableRow(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: tablePadding, horizontal: 12),
padding: const EdgeInsets.symmetric(
vertical: tablePadding,
horizontal: 12,
),
child: Text(AppLocalizations.of(context).sugars),
),
Text(
AppLocalizations.of(context).gValue(planned.carbohydratesSugar.toStringAsFixed(0))),
AppLocalizations.of(context).gValue(planned.carbohydratesSugar.toStringAsFixed(0)),
),
Text(AppLocalizations.of(context).gValue(logged.carbohydratesSugar.toStringAsFixed(0))),
Text((logged.carbohydratesSugar - planned.carbohydratesSugar).toStringAsFixed(0)),
],
@@ -176,7 +183,10 @@ class NutritionDiaryTable extends StatelessWidget {
TableRow(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: tablePadding, horizontal: 12),
padding: const EdgeInsets.symmetric(
vertical: tablePadding,
horizontal: 12,
),
child: Text(AppLocalizations.of(context).saturatedFat),
),
Text(AppLocalizations.of(context).gValue(planned.fatSaturated.toStringAsFixed(0))),

View File

@@ -43,67 +43,85 @@ class NutritionalDiaryTable extends StatelessWidget {
children: [
Text(style: Theme.of(context).textTheme.titleMedium, 'Date'),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).energyShort} (${AppLocalizations.of(context).kcal})'),
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).energyShort} (${AppLocalizations.of(context).kcal})',
),
if (goals.energy != null)
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
AppLocalizations.of(context).difference),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).proteinShort} (${AppLocalizations.of(context).g})'),
AppLocalizations.of(context).difference,
),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).carbohydratesShort} (${AppLocalizations.of(context).g})'),
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).proteinShort} (${AppLocalizations.of(context).g})',
),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).fatShort} (${AppLocalizations.of(context).g})'),
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).carbohydratesShort} (${AppLocalizations.of(context).g})',
),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
'${AppLocalizations.of(context).fatShort} (${AppLocalizations.of(context).g})',
),
],
);
}
TableRow nutritionDiaryEntry(final BuildContext context, NutritionalGoals goals, DateTime date,
final NutritionalValues values) {
TableRow nutritionDiaryEntry(
final BuildContext context,
NutritionalGoals goals,
DateTime date,
final NutritionalValues values,
) {
return TableRow(
decoration: BoxDecoration(border: Border(top: BorderSide(color: Colors.grey[300]!))),
children: [
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey[300]!)),
),
children: [
Text(
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: LIST_OF_COLORS3.first),
DateFormat.Md(Localizations.localeOf(context).languageCode).format(date),
),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.energy.toStringAsFixed(0),
),
if (goals.energy != null)
Text(
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: LIST_OF_COLORS3.first),
DateFormat.Md(Localizations.localeOf(context).languageCode).format(date),
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
(values.energy - goals.energy!).toStringAsFixed(0),
),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.energy.toStringAsFixed(0)),
if (goals.energy != null)
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
(values.energy - goals.energy!).toStringAsFixed(0)),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.protein.toStringAsFixed(0)),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.carbohydrates.toStringAsFixed(0)),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.fat.toStringAsFixed(0)),
].map((element) {
return GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
NutritionalDiaryScreen.routeName,
arguments: NutritionalDiaryArguments(plan, date),
),
child: element);
}).toList());
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.protein.toStringAsFixed(0),
),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.carbohydrates.toStringAsFixed(0),
),
Text(
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.end,
values.fat.toStringAsFixed(0),
),
].map((element) {
return GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
NutritionalDiaryScreen.routeName,
arguments: NutritionalDiaryArguments(plan, date),
),
child: element,
);
}).toList(),
);
}
}

View File

@@ -40,74 +40,80 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
lastWeightEntry != null ? nutritionalGoals / lastWeightEntry.weight.toDouble() : null;
return SliverList(
delegate: SliverChildListDelegate(
[
SizedBox(
width: 300,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FlNutritionalPlanGoalWidget(
delegate: SliverChildListDelegate(
[
SizedBox(
width: 300,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FlNutritionalPlanGoalWidget(
nutritionalPlan: _nutritionalPlan,
),
),
),
const SizedBox(height: 10),
..._nutritionalPlan.meals.map((meal) => MealWidget(
meal,
_nutritionalPlan.dedupMealItems,
false,
false,
)),
MealWidget(
_nutritionalPlan.pseudoMealOthers('Other logs'),
_nutritionalPlan.dedupMealItems,
false,
true,
),
if (nutritionalGoals.isComplete())
Container(
padding: const EdgeInsets.all(15),
height: 220,
child: FlNutritionalPlanPieChartWidget(nutritionalGoals.toValues()),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: MacronutrientsTable(
nutritionalGoals: nutritionalGoals,
plannedValuesPercentage: nutritionalGoals.energyPercentage(),
nutritionalGoalsGperKg: nutritionalGoalsGperKg,
),
),
const Padding(padding: EdgeInsets.all(8.0)),
Text(
AppLocalizations.of(context).logged,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
Container(
padding: const EdgeInsets.only(top: 16, left: 8, right: 8),
height: 300,
child: NutritionalDiaryChartWidgetFl(
nutritionalPlan: _nutritionalPlan,
),
),
),
const SizedBox(height: 10),
..._nutritionalPlan.meals.map((meal) => MealWidget(
meal,
_nutritionalPlan.dedupMealItems,
false,
false,
)),
MealWidget(
_nutritionalPlan.pseudoMealOthers('Other logs'),
_nutritionalPlan.dedupMealItems,
false,
true,
),
if (nutritionalGoals.isComplete())
Container(
padding: const EdgeInsets.all(15),
height: 220,
child: FlNutritionalPlanPieChartWidget(nutritionalGoals.toValues()),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: MacronutrientsTable(
nutritionalGoals: nutritionalGoals,
plannedValuesPercentage: nutritionalGoals.energyPercentage(),
nutritionalGoalsGperKg: nutritionalGoalsGperKg,
),
),
const Padding(padding: EdgeInsets.all(8.0)),
Text(
AppLocalizations.of(context).logged,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
Container(
padding: const EdgeInsets.only(top: 16, left: 8, right: 8),
height: 300,
child: NutritionalDiaryChartWidgetFl(nutritionalPlan: _nutritionalPlan),
),
if (_nutritionalPlan.logEntriesValues.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 15, left: 15, right: 15),
child: Column(
children: [
Text(
AppLocalizations.of(context).nutritionalDiary,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
SizedBox(
if (_nutritionalPlan.logEntriesValues.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 15, left: 15, right: 15),
child: Column(
children: [
Text(
AppLocalizations.of(context).nutritionalDiary,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge,
),
SizedBox(
height: 200,
child: SingleChildScrollView(
child: NutritionalDiaryTable(nutritionalPlan: _nutritionalPlan),
)),
],
child: NutritionalDiaryTable(
nutritionalPlan: _nutritionalPlan,
),
),
),
],
),
),
),
],
));
],
),
);
}
}

View File

@@ -49,8 +49,9 @@ class NutritionalPlansList extends StatelessWidget {
},
title: Text(currentPlan.getLabel(context)),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentPlan.creationDate),
DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentPlan.creationDate),
),
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
const VerticalDivider(),
@@ -60,48 +61,51 @@ class NutritionalPlansList extends StatelessWidget {
onPressed: () async {
// Delete the plan from DB
await showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context)
.confirmDelete(currentPlan.description),
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context)
.confirmDelete(currentPlan.description),
),
actions: [
TextButton(
child: Text(
MaterialLocalizations.of(context).cancelButtonLabel,
),
onPressed: () => Navigator.of(contextDialog).pop(),
),
actions: [
TextButton(
child:
Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style:
TextStyle(color: Theme.of(context).colorScheme.error),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
onPressed: () {
// Confirmed, delete the plan
_nutritionProvider.deletePlan(currentPlan.id!);
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
),
],
);
});
onPressed: () {
// Confirmed, delete the plan
_nutritionProvider.deletePlan(currentPlan.id!);
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
),
],
);
},
);
},
)
),
]),
),
);

View File

@@ -154,8 +154,12 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
final url = context.read<NutritionPlansProvider>().baseProvider.auth.serverUrl;
return ListTile(
leading: suggestion.data.image != null
? CircleAvatar(backgroundImage: NetworkImage(url! + suggestion.data.image!))
: const CircleIconAvatar(Icon(Icons.image, color: Colors.grey)),
? CircleAvatar(
backgroundImage: NetworkImage(url! + suggestion.data.image!),
)
: const CircleIconAvatar(
Icon(Icons.image, color: Colors.grey),
),
title: Text(suggestion.value),
// subtitle: Text(suggestion.data.id.toString()),
);
@@ -197,8 +201,10 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
if (!mounted) {
return;
}
final result = await Provider.of<NutritionPlansProvider>(context, listen: false)
.searchIngredientWithCode(barcode);
final result = await Provider.of<NutritionPlansProvider>(
context,
listen: false,
).searchIngredientWithCode(barcode);
// TODO: show spinner...
if (!mounted) {
return;
@@ -231,11 +237,13 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
),
TextButton(
key: const Key('found-dialog-close-button'),
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
child: Text(
MaterialLocalizations.of(context).closeButtonLabel,
),
onPressed: () {
Navigator.of(ctx).pop();
},
)
),
],
),
);
@@ -252,11 +260,13 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
actions: [
TextButton(
key: const Key('notFound-dialog-close-button'),
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
child: Text(
MaterialLocalizations.of(context).closeButtonLabel,
),
onPressed: () {
Navigator.of(ctx).pop();
},
)
),
],
),
);
@@ -292,13 +302,13 @@ class NutritionDiaryheader extends StatelessWidget {
AppLocalizations.of(context).energy,
AppLocalizations.of(context).protein,
AppLocalizations.of(context).carbohydrates,
AppLocalizations.of(context).fat
AppLocalizations.of(context).fat,
]
.map((e) => MutedText(
e,
textAlign: TextAlign.right,
))
.toList(),
.map((e) => MutedText(
e,
textAlign: TextAlign.right,
))
.toList(),
),
);
}
@@ -338,7 +348,10 @@ class NutritionDiaryEntry extends StatelessWidget {
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
...getMutedNutritionalValues(diaryEntry.nutritionalValues, context),
...getMutedNutritionalValues(
diaryEntry.nutritionalValues,
context,
),
],
),
const SizedBox(height: 12),
@@ -347,12 +360,13 @@ class NutritionDiaryEntry extends StatelessWidget {
),
if (nutritionalPlan != null)
IconButton(
tooltip: AppLocalizations.of(context).delete,
onPressed: () {
Provider.of<NutritionPlansProvider>(context, listen: false)
.deleteLog(diaryEntry.id!, nutritionalPlan!.id!);
},
icon: const Icon(Icons.delete_outline)),
tooltip: AppLocalizations.of(context).delete,
onPressed: () {
Provider.of<NutritionPlansProvider>(context, listen: false)
.deleteLog(diaryEntry.id!, nutritionalPlan!.id!);
},
icon: const Icon(Icons.delete_outline),
),
],
);
}
@@ -367,7 +381,9 @@ class IngredientAvatar extends StatelessWidget {
Widget build(BuildContext context) {
return ingredient.image != null
? GestureDetector(
child: CircleAvatar(backgroundImage: NetworkImage(ingredient.image!.image)),
child: CircleAvatar(
backgroundImage: NetworkImage(ingredient.image!.image),
),
onTap: () async {
if (ingredient.image!.objectUrl != '') {
return launchURL(ingredient.image!.objectUrl, context);

View File

@@ -73,15 +73,16 @@ class _UserProfileFormState extends State<UserProfileForm> {
leading: const Icon(Icons.email_rounded, color: wgerPrimaryColor),
title: TextFormField(
decoration: InputDecoration(
labelText: widget._profile.emailVerified
? AppLocalizations.of(context).verifiedEmail
: AppLocalizations.of(context).unVerifiedEmail,
suffixIcon: widget._profile.emailVerified
? const Icon(
Icons.check_circle,
color: Colors.green,
)
: null),
labelText: widget._profile.emailVerified
? AppLocalizations.of(context).verifiedEmail
: AppLocalizations.of(context).unVerifiedEmail,
suffixIcon: widget._profile.emailVerified
? const Icon(
Icons.check_circle,
color: Colors.green,
)
: null,
),
controller: emailController,
keyboardType: TextInputType.emailAddress,
onSaved: (newValue) {
@@ -104,7 +105,9 @@ class _UserProfileFormState extends State<UserProfileForm> {
}
// Verify
await context.read<UserProvider>().verifyEmail();
await context
.read<UserProvider>()
.verifyEmail();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
@@ -125,10 +128,14 @@ class _UserProfileFormState extends State<UserProfileForm> {
_form.currentState!.save();
// Update profile
context.read<UserProvider>().saveProfile();
context
.read<UserProvider>()
.saveProfile();
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context).successfullySaved)),
SnackBar(
content: Text(AppLocalizations.of(context).successfullySaved),
),
);
},
child: Text(AppLocalizations.of(context).save),

View File

@@ -39,7 +39,9 @@ class WeightEntriesList extends StatelessWidget {
padding: const EdgeInsets.all(15),
height: 220,
child: MeasurementChartWidgetFl(
weightProvider.items.map((e) => MeasurementChartEntry(e.weight, e.date)).toList(),
weightProvider.items
.map((e) => MeasurementChartEntry(e.weight, e.date))
.toList(),
unit: profile!.isMetric
? AppLocalizations.of(context).kg
: AppLocalizations.of(context).lb,
@@ -54,7 +56,7 @@ class WeightEntriesList extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(AppLocalizations.of(context).measurements),
const Icon(Icons.chevron_right)
const Icon(Icons.chevron_right),
],
),
),
@@ -70,22 +72,24 @@ class WeightEntriesList extends StatelessWidget {
child: ListTile(
title: Text('${currentEntry.weight} kg'),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentEntry.date),
DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentEntry.date),
),
trailing: PopupMenuButton(
itemBuilder: (BuildContext context) {
return [
PopupMenuItem(
child: Text(AppLocalizations.of(context).edit),
onTap: () => Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
WeightForm(currentEntry),
),
)),
child: Text(AppLocalizations.of(context).edit),
onTap: () => Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
WeightForm(currentEntry),
),
),
),
PopupMenuItem(
child: Text(AppLocalizations.of(context).delete),
onTap: () async {
@@ -104,7 +108,7 @@ class WeightEntriesList extends StatelessWidget {
);
}
},
)
),
];
},
),

View File

@@ -26,33 +26,37 @@ enum _WorkoutAppBarOptions {
contribute,
}
class WorkoutOverviewAppBar extends StatelessWidget implements PreferredSizeWidget {
class WorkoutOverviewAppBar extends StatelessWidget
implements PreferredSizeWidget {
@override
Widget build(BuildContext context) {
return AppBar(
title: Text(AppLocalizations.of(context).labelWorkoutPlans),
actions: [
PopupMenuButton(itemBuilder: (context) {
return [
PopupMenuItem<_WorkoutAppBarOptions>(
value: _WorkoutAppBarOptions.list,
child: Text(AppLocalizations.of(context).exerciseList),
),
PopupMenuItem<_WorkoutAppBarOptions>(
value: _WorkoutAppBarOptions.contribute,
child: Text(AppLocalizations.of(context).contributeExercise),
),
];
}, onSelected: (value) {
switch (value) {
case _WorkoutAppBarOptions.contribute:
Navigator.of(context).pushNamed(AddExerciseScreen.routeName);
break;
case _WorkoutAppBarOptions.list:
Navigator.of(context).pushNamed(ExercisesScreen.routeName);
break;
}
}),
PopupMenuButton(
itemBuilder: (context) {
return [
PopupMenuItem<_WorkoutAppBarOptions>(
value: _WorkoutAppBarOptions.list,
child: Text(AppLocalizations.of(context).exerciseList),
),
PopupMenuItem<_WorkoutAppBarOptions>(
value: _WorkoutAppBarOptions.contribute,
child: Text(AppLocalizations.of(context).contributeExercise),
),
];
},
onSelected: (value) {
switch (value) {
case _WorkoutAppBarOptions.contribute:
Navigator.of(context).pushNamed(AddExerciseScreen.routeName);
break;
case _WorkoutAppBarOptions.list:
Navigator.of(context).pushNamed(ExercisesScreen.routeName);
break;
}
},
),
],
);
}

View File

@@ -55,7 +55,8 @@ class _LogChartWidgetFlState extends State<LogChartWidgetFl> {
touchTooltipData: LineTouchTooltipData(
getTooltipItems: (touchedSpots) {
return touchedSpots.map((touchedSpot) {
final reps = widget._data['chart_data'][touchedSpot.barIndex].first['reps'];
final reps =
widget._data['chart_data'][touchedSpot.barIndex].first['reps'];
return LineTooltipItem(
'$reps × ${touchedSpot.y} kg',
@@ -68,7 +69,8 @@ class _LogChartWidgetFlState extends State<LogChartWidgetFl> {
}
LineChartData mainData() {
final colors = generateChartColors(widget._data['chart_data'].length).iterator;
final colors =
generateChartColors(widget._data['chart_data'].length).iterator;
return LineChartData(
lineTouchData: tooltipData(),
@@ -106,9 +108,11 @@ class _LogChartWidgetFlState extends State<LogChartWidgetFl> {
return const Text('');
}
final DateTime date = DateTime.fromMillisecondsSinceEpoch(value.toInt());
final DateTime date =
DateTime.fromMillisecondsSinceEpoch(value.toInt());
return Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode).format(date),
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(date),
);
},
interval: chartGetInterval(
@@ -138,10 +142,12 @@ class _LogChartWidgetFlState extends State<LogChartWidgetFl> {
spots: [
...e.map(
(entry) => FlSpot(
DateTime.parse(entry['date']).millisecondsSinceEpoch.toDouble(),
DateTime.parse(entry['date'])
.millisecondsSinceEpoch
.toDouble(),
double.parse(entry['weight']),
),
)
),
],
isCurved: true,
color: colors.current,
@@ -156,7 +162,7 @@ class _LogChartWidgetFlState extends State<LogChartWidgetFl> {
),
),
);
})
}),
],
);
}

View File

@@ -63,7 +63,9 @@ class SettingWidget extends StatelessWidget {
content: ExerciseDetail(setting.exerciseObj),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
child: Text(
MaterialLocalizations.of(context).closeButtonLabel,
),
onPressed: () {
Navigator.of(context).pop();
},
@@ -80,7 +82,9 @@ class SettingWidget extends StatelessWidget {
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...set.getSmartRepr(setting.exerciseObj).map((e) => Text(e)),
...set
.getSmartRepr(setting.exerciseObj)
.map((e) => Text(e)),
],
),
);
@@ -172,55 +176,62 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
),
if (_editing)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Wrap(
spacing: 8,
children: [
TextButton.icon(
label: Text(AppLocalizations.of(context).addSet),
icon: const Icon(Icons.add),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).newSet,
SetFormWidget(widget._day),
hasListView: true,
padding: EdgeInsets.zero,
padding: const EdgeInsets.symmetric(vertical: 8),
child: Wrap(
spacing: 8,
children: [
TextButton.icon(
label: Text(AppLocalizations.of(context).addSet),
icon: const Icon(Icons.add),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).newSet,
SetFormWidget(widget._day),
hasListView: true,
padding: EdgeInsets.zero,
),
);
},
),
TextButton.icon(
icon: const Icon(Icons.calendar_month),
label: Text(AppLocalizations.of(context).edit),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
DayFormWidget(
Provider.of<WorkoutPlansProvider>(
context,
listen: false,
).findById(widget._day.workoutId),
widget._day,
),
);
},
),
TextButton.icon(
icon: const Icon(Icons.calendar_month),
label: Text(AppLocalizations.of(context).edit),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
DayFormWidget(
Provider.of<WorkoutPlansProvider>(context, listen: false)
.findById(widget._day.workoutId),
widget._day),
hasListView: true,
),
);
},
),
TextButton.icon(
icon: const Icon(Icons.delete),
label: Text(AppLocalizations.of(context).delete),
onPressed: () {
Provider.of<WorkoutPlansProvider>(context, listen: false).deleteDay(
widget._day,
);
},
),
],
)),
hasListView: true,
),
);
},
),
TextButton.icon(
icon: const Icon(Icons.delete),
label: Text(AppLocalizations.of(context).delete),
onPressed: () {
Provider.of<WorkoutPlansProvider>(
context,
listen: false,
).deleteDay(
widget._day,
);
},
),
],
),
),
const Divider(),
ReorderableListView(
physics: const NeverScrollableScrollPhysics(),
@@ -237,8 +248,10 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
setState(() {
_sets.insert(newIndex, _sets.removeAt(oldIndex));
});
_sets = await Provider.of<WorkoutPlansProvider>(context, listen: false)
.reorderSets(_sets, startIndex);
_sets = await Provider.of<WorkoutPlansProvider>(
context,
listen: false,
).reorderSets(_sets, startIndex);
},
children: [
for (var i = 0; i < widget._day.sets.length; i++) getSetRow(widget._day.sets[i], i),
@@ -285,7 +298,7 @@ class DayHeader extends StatelessWidget {
onPressed: () {
_toggle();
},
)
),
]),
onTap: () {
Navigator.of(context).pushNamed(

View File

@@ -73,7 +73,9 @@ class WorkoutForm extends StatelessWidget {
),
TextFormField(
key: const Key('field-description'),
decoration: InputDecoration(labelText: AppLocalizations.of(context).description),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).description,
),
minLines: 3,
maxLines: 10,
controller: workoutDescriptionController,
@@ -108,9 +110,10 @@ class WorkoutForm extends StatelessWidget {
Navigator.of(context).pop();
}
} else {
final WorkoutPlan newPlan =
await Provider.of<WorkoutPlansProvider>(context, listen: false)
.addWorkout(_plan);
final WorkoutPlan newPlan = await Provider.of<WorkoutPlansProvider>(
context,
listen: false,
).addWorkout(_plan);
if (context.mounted) {
Navigator.of(context).pushReplacementNamed(
WorkoutPlanScreen.routeName,
@@ -246,7 +249,7 @@ class _DayFormWidgetState extends State<DayFormWidget> {
onPressed: () {
Navigator.of(ctx).pop();
},
)
),
],
),
);
@@ -333,7 +336,9 @@ class _SetFormWidgetState extends State<SetFormWidget> {
min: 1,
max: 10,
divisions: 10,
label: _currentSetSliderValue.round().toString(),
label: _currentSetSliderValue
.round()
.toString(),
onChanged: (double value) {
setState(() {
widget._set.sets = value.round();
@@ -393,13 +398,14 @@ class _SetFormWidgetState extends State<SetFormWidget> {
children: [
Text(AppLocalizations.of(context).selectExercises),
const SizedBox(height: 10),
Text(AppLocalizations.of(context).sameRepetitions)
Text(AppLocalizations.of(context).sameRepetitions),
],
),
actions: [
TextButton(
child: Text(
MaterialLocalizations.of(context).closeButtonLabel),
MaterialLocalizations.of(context).closeButtonLabel,
),
onPressed: () {
Navigator.of(context).pop();
},
@@ -433,18 +439,25 @@ class _SetFormWidgetState extends State<SetFormWidget> {
if (pattern == '') {
return null;
}
return context.read<ExercisesProvider>().searchExercise(
pattern,
languageCode: Localizations.localeOf(context).languageCode,
searchEnglish: _searchEnglish,
);
return context
.read<ExercisesProvider>()
.searchExercise(
pattern,
languageCode: Localizations.localeOf(context).languageCode,
searchEnglish: _searchEnglish,
);
},
itemBuilder: (BuildContext context, Exercise exerciseSuggestion) =>
itemBuilder: (
BuildContext context,
Exercise exerciseSuggestion,
) =>
ListTile(
key: Key('exercise-${exerciseSuggestion.id}'),
leading: SizedBox(
width: 45,
child: ExerciseImageWidget(image: exerciseSuggestion.getMainImage),
child: ExerciseImageWidget(
image: exerciseSuggestion.getMainImage,
),
),
title: Text(
exerciseSuggestion
@@ -474,7 +487,10 @@ class _SetFormWidgetState extends State<SetFormWidget> {
);
},
transitionBuilder: (context, animation, child) => FadeTransition(
opacity: CurvedAnimation(parent: animation, curve: Curves.fastOutSlowIn),
opacity: CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
child: child,
),
onSelected: (Exercise exerciseSuggestion) {
@@ -494,7 +510,7 @@ class _SetFormWidgetState extends State<SetFormWidget> {
});
},
dense: true,
)
),
],
),
),
@@ -558,8 +574,10 @@ class _SetFormWidgetState extends State<SetFormWidget> {
}
_formKey.currentState!.save();
final workoutProvider =
Provider.of<WorkoutPlansProvider>(context, listen: false);
final workoutProvider = Provider.of<WorkoutPlansProvider>(
context,
listen: false,
);
// Save set
final Set setDb = await workoutProvider.addSet(widget._set);
@@ -704,10 +722,11 @@ class ExerciseSetting extends StatelessWidget {
contentPadding: EdgeInsets.zero,
leading: ExerciseImageWidget(image: _exerciseBase.getMainImage),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
removeExercise(_exerciseBase);
}),
icon: const Icon(Icons.delete),
onPressed: () {
removeExercise(_exerciseBase);
},
),
),
const Divider(),
@@ -906,8 +925,8 @@ class _WeightUnitInputWidgetState extends State<WeightUnitInputWidget> {
});
},
items: Provider.of<WorkoutPlansProvider>(context, listen: false)
.weightUnits
.map<DropdownMenuItem<WeightUnit>>((WeightUnit value) {
.weightUnits
.map<DropdownMenuItem<WeightUnit>>((WeightUnit value) {
return DropdownMenuItem<WeightUnit>(
key: Key(value.id.toString()),
value: value,
@@ -937,7 +956,9 @@ class _RepetitionUnitInputWidgetState extends State<RepetitionUnitInputWidget> {
return DropdownButtonFormField(
value: selectedWeightUnit,
decoration: InputDecoration(labelText: AppLocalizations.of(context).repetitionUnit),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).repetitionUnit,
),
isDense: true,
onChanged: (RepetitionUnit? newValue) {
setState(() {
@@ -946,8 +967,8 @@ class _RepetitionUnitInputWidgetState extends State<RepetitionUnitInputWidget> {
});
},
items: Provider.of<WorkoutPlansProvider>(context, listen: false)
.repetitionUnits
.map<DropdownMenuItem<RepetitionUnit>>((RepetitionUnit value) {
.repetitionUnits
.map<DropdownMenuItem<RepetitionUnit>>((RepetitionUnit value) {
return DropdownMenuItem<RepetitionUnit>(
key: Key(value.id.toString()),
value: value,

View File

@@ -156,7 +156,7 @@ class _GymModeState extends State<GymMode> {
...getContent(),
SessionPage(
Provider.of<WorkoutPlansProvider>(context, listen: false)
.findById(widget._workoutDay.workoutId),
.findById(widget._workoutDay.workoutId),
_controller,
widget._start,
_exercisePages,
@@ -199,7 +199,9 @@ class StartPage extends StatelessWidget {
.name,
style: Theme.of(context).textTheme.titleLarge,
),
...set.getSmartRepr(s.exerciseObj).map((e) => Text(e)),
...set
.getSmartRepr(s.exerciseObj)
.map((e) => Text(e)),
const SizedBox(height: 15),
],
);
@@ -215,7 +217,9 @@ class StartPage extends StatelessWidget {
child: Text(AppLocalizations.of(context).start),
onPressed: () {
_controller.nextPage(
duration: const Duration(milliseconds: 200), curve: Curves.bounceIn);
duration: const Duration(milliseconds: 200),
curve: Curves.bounceIn,
);
},
),
NavigationFooter(
@@ -452,7 +456,7 @@ class _LogPageState extends State<LogPage> {
children: [
Flexible(child: getWeightWidget()),
const SizedBox(width: 8),
Flexible(child: WeightUnitInputWidget(widget._log))
Flexible(child: WeightUnitInputWidget(widget._log)),
],
),
if (_detailed) RiRInputWidget(widget._log),
@@ -479,8 +483,10 @@ class _LogPageState extends State<LogPage> {
// Save the entry on the server
try {
await Provider.of<WorkoutPlansProvider>(context, listen: false)
.addLog(widget._log);
await Provider.of<WorkoutPlansProvider>(
context,
listen: false,
).addLog(widget._log);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 2), // default is 4
@@ -535,8 +541,9 @@ class _LogPageState extends State<LogPage> {
.map((log) {
return ListTile(
title: Text(log.singleLogRepTextNoNl),
subtitle:
Text(DateFormat.yMd(Localizations.localeOf(context).languageCode).format(log.date)),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode).format(log.date),
),
trailing: const Icon(Icons.copy),
onTap: () {
setState(() {
@@ -550,8 +557,9 @@ class _LogPageState extends State<LogPage> {
widget._log.weightUnit = log.weightUnitObj;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).dataCopied)));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context).dataCopied),
));
});
},
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
@@ -600,7 +608,9 @@ class _LogPageState extends State<LogPage> {
alignment: Alignment.center,
child: Text(
key.toString(),
style: const TextStyle(fontWeight: FontWeight.bold),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
),
@@ -609,10 +619,12 @@ class _LogPageState extends State<LogPage> {
const SizedBox(width: 10),
],
),
)
),
],
)
: MutedText(AppLocalizations.of(context).plateCalculatorNotDivisible),
: MutedText(
AppLocalizations.of(context).plateCalculatorNotDivisible,
),
),
const SizedBox(height: 3),
],
@@ -642,9 +654,10 @@ class _LogPageState extends State<LogPage> {
),
const SizedBox(height: 10),
Expanded(
child: (widget._workoutPlan.filterLogsByExerciseBase(widget._exerciseBase).isNotEmpty)
? getPastLogs()
: Container()),
child: (widget._workoutPlan.filterLogsByExerciseBase(widget._exerciseBase).isNotEmpty)
? getPastLogs()
: Container(),
),
// Only show calculator for barbell
if (widget._log.exerciseBaseObj.equipment.map((e) => e.id).contains(ID_EQUIPMENT_BARBELL))
getPlates(),
@@ -710,9 +723,10 @@ class ExerciseOverview extends StatelessWidget {
),
),
Html(
data: _exerciseBase
.getExercise(Localizations.localeOf(context).languageCode)
.description),
data: _exerciseBase
.getExercise(Localizations.localeOf(context).languageCode)
.description,
),
],
),
),
@@ -826,44 +840,46 @@ class _SessionPageState extends State<SessionPage> {
children: [
Flexible(
child: TextFormField(
decoration: InputDecoration(
labelText: AppLocalizations.of(context).timeStart,
errorMaxLines: 2,
),
controller: timeStartController,
onFieldSubmitted: (_) {},
onTap: () async {
// Stop keyboard from appearing
FocusScope.of(context).requestFocus(FocusNode());
decoration: InputDecoration(
labelText: AppLocalizations.of(context).timeStart,
errorMaxLines: 2,
),
controller: timeStartController,
onFieldSubmitted: (_) {},
onTap: () async {
// Stop keyboard from appearing
FocusScope.of(context).requestFocus(FocusNode());
// Open time picker
final pickedTime = await showTimePicker(
context: context,
initialTime: _session.timeStart,
);
// Open time picker
final pickedTime = await showTimePicker(
context: context,
initialTime: _session.timeStart,
);
if (pickedTime != null) {
timeStartController.text = timeToString(pickedTime)!;
_session.timeStart = pickedTime;
}
},
onSaved: (newValue) {
_session.timeStart = stringToTime(newValue);
},
validator: (_) {
final TimeOfDay startTime = stringToTime(timeStartController.text);
final TimeOfDay endTime = stringToTime(timeEndController.text);
if (startTime.isAfter(endTime)) {
return AppLocalizations.of(context).timeStartAhead;
}
return null;
}),
if (pickedTime != null) {
timeStartController.text = timeToString(pickedTime)!;
_session.timeStart = pickedTime;
}
},
onSaved: (newValue) {
_session.timeStart = stringToTime(newValue);
},
validator: (_) {
final TimeOfDay startTime = stringToTime(timeStartController.text);
final TimeOfDay endTime = stringToTime(timeEndController.text);
if (startTime.isAfter(endTime)) {
return AppLocalizations.of(context).timeStartAhead;
}
return null;
},
),
),
const SizedBox(width: 10),
Flexible(
child: TextFormField(
decoration:
InputDecoration(labelText: AppLocalizations.of(context).timeEnd),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).timeEnd,
),
controller: timeEndController,
onFieldSubmitted: (_) {},
onTap: () async {
@@ -900,8 +916,10 @@ class _SessionPageState extends State<SessionPage> {
// Save the entry on the server
try {
await Provider.of<WorkoutPlansProvider>(context, listen: false)
.addSession(_session);
await Provider.of<WorkoutPlansProvider>(
context,
listen: false,
).addSession(_session);
if (mounted) {
Navigator.of(context).pop();
}
@@ -1002,7 +1020,10 @@ class _TimerWidgetState extends State<TimerWidget> {
child: Center(
child: Text(
DateFormat('m:ss').format(today.add(Duration(seconds: _seconds))),
style: Theme.of(context).textTheme.displayLarge!.copyWith(color: wgerPrimaryColor),
style: Theme.of(context)
.textTheme
.displayLarge!
.copyWith(color: wgerPrimaryColor),
),
),
),
@@ -1018,8 +1039,12 @@ class NavigationFooter extends StatelessWidget {
final bool showPrevious;
final bool showNext;
const NavigationFooter(this._controller, this._ratioCompleted,
{this.showPrevious = true, this.showNext = true});
const NavigationFooter(
this._controller,
this._ratioCompleted, {
this.showPrevious = true,
this.showNext = true,
});
@override
Widget build(BuildContext context) {
@@ -1105,7 +1130,7 @@ class NavigationHeader extends StatelessWidget {
onPressed: () {
Navigator.of(context).pop();
},
)
),
],
);
}

View File

@@ -36,7 +36,8 @@ class ExerciseLogChart extends StatelessWidget {
@override
Widget build(BuildContext context) {
final workoutPlansData = Provider.of<WorkoutPlansProvider>(context, listen: false);
final workoutPlansData =
Provider.of<WorkoutPlansProvider>(context, listen: false);
final workout = workoutPlansData.currentPlan;
var colors = generateChartColors(1).iterator;
@@ -45,44 +46,46 @@ class ExerciseLogChart extends StatelessWidget {
}
return FutureBuilder(
future: getChartEntries(context),
builder: (context, AsyncSnapshot<Map<String, dynamic>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
colors = generateChartColors(snapshot.data!['chart_data'].length).iterator;
}
future: getChartEntries(context),
builder: (context, AsyncSnapshot<Map<String, dynamic>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
colors =
generateChartColors(snapshot.data!['chart_data'].length).iterator;
}
return SizedBox(
height: 260,
child: snapshot.connectionState == ConnectionState.waiting
? const Center(child: CircularProgressIndicator())
: Column(
mainAxisSize: MainAxisSize.max,
children: [
LogChartWidgetFl(snapshot.data!, _currentDate),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...snapshot.data!['chart_data'].map((e) {
// e is the list of logs with the same reps, so we can just take the
// first entry and read the reps from it. Yes, this is an amazingly ugly hack
final reps = e.first['reps'];
return SizedBox(
height: 260,
child: snapshot.connectionState == ConnectionState.waiting
? const Center(child: CircularProgressIndicator())
: Column(
mainAxisSize: MainAxisSize.max,
children: [
LogChartWidgetFl(snapshot.data!, _currentDate),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...snapshot.data!['chart_data'].map((e) {
// e is the list of logs with the same reps, so we can just take the
// first entry and read the reps from it. Yes, this is an amazingly ugly hack
final reps = e.first['reps'];
colors.moveNext();
return Indicator(
color: colors.current,
text: reps.toString(),
isSquare: false,
);
}).toList(),
],
),
const SizedBox(
height: 15,
)
],
),
);
});
colors.moveNext();
return Indicator(
color: colors.current,
text: reps.toString(),
isSquare: false,
);
}).toList(),
],
),
const SizedBox(
height: 15,
),
],
),
);
},
);
}
}
@@ -109,12 +112,14 @@ class _DayLogWidgetState extends State<DayLogWidget> {
child: Column(
children: [
Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode).format(widget._date),
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(widget._date),
style: Theme.of(context).textTheme.headlineSmall,
),
if (widget._session != null) const Text('Session data here'),
...widget._exerciseData.keys.map((base) {
final exercise = base.getExercise(Localizations.localeOf(context).languageCode);
final exercise =
base.getExercise(Localizations.localeOf(context).languageCode);
return Column(
children: [
if (widget._exerciseData[base]!.isNotEmpty)
@@ -133,7 +138,12 @@ class _DayLogWidgetState extends State<DayLogWidget> {
icon: const Icon(Icons.delete),
onPressed: () async {
showDeleteDialog(
context, exercise.name, log, exercise, widget._exerciseData);
context,
exercise.name,
log,
exercise,
widget._exerciseData,
);
},
),
],
@@ -142,10 +152,10 @@ class _DayLogWidgetState extends State<DayLogWidget> {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ExerciseLogChart(base, widget._date),
)
),
],
);
})
}),
],
),
);

View File

@@ -140,7 +140,7 @@ class _WorkoutLogCalendarState extends State<WorkoutLogCalendar> {
date,
entry['session'],
entry['exercises'],
)
),
];
}
@@ -190,17 +190,18 @@ class _WorkoutLogCalendarState extends State<WorkoutLogCalendar> {
const SizedBox(height: 8.0),
SizedBox(
child: ValueListenableBuilder<List<WorkoutLogEvent>>(
valueListenable: _selectedEvents,
builder: (context, logEvents, _) {
// At the moment there is only one "event" per day
return logEvents.isNotEmpty
? DayLogWidget(
logEvents.first.dateTime,
logEvents.first.exercises,
logEvents.first.session,
)
: Container();
}),
valueListenable: _selectedEvents,
builder: (context, logEvents, _) {
// At the moment there is only one "event" per day
return logEvents.isNotEmpty
? DayLogWidget(
logEvents.first.dateTime,
logEvents.first.exercises,
logEvents.first.session,
)
: Container();
},
),
),
],
);

View File

@@ -41,26 +41,28 @@ class _WorkoutPlanDetailState extends State<WorkoutPlanDetail> {
children: [
if (widget._workoutPlan.days.isNotEmpty)
Padding(
padding: const EdgeInsets.all(10),
child: ToggleButtons(
renderBorder: false,
onPressed: (int index) {
if (index == 1) {
widget._changeMode(WorkoutScreenMode.log);
}
},
isSelected: const [true, false],
children: const <Widget>[
Icon(Icons.table_chart),
Icon(Icons.show_chart),
],
)),
padding: const EdgeInsets.all(10),
child: ToggleButtons(
renderBorder: false,
onPressed: (int index) {
if (index == 1) {
widget._changeMode(WorkoutScreenMode.log);
}
},
isSelected: const [true, false],
children: const <Widget>[
Icon(Icons.table_chart),
Icon(Icons.show_chart),
],
),
),
if (widget._workoutPlan.description != '')
Padding(
padding: const EdgeInsets.all(15),
child: Text(widget._workoutPlan.description),
),
...widget._workoutPlan.days.map((workoutDay) => WorkoutDayWidget(workoutDay)),
...widget._workoutPlan.days
.map((workoutDay) => WorkoutDayWidget(workoutDay)),
Column(
children: [
ElevatedButton(

View File

@@ -42,26 +42,29 @@ class WorkoutPlansList extends StatelessWidget {
final currentWorkout = _workoutProvider.items[index];
return Card(
child: ListTile(
onTap: () {
_workoutProvider.setCurrentPlan(currentWorkout.id!);
child: ListTile(
onTap: () {
_workoutProvider.setCurrentPlan(currentWorkout.id!);
Navigator.of(context)
.pushNamed(WorkoutPlanScreen.routeName, arguments: currentWorkout);
},
title: Text(currentWorkout.name),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentWorkout.creationDate),
),
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
const VerticalDivider(),
IconButton(
icon: const Icon(Icons.delete),
tooltip: AppLocalizations.of(context).delete,
onPressed: () async {
// Delete workout from DB
await showDialog(
Navigator.of(context).pushNamed(
WorkoutPlanScreen.routeName,
arguments: currentWorkout,
);
},
title: Text(currentWorkout.name),
subtitle: Text(
DateFormat.yMd(
Localizations.localeOf(context).languageCode,
).format(currentWorkout.creationDate),
),
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
const VerticalDivider(),
IconButton(
icon: const Icon(Icons.delete),
tooltip: AppLocalizations.of(context).delete,
onPressed: () async {
// Delete workout from DB
await showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
@@ -70,19 +73,24 @@ class WorkoutPlansList extends StatelessWidget {
),
actions: [
TextButton(
child:
Text(MaterialLocalizations.of(context).cancelButtonLabel),
child: Text(
MaterialLocalizations.of(context).cancelButtonLabel,
),
onPressed: () => Navigator.of(contextDialog).pop(),
),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).colorScheme.error),
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
onPressed: () {
// Confirmed, delete the workout
Provider.of<WorkoutPlansProvider>(context, listen: false)
.deleteWorkout(currentWorkout.id!);
Provider.of<WorkoutPlansProvider>(
context,
listen: false,
).deleteWorkout(currentWorkout.id!);
// Close the popup
Navigator.of(contextDialog).pop();
@@ -100,11 +108,13 @@ class WorkoutPlansList extends StatelessWidget {
),
],
);
});
},
)
]),
));
},
);
},
),
]),
),
);
},
),
);