mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Polish the add plate widget
This commit is contained in:
@@ -130,3 +130,5 @@ const LIBERAPAY_URL = 'https://liberapay.com/wger';
|
|||||||
/// the milliseconds themselves can cause the application to crash since it runs
|
/// the milliseconds themselves can cause the application to crash since it runs
|
||||||
/// out of memory...
|
/// out of memory...
|
||||||
const double CHART_MILLISECOND_FACTOR = 100000.0;
|
const double CHART_MILLISECOND_FACTOR = 100000.0;
|
||||||
|
|
||||||
|
enum WeightUnitEnum { kg, lb }
|
||||||
|
|||||||
@@ -19,13 +19,14 @@
|
|||||||
/// Calculates the number of plates needed to reach a specific weight
|
/// Calculates the number of plates needed to reach a specific weight
|
||||||
List<num> plateCalculator(num totalWeight, num barWeight, List<num> plates) {
|
List<num> plateCalculator(num totalWeight, num barWeight, List<num> plates) {
|
||||||
final List<num> result = [];
|
final List<num> result = [];
|
||||||
|
final sortedPlates = List.of(plates)..sort();
|
||||||
|
|
||||||
// Weight is less than the bar
|
// Weight is less than the bar
|
||||||
if (totalWeight < barWeight) {
|
if (totalWeight < barWeight) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plates.isEmpty) {
|
if (sortedPlates.isEmpty) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,12 +34,12 @@ List<num> plateCalculator(num totalWeight, num barWeight, List<num> plates) {
|
|||||||
totalWeight = (totalWeight - barWeight) / 2;
|
totalWeight = (totalWeight - barWeight) / 2;
|
||||||
|
|
||||||
// Weight can't be divided with the smallest plate
|
// Weight can't be divided with the smallest plate
|
||||||
if (totalWeight % plates.first > 0) {
|
if (totalWeight % sortedPlates.first > 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through the plates, beginning with the biggest ones
|
// Iterate through the plates, beginning with the biggest ones
|
||||||
for (final plate in plates.reversed) {
|
for (final plate in sortedPlates.reversed) {
|
||||||
while (totalWeight >= plate) {
|
while (totalWeight >= plate) {
|
||||||
totalWeight -= plate;
|
totalWeight -= plate;
|
||||||
result.add(plate);
|
result.add(plate);
|
||||||
|
|||||||
@@ -90,20 +90,20 @@ void main() async {
|
|||||||
await PreferenceHelper.instance.migrationSupportFunctionForSharedPreferences();
|
await PreferenceHelper.instance.migrationSupportFunctionForSharedPreferences();
|
||||||
|
|
||||||
// Catch errors from Flutter itself (widget build, layout, paint, etc.)
|
// Catch errors from Flutter itself (widget build, layout, paint, etc.)
|
||||||
FlutterError.onError = (FlutterErrorDetails details) {
|
// FlutterError.onError = (FlutterErrorDetails details) {
|
||||||
final stack = details.stack ?? StackTrace.empty;
|
// final stack = details.stack ?? StackTrace.empty;
|
||||||
if (kDebugMode) {
|
// if (kDebugMode) {
|
||||||
FlutterError.dumpErrorToConsole(details);
|
// FlutterError.dumpErrorToConsole(details);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Don't show the full error dialog for network image loading errors.
|
// // Don't show the full error dialog for network image loading errors.
|
||||||
if (details.exception is NetworkImageLoadException) {
|
// if (details.exception is NetworkImageLoadException) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
showGeneralErrorDialog(details.exception, stack);
|
// // showGeneralErrorDialog(details.exception, stack);
|
||||||
// throw details.exception;
|
// throw details.exception;
|
||||||
};
|
// };
|
||||||
|
|
||||||
// Catch errors that happen outside of the Flutter framework (e.g., in async operations)
|
// Catch errors that happen outside of the Flutter framework (e.g., in async operations)
|
||||||
PlatformDispatcher.instance.onError = (error, stack) {
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
@@ -114,8 +114,8 @@ void main() async {
|
|||||||
if (error is WgerHttpException) {
|
if (error is WgerHttpException) {
|
||||||
showHttpExceptionErrorDialog(error);
|
showHttpExceptionErrorDialog(error);
|
||||||
} else {
|
} else {
|
||||||
showGeneralErrorDialog(error, stack);
|
// showGeneralErrorDialog(error, stack);
|
||||||
// throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true to indicate that the error has been handled.
|
// Return true to indicate that the error has been handled.
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:wger/helpers/consts.dart';
|
||||||
import 'package:wger/helpers/gym_mode.dart';
|
import 'package:wger/helpers/gym_mode.dart';
|
||||||
|
import 'package:wger/helpers/shared_preferences.dart';
|
||||||
|
|
||||||
const DEFAULT_KG_PLATES = [2.5, 5, 10, 15, 20, 25];
|
const DEFAULT_KG_PLATES = [2.5, 5, 10, 15, 20, 25];
|
||||||
|
const DEFAULT_LB_PLATES = [2.5, 5, 10, 25, 35, 45];
|
||||||
|
|
||||||
const PREFS_KEY_PLATES = 'selectedPlates';
|
const PREFS_KEY_PLATES = 'selectedPlates';
|
||||||
|
|
||||||
@@ -14,6 +18,12 @@ final plateWeightsProvider = StateNotifierProvider<PlateWeightsNotifier, PlateWe
|
|||||||
});
|
});
|
||||||
|
|
||||||
class PlateWeightsState {
|
class PlateWeightsState {
|
||||||
|
final _logger = Logger('PlateWeightsState');
|
||||||
|
|
||||||
|
final barWeightKg = 20;
|
||||||
|
final barWeightLb = 45;
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Barbell#Bumper_plates
|
||||||
final Map<double, Color> plateColorMapKg = {
|
final Map<double, Color> plateColorMapKg = {
|
||||||
25: Colors.red,
|
25: Colors.red,
|
||||||
20: Colors.blue,
|
20: Colors.blue,
|
||||||
@@ -33,26 +43,29 @@ class PlateWeightsState {
|
|||||||
25: Colors.green,
|
25: Colors.green,
|
||||||
10: Colors.white,
|
10: Colors.white,
|
||||||
5: Colors.blue,
|
5: Colors.blue,
|
||||||
|
2.5: Colors.green,
|
||||||
1.25: Colors.white,
|
1.25: Colors.white,
|
||||||
};
|
};
|
||||||
|
|
||||||
final bool isMetric;
|
final bool isMetric;
|
||||||
final num totalWeight;
|
final num totalWeight;
|
||||||
final num barWeight;
|
|
||||||
final List<num> selectedPlates;
|
final List<num> selectedPlates;
|
||||||
final List<num> kgWeights = const [0.5, 1, 1.25, 2, 2.5, 5, 10, 15, 20, 25];
|
final List<num> availablePlatesKg = const [0.5, 1, 1.25, 2, 2.5, 5, 10, 15, 20, 25];
|
||||||
final List<num> lbsWeights = const [2.5, 5, 10, 25, 35, 45];
|
final List<num> availablePlatesLb = const [2.5, 5, 10, 25, 35, 45];
|
||||||
|
|
||||||
PlateWeightsState({
|
PlateWeightsState({
|
||||||
this.isMetric = true,
|
this.isMetric = true,
|
||||||
this.totalWeight = 0,
|
this.totalWeight = 0,
|
||||||
this.barWeight = 20,
|
|
||||||
List<num>? selectedPlates,
|
List<num>? selectedPlates,
|
||||||
}) : selectedPlates = selectedPlates ?? [...DEFAULT_KG_PLATES];
|
}) : selectedPlates = selectedPlates ?? [...DEFAULT_KG_PLATES];
|
||||||
|
|
||||||
num get totalWeightInKg => isMetric ? totalWeight : totalWeight / 2.205;
|
num get barWeight {
|
||||||
|
return isMetric ? barWeightKg : barWeightLb;
|
||||||
|
}
|
||||||
|
|
||||||
num get barWeightInKg => isMetric ? barWeight : barWeight / 2.205;
|
List<num> get availablePlates {
|
||||||
|
return isMetric ? availablePlatesKg : availablePlatesLb;
|
||||||
|
}
|
||||||
|
|
||||||
List<num> get platesList {
|
List<num> get platesList {
|
||||||
return plateCalculator(totalWeight, barWeight, selectedPlates);
|
return plateCalculator(totalWeight, barWeight, selectedPlates);
|
||||||
@@ -63,8 +76,7 @@ class PlateWeightsState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<num, int> get calculatePlates {
|
Map<num, int> get calculatePlates {
|
||||||
List<num> sortedPlates = List.from(selectedPlates)..sort();
|
return groupPlates(plateCalculator(totalWeight, barWeight, selectedPlates));
|
||||||
return groupPlates(plateCalculator(totalWeight, barWeight, sortedPlates));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color getColor(num plate) {
|
Color getColor(num plate) {
|
||||||
@@ -74,91 +86,87 @@ class PlateWeightsState {
|
|||||||
return plateColorMapLb[plate.toDouble()] ?? Colors.grey;
|
return plateColorMapLb[plate.toDouble()] ?? Colors.grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {'isMetric': isMetric, 'selectedPlates': selectedPlates};
|
||||||
|
}
|
||||||
|
|
||||||
PlateWeightsState copyWith({
|
PlateWeightsState copyWith({
|
||||||
bool? isMetric,
|
bool? isMetric,
|
||||||
num? totalWeight,
|
num? totalWeight,
|
||||||
num? barWeight,
|
|
||||||
List<num>? selectedPlates,
|
List<num>? selectedPlates,
|
||||||
}) {
|
}) {
|
||||||
return PlateWeightsState(
|
return PlateWeightsState(
|
||||||
isMetric: isMetric ?? this.isMetric,
|
isMetric: isMetric ?? this.isMetric,
|
||||||
totalWeight: totalWeight ?? this.totalWeight,
|
totalWeight: totalWeight ?? this.totalWeight,
|
||||||
barWeight: barWeight ?? this.barWeight,
|
|
||||||
selectedPlates: selectedPlates ?? this.selectedPlates,
|
selectedPlates: selectedPlates ?? this.selectedPlates,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlateWeightsNotifier extends StateNotifier<PlateWeightsState> {
|
class PlateWeightsNotifier extends StateNotifier<PlateWeightsState> {
|
||||||
PlateWeightsNotifier() : super(PlateWeightsState()) {
|
final _logger = Logger('PlateWeightsNotifier');
|
||||||
_readPlates();
|
|
||||||
|
late SharedPreferencesAsync prefs;
|
||||||
|
|
||||||
|
PlateWeightsNotifier({SharedPreferencesAsync? prefs}) : super(PlateWeightsState()) {
|
||||||
|
this.prefs = prefs ?? PreferenceHelper.asyncPref;
|
||||||
|
_readDataFromSharedPrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _saveIntoSharedPrefs() async {
|
Future<void> saveToSharedPrefs() async {
|
||||||
final pref = await SharedPreferences.getInstance();
|
await prefs.setString(PREFS_KEY_PLATES, jsonEncode(state.toJson()));
|
||||||
pref.setString(PREFS_KEY_PLATES, jsonEncode(state.selectedPlates));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _readPlates() async {
|
Future<void> _readDataFromSharedPrefs() async {
|
||||||
final pref = await SharedPreferences.getInstance();
|
final prefsData = await prefs.getString(PREFS_KEY_PLATES);
|
||||||
final platePrefData = pref.getString(PREFS_KEY_PLATES);
|
|
||||||
if (platePrefData != null) {
|
if (prefsData != null) {
|
||||||
try {
|
try {
|
||||||
final plateData = json.decode(platePrefData);
|
final plateData = json.decode(prefsData);
|
||||||
if (plateData is List) {
|
state = state.copyWith(
|
||||||
state = state.copyWith(selectedPlates: plateData.cast<num>());
|
isMetric: plateData['isMetric'] ?? true,
|
||||||
} else {
|
selectedPlates: plateData['selectedPlates'].cast<num>() ?? [...DEFAULT_KG_PLATES],
|
||||||
throw const FormatException('Not a List');
|
);
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
state = state.copyWith(selectedPlates: []);
|
_logger.fine('Error decoding plate data from SharedPreferences: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> toggleSelection(num x) async {
|
Future<void> toggleSelection(num x) async {
|
||||||
final newSelectedPlates = List<num>.from(state.selectedPlates);
|
final newSelectedPlates = List.of(state.selectedPlates);
|
||||||
if (newSelectedPlates.contains(x)) {
|
if (newSelectedPlates.contains(x)) {
|
||||||
newSelectedPlates.remove(x);
|
newSelectedPlates.remove(x);
|
||||||
} else {
|
} else {
|
||||||
newSelectedPlates.add(x);
|
newSelectedPlates.add(x);
|
||||||
}
|
}
|
||||||
state = state.copyWith(selectedPlates: newSelectedPlates);
|
state = state.copyWith(selectedPlates: newSelectedPlates);
|
||||||
await _saveIntoSharedPrefs();
|
await saveToSharedPrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void unitChange() {
|
void unitChange({WeightUnitEnum? unit}) {
|
||||||
if (state.isMetric == false) {
|
final WeightUnitEnum changeTo =
|
||||||
|
unit ?? (state.isMetric ? WeightUnitEnum.lb : WeightUnitEnum.kg);
|
||||||
|
|
||||||
|
if (changeTo == WeightUnitEnum.kg && !state.isMetric) {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
isMetric: true,
|
isMetric: true,
|
||||||
totalWeight: state.totalWeightInKg,
|
totalWeight: 0,
|
||||||
barWeight: state.barWeightInKg,
|
selectedPlates: [...DEFAULT_KG_PLATES],
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (changeTo == WeightUnitEnum.lb && state.isMetric) {
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
isMetric: false,
|
isMetric: false,
|
||||||
totalWeight: state.totalWeightInKg * 2.205,
|
totalWeight: 0,
|
||||||
barWeight: state.barWeightInKg * 2.205,
|
selectedPlates: [...DEFAULT_LB_PLATES],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() async {
|
|
||||||
state = state.copyWith(selectedPlates: []);
|
|
||||||
await _saveIntoSharedPrefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setWeight(num x) {
|
void setWeight(num x) {
|
||||||
|
_logger.fine('Setting weight to $x');
|
||||||
state = state.copyWith(totalWeight: x);
|
state = state.copyWith(totalWeight: x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetPlates() async {
|
|
||||||
state = state.copyWith(selectedPlates: [...DEFAULT_KG_PLATES]);
|
|
||||||
await _saveIntoSharedPrefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void selectAllPlates() async {
|
|
||||||
state = state.copyWith(selectedPlates: [...state.kgWeights]);
|
|
||||||
await _saveIntoSharedPrefs();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,13 +46,12 @@ class UserProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change the unit of plates
|
// change the unit of plates
|
||||||
void unitChange() {
|
void changeUnit({changeTo = 'kg'}) {
|
||||||
if (profile?.weightUnitStr == 'kg') {
|
if (changeTo == 'kg') {
|
||||||
profile?.weightUnitStr = 'lb';
|
profile?.weightUnitStr = 'lb';
|
||||||
} else {
|
} else {
|
||||||
profile?.weightUnitStr = 'kg';
|
profile?.weightUnitStr = 'kg';
|
||||||
}
|
}
|
||||||
ChangeNotifier();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load theme mode from SharedPreferences
|
// Load theme mode from SharedPreferences
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:provider/provider.dart' as provider;
|
import 'package:provider/provider.dart' as provider;
|
||||||
|
import 'package:wger/helpers/consts.dart';
|
||||||
|
import 'package:wger/l10n/generated/app_localizations.dart';
|
||||||
import 'package:wger/providers/plate_weights.dart';
|
import 'package:wger/providers/plate_weights.dart';
|
||||||
import 'package:wger/providers/user.dart';
|
import 'package:wger/providers/user.dart';
|
||||||
|
import 'package:wger/widgets/routines/plate_calculator.dart';
|
||||||
|
|
||||||
class AddPlateWeights extends ConsumerStatefulWidget {
|
class AddPlateWeights extends ConsumerStatefulWidget {
|
||||||
const AddPlateWeights({super.key});
|
const AddPlateWeights({super.key});
|
||||||
@@ -13,8 +16,7 @@ class AddPlateWeights extends ConsumerStatefulWidget {
|
|||||||
|
|
||||||
class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
|
class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late AnimationController _controller;
|
final _unitController = TextEditingController();
|
||||||
late Animation<Offset> _animation;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -22,151 +24,79 @@ class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
|
|||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
ref.read(plateWeightsProvider.notifier);
|
ref.read(plateWeightsProvider.notifier);
|
||||||
});
|
});
|
||||||
_controller = AnimationController(
|
|
||||||
vsync: this,
|
|
||||||
duration: const Duration(seconds: 1),
|
|
||||||
);
|
|
||||||
_animation = Tween<Offset>(
|
|
||||||
begin: const Offset(-1.0, 0.0),
|
|
||||||
end: Offset.zero,
|
|
||||||
).animate(CurvedAnimation(
|
|
||||||
parent: _controller,
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
));
|
|
||||||
|
|
||||||
_controller.forward();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_controller.dispose();
|
_unitController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final i18n = AppLocalizations.of(context);
|
||||||
|
|
||||||
final plateWeightsState = ref.watch(plateWeightsProvider);
|
final plateWeightsState = ref.watch(plateWeightsProvider);
|
||||||
final plateWeightsNotifier = ref.read(plateWeightsProvider.notifier);
|
final plateWeightsNotifier = ref.read(plateWeightsProvider.notifier);
|
||||||
final userProviderInstance = provider.Provider.of<UserProvider>(context);
|
final userProvider = provider.Provider.of<UserProvider>(context);
|
||||||
final userProfile = userProviderInstance.profile;
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Select Available Plates')),
|
appBar: AppBar(title: const Text('Select Available Plates')),
|
||||||
body: Column(
|
body: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
padding: const EdgeInsets.all(10),
|
||||||
children: [
|
child: DropdownMenu<WeightUnitEnum>(
|
||||||
const Text('Preferred Unit'),
|
width: double.infinity,
|
||||||
DropdownButton<String>(
|
initialSelection: plateWeightsState.isMetric ? WeightUnitEnum.kg : WeightUnitEnum.lb,
|
||||||
value: (userProfile?.isMetric ?? true) ? 'kg' : 'lbs',
|
controller: _unitController,
|
||||||
onChanged: (String? newValue) {
|
requestFocusOnTap: true,
|
||||||
if (newValue == null) return;
|
label: Text(i18n.unit),
|
||||||
final selectedUnitIsMetric = (newValue == 'kg');
|
onSelected: (WeightUnitEnum? unit) {
|
||||||
if (selectedUnitIsMetric != (userProfile?.isMetric ?? true)) {
|
if (unit == null) {
|
||||||
plateWeightsNotifier.unitChange();
|
return;
|
||||||
provider.Provider.of<UserProvider>(context, listen: false).unitChange();
|
}
|
||||||
_controller.reset();
|
plateWeightsNotifier.unitChange(unit: unit);
|
||||||
_controller.forward();
|
// userProvider.changeUnit(changeTo: unit.name);
|
||||||
}
|
// userProvider.saveProfile();
|
||||||
},
|
},
|
||||||
items: ['kg', 'lbs'].map((unit) {
|
dropdownMenuEntries: WeightUnitEnum.values.map((unit) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuEntry<WeightUnitEnum>(
|
||||||
value: unit,
|
value: unit,
|
||||||
child: Text(unit),
|
label: unit == WeightUnitEnum.kg ? i18n.kg : i18n.lb,
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
const double widthThreshold = 450.0;
|
||||||
|
final int crossAxisCount = constraints.maxWidth > widthThreshold ? 10 : 5;
|
||||||
|
return GridView.count(
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: plateWeightsState.availablePlates.map((number) {
|
||||||
|
final bool isSelected = plateWeightsState.selectedPlates.contains(number);
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => plateWeightsNotifier.toggleSelection(number),
|
||||||
|
child: PlateWeight(
|
||||||
|
value: number,
|
||||||
|
isSelected: isSelected,
|
||||||
|
color: plateWeightsState.getColor(number),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
);
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
Wrap(
|
FilledButton(
|
||||||
alignment: WrapAlignment.center,
|
onPressed: () {
|
||||||
runAlignment: WrapAlignment.center,
|
plateWeightsNotifier.saveToSharedPrefs();
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
Navigator.pop(context);
|
||||||
children: (userProfile == null || userProfile.isMetric)
|
},
|
||||||
? plateWeightsState.kgWeights.map((number) {
|
child: Text(i18n.save),
|
||||||
return SlideTransition(
|
|
||||||
position: _animation,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => plateWeightsNotifier.toggleSelection(number),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Container(
|
|
||||||
height: 50,
|
|
||||||
width: 50,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
margin: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: plateWeightsState.selectedPlates.contains(number)
|
|
||||||
? plateWeightsState.getColor(number)
|
|
||||||
: const Color.fromARGB(255, 97, 105, 101),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(color: Colors.black, width: 2),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
number.toString(),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList()
|
|
||||||
: plateWeightsState.lbsWeights.map((number) {
|
|
||||||
return SlideTransition(
|
|
||||||
position: _animation,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => plateWeightsNotifier.toggleSelection(number),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.all(8),
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: plateWeightsState.selectedPlates.contains(number)
|
|
||||||
? const Color.fromARGB(255, 82, 226, 236)
|
|
||||||
: const Color.fromARGB(255, 97, 105, 101),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'$number lbs',
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
child: const Text('Done'),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
plateWeightsNotifier.selectAllPlates();
|
|
||||||
},
|
|
||||||
child: const Text('Select all'),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
plateWeightsNotifier.resetPlates();
|
|
||||||
},
|
|
||||||
child: const Text('Reset'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import 'package:wger/widgets/routines/forms/reps_unit.dart';
|
|||||||
import 'package:wger/widgets/routines/forms/rir.dart';
|
import 'package:wger/widgets/routines/forms/rir.dart';
|
||||||
import 'package:wger/widgets/routines/forms/weight_unit.dart';
|
import 'package:wger/widgets/routines/forms/weight_unit.dart';
|
||||||
import 'package:wger/widgets/routines/gym_mode/navigation.dart';
|
import 'package:wger/widgets/routines/gym_mode/navigation.dart';
|
||||||
|
import 'package:wger/widgets/routines/plate_calculator.dart';
|
||||||
|
|
||||||
class LogPage extends ConsumerStatefulWidget {
|
class LogPage extends ConsumerStatefulWidget {
|
||||||
final PageController _controller;
|
final PageController _controller;
|
||||||
@@ -379,21 +380,26 @@ class _LogPageState extends ConsumerState<LogPage> {
|
|||||||
|
|
||||||
Widget getPlates() {
|
Widget getPlates() {
|
||||||
final plateWeightsState = ref.watch(plateWeightsProvider);
|
final plateWeightsState = ref.watch(plateWeightsProvider);
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Row(
|
||||||
AppLocalizations.of(context).plateCalculator,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
children: [
|
||||||
),
|
Text(
|
||||||
IconButton(
|
AppLocalizations.of(context).plateCalculator,
|
||||||
onPressed: () {
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
Navigator.of(context)
|
),
|
||||||
.push(MaterialPageRoute(builder: (context) => const AddPlateWeights()));
|
IconButton(
|
||||||
},
|
onPressed: () {
|
||||||
icon: const Icon(Icons.settings),
|
Navigator.of(context)
|
||||||
|
.push(MaterialPageRoute(builder: (context) => const AddPlateWeights()));
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.settings),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 35,
|
|
||||||
child: plateWeightsState.hasPlates
|
child: plateWeightsState.hasPlates
|
||||||
? Row(
|
? Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -403,28 +409,12 @@ class _LogPageState extends ConsumerState<LogPage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(entry.value.toString()),
|
Text(entry.value.toString()),
|
||||||
const Text('×'),
|
const Text('×'),
|
||||||
Container(
|
PlateWeight(
|
||||||
decoration: BoxDecoration(
|
value: entry.key,
|
||||||
color: ref.read(plateWeightsProvider).getColor(entry.key),
|
size: 37,
|
||||||
shape: BoxShape.circle,
|
padding: 2,
|
||||||
border: Border.all(color: Colors.black, width: 1),
|
margin: 0,
|
||||||
),
|
color: ref.read(plateWeightsProvider).getColor(entry.key),
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
|
||||||
child: SizedBox(
|
|
||||||
height: 35,
|
|
||||||
width: 35,
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Text(
|
|
||||||
entry.key.toString(),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
|
|||||||
48
lib/widgets/routines/plate_calculator.dart
Normal file
48
lib/widgets/routines/plate_calculator.dart
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class PlateWeight extends StatelessWidget {
|
||||||
|
final num value;
|
||||||
|
final Color color;
|
||||||
|
final bool isSelected;
|
||||||
|
final double size;
|
||||||
|
final double padding;
|
||||||
|
final double margin;
|
||||||
|
|
||||||
|
const PlateWeight({
|
||||||
|
super.key,
|
||||||
|
required this.value,
|
||||||
|
required this.color,
|
||||||
|
this.isSelected = true,
|
||||||
|
this.size = 50,
|
||||||
|
this.padding = 8,
|
||||||
|
this.margin = 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: size,
|
||||||
|
width: size,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(padding),
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.all(margin),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected ? color : Colors.black12,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(color: Colors.black, width: isSelected ? 1 : 0),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
value.toString(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: isSelected ? Colors.black : Colors.black87,
|
||||||
|
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1181,10 +1181,11 @@ class MockUserProvider extends _i1.Mock implements _i22.UserProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void unitChange() => super.noSuchMethod(
|
void changeUnit({dynamic changeTo = 'kg'}) => super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#unitChange,
|
#changeUnit,
|
||||||
[],
|
[],
|
||||||
|
{#changeTo: changeTo},
|
||||||
),
|
),
|
||||||
returnValueForMissingStub: null,
|
returnValueForMissingStub: null,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -593,10 +593,11 @@ class MockUserProvider extends _i1.Mock implements _i17.UserProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void unitChange() => super.noSuchMethod(
|
void changeUnit({dynamic changeTo = 'kg'}) => super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#unitChange,
|
#changeUnit,
|
||||||
[],
|
[],
|
||||||
|
{#changeTo: changeTo},
|
||||||
),
|
),
|
||||||
returnValueForMissingStub: null,
|
returnValueForMissingStub: null,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -340,10 +340,11 @@ class MockUserProvider extends _i1.Mock implements _i13.UserProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void unitChange() => super.noSuchMethod(
|
void changeUnit({dynamic changeTo = 'kg'}) => super.noSuchMethod(
|
||||||
Invocation.method(
|
Invocation.method(
|
||||||
#unitChange,
|
#changeUnit,
|
||||||
[],
|
[],
|
||||||
|
{#changeTo: changeTo},
|
||||||
),
|
),
|
||||||
returnValueForMissingStub: null,
|
returnValueForMissingStub: null,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user