Add bar weight and color options to the plate calculator provider

This commit is contained in:
Roland Geider
2025-05-27 16:56:12 +02:00
parent cce0b94edc
commit 4bcc268896
13 changed files with 277 additions and 166 deletions

View File

@@ -37,7 +37,9 @@
"@passwordTooShort": {
"description": "Error message when the user a password that is too short"
},
"selectAvailablePlates": "Select Available Plates",
"selectAvailablePlates": "Select available plates",
"barWeight": "Bar weight",
"useColors": "Use colors",
"password": "Password",
"@password": {},
"confirmPassword": "Confirm password",

View File

@@ -90,20 +90,20 @@ void main() async {
await PreferenceHelper.instance.migrationSupportFunctionForSharedPreferences();
// Catch errors from Flutter itself (widget build, layout, paint, etc.)
FlutterError.onError = (FlutterErrorDetails details) {
final stack = details.stack ?? StackTrace.empty;
if (kDebugMode) {
FlutterError.dumpErrorToConsole(details);
}
// Don't show the full error dialog for network image loading errors.
if (details.exception is NetworkImageLoadException) {
return;
}
showGeneralErrorDialog(details.exception, stack);
// throw details.exception;
};
// FlutterError.onError = (FlutterErrorDetails details) {
// final stack = details.stack ?? StackTrace.empty;
// if (kDebugMode) {
// FlutterError.dumpErrorToConsole(details);
// }
//
// // Don't show the full error dialog for network image loading errors.
// if (details.exception is NetworkImageLoadException) {
// return;
// }
//
// // showGeneralErrorDialog(details.exception, stack);
// // throw details.exception;
// };
// Catch errors that happen outside of the Flutter framework (e.g., in async operations)
PlatformDispatcher.instance.onError = (error, stack) {

View File

@@ -11,6 +11,9 @@ import 'package:wger/helpers/shared_preferences.dart';
const DEFAULT_KG_PLATES = [2.5, 5, 10, 15, 20, 25];
const DEFAULT_LB_PLATES = [2.5, 5, 10, 25, 35, 45];
const DEFAULT_BAR_WEIGHT_KG = 20;
const DEFAULT_BAR_WEIGHT_LB = 45;
const PREFS_KEY_PLATES = 'selectedPlates';
final plateCalculatorProvider =
@@ -21,9 +24,6 @@ final plateCalculatorProvider =
class PlateCalculatorState {
final _logger = Logger('PlateWeightsState');
final barWeightKg = 20;
final barWeightLb = 45;
// https://en.wikipedia.org/wiki/Barbell#Bumper_plates
final Map<double, Color> plateColorMapKg = {
25: Colors.red,
@@ -48,26 +48,68 @@ class PlateCalculatorState {
1.25: Colors.white,
};
final bool useColors;
final bool isMetric;
final num totalWeight;
final num barWeight;
final List<num> selectedPlates;
final List<num> availablePlatesKg = const [0.5, 1, 1.25, 2, 2.5, 5, 10, 15, 20, 25];
final List<num> availablePlatesLb = const [2.5, 5, 10, 25, 35, 45];
final availableBarWeightsKg = [10, 15, 20];
final availableBarWeightsLb = [15, 20, 25, 33, 45];
PlateCalculatorState({
this.useColors = true,
num? barWeight,
this.isMetric = true,
this.totalWeight = 0,
List<num>? selectedPlates,
}) : selectedPlates = selectedPlates ?? [...DEFAULT_KG_PLATES];
}) : barWeight = barWeight ?? (isMetric ? DEFAULT_BAR_WEIGHT_KG : DEFAULT_BAR_WEIGHT_LB),
selectedPlates =
selectedPlates ?? (isMetric ? [...DEFAULT_KG_PLATES] : [...DEFAULT_LB_PLATES]);
num get barWeight {
return isMetric ? barWeightKg : barWeightLb;
PlateCalculatorState.fromJson(Map<String, dynamic> plateData)
: useColors = plateData['useColors'] ?? true,
isMetric = plateData['isMetric'] ?? true,
selectedPlates = plateData['selectedPlates']?.cast<num>() ?? [...DEFAULT_KG_PLATES],
barWeight = plateData['barWeight'] ??
((plateData['isMetric'] ?? true) ? DEFAULT_BAR_WEIGHT_KG : DEFAULT_BAR_WEIGHT_LB),
totalWeight = 0;
PlateCalculatorState copyWith({
bool? useColors,
bool? isMetric,
num? totalWeight,
num? barWeight,
List<num>? selectedPlates,
}) {
return PlateCalculatorState(
useColors: useColors ?? this.useColors,
isMetric: isMetric ?? this.isMetric,
totalWeight: totalWeight ?? this.totalWeight,
barWeight: barWeight ?? this.barWeight,
selectedPlates: selectedPlates ?? this.selectedPlates,
);
}
Map<String, dynamic> toJson() {
return {
'useColors': useColors,
'isMetric': isMetric,
'selectedPlates': selectedPlates,
'barWeight': barWeight,
};
}
List<num> get availablePlates {
return isMetric ? availablePlatesKg : availablePlatesLb;
}
List<num> get availableBarsWeights {
return isMetric ? availableBarWeightsKg : availableBarWeightsLb;
}
List<num> get platesList {
return plateCalculator(totalWeight, barWeight, selectedPlates);
}
@@ -81,27 +123,15 @@ class PlateCalculatorState {
}
Color getColor(num plate) {
if (!useColors) {
return Colors.grey;
}
if (isMetric) {
return plateColorMapKg[plate.toDouble()] ?? Colors.grey;
}
return plateColorMapLb[plate.toDouble()] ?? Colors.grey;
}
Map<String, dynamic> toJson() {
return {'isMetric': isMetric, 'selectedPlates': selectedPlates};
}
PlateCalculatorState copyWith({
bool? isMetric,
num? totalWeight,
List<num>? selectedPlates,
}) {
return PlateCalculatorState(
isMetric: isMetric ?? this.isMetric,
totalWeight: totalWeight ?? this.totalWeight,
selectedPlates: selectedPlates ?? this.selectedPlates,
);
}
}
class PlateCalculatorNotifier extends StateNotifier<PlateCalculatorState> {
@@ -125,13 +155,11 @@ class PlateCalculatorNotifier extends StateNotifier<PlateCalculatorState> {
if (prefsData != null) {
try {
final plateData = json.decode(prefsData);
state = state.copyWith(
isMetric: plateData['isMetric'] ?? true,
selectedPlates: plateData['selectedPlates'].cast<num>() ?? [...DEFAULT_KG_PLATES],
);
state = PlateCalculatorState.fromJson(json.decode(prefsData));
_logger.fine('Plate data loaded from SharedPreferences: ${state.toJson()}');
} catch (e) {
_logger.fine('Error decoding plate data from SharedPreferences: $e');
state = PlateCalculatorState();
}
}
}
@@ -147,6 +175,15 @@ class PlateCalculatorNotifier extends StateNotifier<PlateCalculatorState> {
await saveToSharedPrefs();
}
void setBarWeight(num x) {
_logger.fine('Setting bar weight to $x');
state = state.copyWith(barWeight: x);
}
void setUseColors(bool value) {
state = state.copyWith(useColors: value);
}
void unitChange({WeightUnitEnum? unit}) {
final WeightUnitEnum changeTo =
unit ?? (state.isMetric ? WeightUnitEnum.lb : WeightUnitEnum.kg);
@@ -155,6 +192,7 @@ class PlateCalculatorNotifier extends StateNotifier<PlateCalculatorState> {
state = state.copyWith(
isMetric: true,
totalWeight: 0,
barWeight: DEFAULT_BAR_WEIGHT_KG,
selectedPlates: [...DEFAULT_KG_PLATES],
);
}
@@ -163,6 +201,7 @@ class PlateCalculatorNotifier extends StateNotifier<PlateCalculatorState> {
state = state.copyWith(
isMetric: false,
totalWeight: 0,
barWeight: DEFAULT_BAR_WEIGHT_LB,
selectedPlates: [...DEFAULT_LB_PLATES],
);
}

View File

@@ -1,23 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
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/user.dart';
import 'package:wger/widgets/routines/plate_calculator.dart';
class AddPlateWeights extends ConsumerStatefulWidget {
const AddPlateWeights({super.key});
class ConfigureAvailablePlates extends ConsumerStatefulWidget {
const ConfigureAvailablePlates({super.key});
@override
ConsumerState<AddPlateWeights> createState() => _AddPlateWeightsState();
ConsumerState<ConfigureAvailablePlates> createState() => _AddPlateWeightsState();
}
class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
with SingleTickerProviderStateMixin {
final _unitController = TextEditingController();
class _AddPlateWeightsState extends ConsumerState<ConfigureAvailablePlates> {
@override
void initState() {
super.initState();
@@ -26,19 +21,13 @@ class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
});
}
@override
void dispose() {
_unitController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final i18n = AppLocalizations.of(context);
final plateWeightsState = ref.watch(plateCalculatorProvider);
final plateWeightsNotifier = ref.read(plateCalculatorProvider.notifier);
final userProvider = provider.Provider.of<UserProvider>(context);
// final userProvider = provider.Provider.of<UserProvider>(context);
return Scaffold(
appBar: AppBar(title: const Text('Select Available Plates')),
@@ -50,7 +39,6 @@ class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
child: DropdownMenu<WeightUnitEnum>(
width: double.infinity,
initialSelection: plateWeightsState.isMetric ? WeightUnitEnum.kg : WeightUnitEnum.lb,
controller: _unitController,
requestFocusOnTap: true,
label: Text(i18n.unit),
onSelected: (WeightUnitEnum? unit) {
@@ -69,6 +57,32 @@ class _AddPlateWeightsState extends ConsumerState<AddPlateWeights>
}).toList(),
),
),
Padding(
padding: const EdgeInsets.all(10),
child: DropdownMenu<num>(
width: double.infinity,
initialSelection: plateWeightsState.barWeight,
requestFocusOnTap: true,
label: Text(i18n.barWeight),
onSelected: (num? value) {
if (value == null) {
return;
}
plateWeightsNotifier.setBarWeight(value);
},
dropdownMenuEntries: plateWeightsState.availableBarsWeights.map((value) {
return DropdownMenuEntry<num>(
value: value,
label: value.toString(),
);
}).toList(),
),
),
SwitchListTile(
title: Text(i18n.useColors),
value: plateWeightsState.useColors,
onChanged: (state) => plateWeightsNotifier.setUseColors(state),
),
LayoutBuilder(
builder: (context, constraints) {
const double widthThreshold = 450.0;

View File

@@ -47,6 +47,8 @@ class GymModeScreen extends StatelessWidget {
.first;
return Scaffold(
// backgroundColor: Theme.of(context).cardColor,
// primary: false,
body: SafeArea(
child: Consumer<RoutinesProvider>(
builder: (context, value, child) => GymMode(dayDataGym, dayDataDisplay, args.iteration),

View File

@@ -42,7 +42,6 @@ class ExerciseOverview extends StatelessWidget {
_controller,
exercisePages: _exercisePages,
),
const Divider(),
Expanded(
child: SingleChildScrollView(
child: Padding(

View File

@@ -29,7 +29,7 @@ import 'package:wger/models/workouts/set_config_data.dart';
import 'package:wger/models/workouts/slot_data.dart';
import 'package:wger/providers/plate_weights.dart';
import 'package:wger/providers/routines.dart';
import 'package:wger/screens/add_plate_weights.dart';
import 'package:wger/screens/configure_plates.dart';
import 'package:wger/widgets/core/core.dart';
import 'package:wger/widgets/core/progress_indicator.dart';
import 'package:wger/widgets/routines/forms/reps_unit.dart';
@@ -260,6 +260,7 @@ class _LogPageState extends ConsumerState<LogPage> {
onChanged: (v) => {},
),
),
const SizedBox(width: 8),
],
),
if (_detailed)
@@ -271,6 +272,7 @@ class _LogPageState extends ConsumerState<LogPage> {
Flexible(
child: WeightUnitInputWidget(widget._log.weightUnitId, onChanged: (v) => {}),
),
const SizedBox(width: 8),
],
),
if (_detailed)
@@ -285,6 +287,7 @@ class _LogPageState extends ConsumerState<LogPage> {
},
),
SwitchListTile(
dense: true,
title: Text(AppLocalizations.of(context).setUnitsAndRir),
value: _detailed,
onChanged: (value) {
@@ -293,7 +296,7 @@ class _LogPageState extends ConsumerState<LogPage> {
});
},
),
ElevatedButton(
FilledButton(
onPressed: _isSaving
? null
: () async {
@@ -340,98 +343,119 @@ class _LogPageState extends ConsumerState<LogPage> {
}
Widget getPastLogs() {
return ListView(
children: [
Text(
AppLocalizations.of(context).labelWorkoutLogs,
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
...widget._workoutPlan.filterLogsByExercise(widget._exercise.id!, unique: true).map((log) {
return ListTile(
title: Text(log.singleLogRepTextNoNl),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode).format(log.date),
),
trailing: const Icon(Icons.copy),
onTap: () {
setState(() {
// Text field
_repetitionsController.text = log.repetitions?.toString() ?? '';
_weightController.text = log.weight?.toString() ?? '';
return Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
// color: Theme.of(context).secondaryHeaderColor,
// color: Theme.of(context).splashColor,
// color: Theme.of(context).colorScheme.onInverseSurface,
// border: Border.all(color: Colors.black, width: 1),
),
child: ListView(
children: [
Text(
AppLocalizations.of(context).labelWorkoutLogs,
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.center,
),
...widget._workoutPlan
.filterLogsByExercise(widget._exercise.id!, unique: true)
.map((log) {
return ListTile(
title: Text(log.singleLogRepTextNoNl),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode).format(log.date),
),
trailing: const Icon(Icons.copy),
onTap: () {
setState(() {
// Text field
_repetitionsController.text = log.repetitions?.toString() ?? '';
_weightController.text = log.weight?.toString() ?? '';
// Drop downs
widget._log.rir = log.rir;
widget._log.repetitionUnit = log.repetitionsUnitObj;
widget._log.weightUnit = log.weightUnitObj;
// Drop downs
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context).dataCopied),
));
});
},
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
);
}),
],
widget._log.rir = log.rir;
widget._log.repetitionUnit = log.repetitionsUnitObj;
widget._log.weightUnit = log.weightUnitObj;
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(AppLocalizations.of(context).dataCopied),
));
});
},
contentPadding: const EdgeInsets.symmetric(horizontal: 40),
);
}),
],
),
);
}
Widget getPlates() {
final plateWeightsState = ref.watch(plateCalculatorProvider);
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
AppLocalizations.of(context).plateCalculator,
style: Theme.of(context).textTheme.titleLarge,
),
IconButton(
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => const AddPlateWeights()));
},
icon: const Icon(Icons.settings),
),
],
),
SizedBox(
child: plateWeightsState.hasPlates
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...plateWeightsState.calculatePlates.entries.map(
(entry) => Row(
children: [
Text(entry.value.toString()),
const Text('×'),
PlateWeight(
value: entry.key,
size: 37,
padding: 2,
margin: 0,
color: ref.read(plateCalculatorProvider).getColor(entry.key),
return Container(
color: Theme.of(context).colorScheme.onInverseSurface,
child: Column(
children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// AppLocalizations.of(context).plateCalculator,
// style: Theme.of(context).textTheme.titleMedium,
// ),
// IconButton(
// onPressed: () {
// Navigator.of(context)
// .push(MaterialPageRoute(builder: (context) => const AddPlateWeights()));
// },
// icon: const Icon(Icons.settings, size: 16),
// ),
// ],
// ),
GestureDetector(
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => const ConfigureAvailablePlates()));
},
child: SizedBox(
child: plateWeightsState.hasPlates
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...plateWeightsState.calculatePlates.entries.map(
(entry) => Row(
children: [
Text(entry.value.toString()),
const Text('×'),
PlateWeight(
value: entry.key,
size: 37,
padding: 2,
margin: 0,
color: ref.read(plateCalculatorProvider).getColor(entry.key),
),
const SizedBox(width: 10),
],
),
const SizedBox(width: 10),
],
),
],
)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: MutedText(
AppLocalizations.of(context).plateCalculatorNotDivisible,
textAlign: TextAlign.center,
),
),
],
)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: MutedText(
AppLocalizations.of(context).plateCalculatorNotDivisible,
textAlign: TextAlign.center,
),
),
),
const SizedBox(height: 3),
],
),
),
const SizedBox(height: 3),
],
),
);
}
@@ -444,27 +468,43 @@ class _LogPageState extends ConsumerState<LogPage> {
widget._controller,
exercisePages: widget._exercisePages,
),
Center(
child: Text(
widget._configData.textRepr,
style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
Container(
color: Theme.of(context).colorScheme.onInverseSurface,
padding: const EdgeInsets.symmetric(vertical: 10),
child: Center(
child: Text(
widget._configData.textRepr,
style: Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(color: Theme.of(context).colorScheme.primary),
textAlign: TextAlign.center,
),
),
),
if (widget._slotData.comment != '')
if (widget._slotData.comment.isNotEmpty)
Text(widget._slotData.comment, textAlign: TextAlign.center),
// Only show calculator for barbell
if (widget._log.exercise.equipment.map((e) => e.id).contains(ID_EQUIPMENT_BARBELL))
getPlates(),
const SizedBox(height: 10),
Expanded(
child: (widget._workoutPlan.filterLogsByExercise(widget._exercise.id!).isNotEmpty)
? getPastLogs()
: Container(),
),
// Only show calculator for barbell
if (widget._log.exercise.equipment.map((e) => e.id).contains(ID_EQUIPMENT_BARBELL))
getPlates(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Card(child: getForm()),
padding: const EdgeInsets.all(10),
child: Card(
color: Theme.of(context).colorScheme.inversePrimary,
// color: Theme.of(context).secondaryHeaderColor,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: getForm(),
),
),
),
NavigationFooter(widget._controller, widget._ratioCompleted),
],

View File

@@ -52,7 +52,7 @@ class NavigationFooter extends StatelessWidget {
const SizedBox(width: 48),
Expanded(
child: LinearProgressIndicator(
minHeight: 1.5,
minHeight: 3,
value: _ratioCompleted,
valueColor: const AlwaysStoppedAnimation<Color>(wgerPrimaryColor),
),

View File

@@ -59,7 +59,6 @@ class SessionPage extends StatelessWidget {
_controller,
exercisePages: _exercisePages,
),
const Divider(),
Expanded(child: Container()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),

View File

@@ -21,7 +21,6 @@ class StartPage extends StatelessWidget {
_controller,
exercisePages: _exercisePages,
),
const Divider(),
Expanded(
child: ListView(
children: [

View File

@@ -31,7 +31,7 @@ class PlateWeight extends StatelessWidget {
decoration: BoxDecoration(
color: isSelected ? color : Colors.black12,
shape: BoxShape.circle,
border: Border.all(color: Colors.black, width: isSelected ? 1 : 0),
border: Border.all(color: Colors.black, width: isSelected ? 2 : 0),
),
child: Text(
value.toString(),

View File

@@ -39,7 +39,7 @@ void main() {
expect(notifier.state.isMetric, false);
expect(notifier.state.totalWeight, 0);
expect(notifier.state.selectedPlates, DEFAULT_LB_PLATES);
expect(notifier.state.barWeight, notifier.state.barWeightLb);
expect(notifier.state.barWeight, DEFAULT_BAR_WEIGHT_LB);
expect(notifier.state.availablePlates, notifier.state.availablePlatesLb);
// Change back to metric
@@ -48,7 +48,7 @@ void main() {
expect(notifier.state.isMetric, true);
expect(notifier.state.totalWeight, 0);
expect(notifier.state.selectedPlates, DEFAULT_KG_PLATES);
expect(notifier.state.barWeight, notifier.state.barWeightKg);
expect(notifier.state.barWeight, DEFAULT_BAR_WEIGHT_KG);
expect(notifier.state.availablePlates, notifier.state.availablePlatesKg);
});
@@ -64,10 +64,12 @@ void main() {
final updatedState = initialState.copyWith(
isMetric: false,
totalWeight: 100,
barWeight: 15,
selectedPlates: [1, 2, 3],
);
expect(updatedState.isMetric, false);
expect(updatedState.barWeight, 15);
expect(updatedState.totalWeight, 100);
expect(updatedState.selectedPlates, [1, 2, 3]);
});
@@ -76,18 +78,19 @@ void main() {
final state = PlateCalculatorState(isMetric: false, selectedPlates: [10, 20]);
final json = state.toJson();
expect(json['isMetric'], false);
expect(json['barWeight'], DEFAULT_BAR_WEIGHT_LB);
expect(json['selectedPlates'], [10, 20]);
});
test('barWeight returns correct value based on isMetric', () {
test('barWeight returns correct default value based on isMetric', () {
final metricState = PlateCalculatorState(isMetric: true);
expect(metricState.barWeight, metricState.barWeightKg);
expect(metricState.barWeight, DEFAULT_BAR_WEIGHT_KG);
final imperialState = PlateCalculatorState(isMetric: false);
expect(imperialState.barWeight, imperialState.barWeightLb);
expect(imperialState.barWeight, DEFAULT_BAR_WEIGHT_LB);
});
test('availablePlates returns correct list based on isMetric', () {
test('availablePlates returns correct default list based on isMetric', () {
final metricState = PlateCalculatorState(isMetric: true);
expect(metricState.availablePlates, metricState.availablePlatesKg);
@@ -95,14 +98,26 @@ void main() {
expect(imperialState.availablePlates, metricState.availablePlatesLb);
});
test('getColor returns correct color', () {
test('getColor returns correct color - 1', () {
final metricState = PlateCalculatorState(isMetric: true);
expect(metricState.getColor(20), Colors.blue);
expect(metricState.getColor(10), Colors.green);
expect(metricState.getColor(0.1), Colors.grey, reason: 'Fallback color');
final imperialState = PlateCalculatorState(isMetric: false);
expect(imperialState.getColor(45), Colors.blue);
expect(imperialState.getColor(35), Colors.yellow);
expect(imperialState.getColor(0.1), Colors.grey, reason: 'Fallback color');
});
test('getColor returns correct color - 2', () {
final metricState = PlateCalculatorState(isMetric: true, useColors: false);
expect(metricState.getColor(20), Colors.grey);
expect(metricState.getColor(10), Colors.grey);
final imperialState = PlateCalculatorState(isMetric: false, useColors: false);
expect(imperialState.getColor(45), Colors.grey);
expect(imperialState.getColor(25), Colors.grey);
});
});
}

View File

@@ -53,9 +53,11 @@ void main() {
final testRoutine = getTestRoutine();
final testExercises = getTestExercises();
Widget renderGymMode({locale = 'en'}) {
setUp(() {
SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty();
});
Widget renderGymMode({locale = 'en'}) {
return ChangeNotifierProvider<RoutinesProvider>(
create: (context) => RoutinesProvider(
mockBaseProvider,