Merge branch 'master' into feature/flexible-routines

# Conflicts:
#	lib/main.dart
#	lib/models/exercises/exercise.dart
#	lib/models/exercises/exercise_api.freezed.dart
#	lib/models/workouts/day.g.dart
#	lib/models/workouts/session.g.dart
#	lib/models/workouts/workout_plan.g.dart
#	lib/providers/exercises.dart
#	lib/widgets/core/settings.dart
#	pubspec.lock
#	test/auth/auth_screen_test.mocks.dart
#	test/core/settings_test.mocks.dart
#	test/exercises/contribute_exercise_test.mocks.dart
#	test/gallery/gallery_form_test.mocks.dart
#	test/gallery/gallery_screen_test.mocks.dart
#	test/measurements/measurement_categories_screen_test.mocks.dart
#	test/measurements/measurement_provider_test.mocks.dart
#	test/nutrition/nutritional_meal_form_test.mocks.dart
#	test/nutrition/nutritional_plan_form_test.mocks.dart
#	test/nutrition/nutritional_plan_screen_test.mocks.dart
#	test/nutrition/nutritional_plans_screen_test.mocks.dart
#	test/other/base_provider_test.mocks.dart
#	test/user/provider_test.mocks.dart
#	test/weight/weight_provider_test.mocks.dart
#	test/weight/weight_screen_test.mocks.dart
#	test/workout/gym_mode_screen_test.mocks.dart
#	test/workout/repetition_unit_form_widget_test.mocks.dart
#	test/workout/routine_screen_test.mocks.dart
#	test/workout/routines_provider_test.mocks.dart
#	test/workout/routines_screen_test.mocks.dart
#	test/workout/weight_unit_form_widget_test.mocks.dart
#	test/workout/workout_day_form_test.mocks.dart
#	test/workout/workout_form_test.mocks.dart
#	test/workout/workout_set_form_test.mocks.dart
This commit is contained in:
Roland Geider
2025-01-24 15:05:28 +01:00
20 changed files with 754 additions and 273 deletions

View File

@@ -32,6 +32,7 @@
- sizzlesloth - <https://github.com/sizzlesloth>
- Arya Singh - <https://github.com/ARYPROGRAMMER>
- Xianglin Zeng - <https://github.com/FutureYL3>
- Sangharsh Sulke - <https://github.com/Sangharshdeveloper>
## Translators

View File

@@ -1,5 +1,9 @@
source "https://rubygems.org"
gem "fastlane"
gem 'logger'
gem 'mutex_m'
gem 'abbrev'
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)

View File

@@ -5,25 +5,26 @@ GEM
base64
nkf
rexml
abbrev (0.1.2)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
aws-partitions (1.1003.0)
aws-sdk-core (3.212.0)
aws-partitions (1.1042.0)
aws-sdk-core (3.216.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.95.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-kms (1.97.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.170.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-s3 (1.178.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.10.1)
aws-sigv4 (1.11.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
@@ -58,8 +59,8 @@ GEM
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-multipart (1.1.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
@@ -67,8 +68,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.3.1)
fastlane (2.225.0)
fastimage (2.4.0)
fastlane (2.226.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -108,7 +109,7 @@ GEM
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty (~> 0.4.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-versioning (0.7.0)
fastlane-sirp (1.0.0)
@@ -151,23 +152,25 @@ GEM
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.7)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.2)
json (2.8.1)
jwt (2.9.3)
json (2.9.1)
jwt (2.10.1)
base64
logger (1.6.5)
mini_magick (4.13.2)
mini_mime (1.1.5)
multi_json (1.15.0)
multipart-post (2.4.1)
mutex_m (0.3.0)
nanaimo (0.4.0)
naturally (2.2.1)
nkf (0.2.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.1)
plist (3.7.2)
public_suffix (6.0.1)
rake (13.2.1)
representable (3.2.0)
@@ -175,10 +178,10 @@ GEM
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.3.9)
rouge (2.0.7)
rexml (3.4.0)
rouge (3.28.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
rubyzip (2.4.1)
security (0.1.5)
signet (0.19.0)
addressable (~> 2.8)
@@ -207,8 +210,8 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty (0.4.0)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
@@ -216,11 +219,15 @@ PLATFORMS
arm64-darwin-21
arm64-darwin-22
arm64-darwin-23
arm64-darwin-24
x86_64-linux
DEPENDENCIES
abbrev
fastlane
fastlane-plugin-versioning
logger
mutex_m
BUNDLED WITH
2.2.32
2.6.3

View File

@@ -0,0 +1 @@

View File

@@ -8,32 +8,26 @@
fitness goals.
</p>
<p>Workout Management</p>
<ul>
<li>
Workout Management
<ul>
<li>Create and customize workout routines tailored to your fitness level and
goals
</li>
<li>Track your progress with detailed exercise logs</li>
<li>Access a vast library of exercises</li>
</ul>
</li>
<li>
Nutrition Tracking
<ul>
<li>Log your food intake using the Open Food Facts database</li>
<li>Calculate your calorie intake and macronutrient breakdown</li>
<li>Set personalized dietary goals and track your progress</li>
</ul>
</li>
<li>
Body Measurement Tracking
<ul>
<li>Monitor your body weight, body fat percentage, and other measurements</li>
<li>Visualize your progress with charts and graphs</li>
</ul>
<li>Create and customize workout routines tailored to your fitness level and
goals
</li>
<li>Track your progress with detailed exercise logs</li>
<li>Access a vast library of exercises</li>
</ul>
<p>Nutrition Tracking</p>
<ul>
<li>Log your food intake using the Open Food Facts database</li>
<li>Calculate your calorie intake and macronutrient breakdown</li>
<li>Set personalized dietary goals and track your progress</li>
</ul>
<p>Body Measurement Tracking</p>
<ul>
<li>Monitor your body weight, body fat percentage, and other measurements</li>
<li>Visualize your progress with charts and graphs</li>
</ul>
</description>

View File

@@ -62,6 +62,7 @@ const PREFS_LAST_UPDATED_LANGUAGES = 'lastUpdatedLanguages';
const PREFS_INGREDIENTS = 'ingredientData';
const PREFS_WORKOUT_UNITS = 'workoutUnits';
const PREFS_USER = 'userData';
const PREFS_USER_DARK_THEME = 'userDarkMode';
const PREFS_LAST_SERVER = 'lastServer';
const DEFAULT_ANIMATION_DURATION = Duration(milliseconds: 200);

View File

@@ -925,5 +925,9 @@
"indicatorAvg": "avg",
"@indicatorAvg": {
"description": "added for localization of Class Indicator's field text"
}
},
"themeMode": "Theme mode",
"darkMode": "Always dark mode",
"lightMode": "Always light mode",
"systemMode": "System settings"
}

1
lib/l10n/app_nl.arb Normal file
View File

@@ -0,0 +1 @@
{}

View File

@@ -161,7 +161,7 @@
"@planned": {
"description": "Header for the column of 'planned' nutritional values, i.e. what should be eaten"
},
"weekAverage": "média dos 7 dias",
"weekAverage": "Média dos 7 dias",
"@weekAverage": {
"description": "Header for the column of '7 day average' nutritional values, i.e. what was logged last week"
},
@@ -775,5 +775,90 @@
"aboutDonateTitle": "Doar",
"@aboutDonateTitle": {},
"aboutDonateText": "Nos compre um café para ajudar o projeto, pagar os custos do servidor e nos manter energizados",
"@aboutDonateText": {}
"@aboutDonateText": {},
"onlyLogging": "Só controlar calorias",
"@onlyLogging": {},
"goalEnergy": "Meta energética",
"@goalEnergy": {},
"goalProtein": "Meta proteica",
"@goalProtein": {},
"goalCarbohydrates": "Meta de carboidratos",
"@goalCarbohydrates": {},
"goalFat": "Meta de gordura",
"@goalFat": {},
"today": "Hoje",
"@today": {},
"kcalValue": "{value} kcal",
"@kcalValue": {
"description": "A value in kcal, e.g. 500 kcal",
"type": "text",
"placeholders": {
"value": {}
}
},
"gValue": "{value} g",
"@gValue": {
"description": "A value in grams, e.g. 5 g",
"type": "text",
"placeholders": {
"value": {}
}
},
"percentValue": "{value} %",
"@percentValue": {
"description": "A value in percent, e.g. 10 %",
"type": "text",
"placeholders": {
"value": {}
}
},
"noIngredientsDefined": "Sem ingredientes definido ainda",
"@noIngredientsDefined": {},
"ingredientLogged": "Ingrediente ativo registrado diariamente",
"@ingredientLogged": {},
"onlyLoggingHelpText": "Marque a caixa se você desejar apenas o seu registo de calorias e não quer a configuração de um detalhado plano nutricional com refeições",
"@onlyLoggingHelpText": {},
"goalMacro": "Macro objetivos",
"@goalMacro": {
"description": "The goal for macronutrients"
},
"selectMealToLog": "Selecione uma refeição para entrar no diário",
"@selectMealToLog": {},
"goalFiber": "Meta de fibra",
"@goalFiber": {},
"deficit": "Défice",
"@deficit": {
"description": "Caloric deficit (either planned or unplanned)"
},
"surplus": "Excedente",
"@surplus": {
"description": "Caloric surplus (either planned or unplanned)"
},
"chart30DaysTitle": "{name} Ultimos 30 dias",
"@chart30DaysTitle": {
"description": "last 30 days chart of 'name' (e.g. 'weight', 'body fat' etc.)",
"type": "text",
"placeholders": {
"name": {}
}
},
"chartDuringPlanTitle": "{chartName} Durante o plano nutricional {planName}",
"@chartDuringPlanTitle": {
"description": "chart of 'chartName' (e.g. 'weight', 'body fat' etc.) logged during plan",
"type": "text",
"placeholders": {
"chartName": {},
"planName": {}
}
},
"loggedToday": "Registrado hoje",
"@loggedToday": {},
"chartAllTimeTitle": "{name} de todo o tempo",
"@chartAllTimeTitle": {
"description": "All-time chart of 'name' (e.g. 'weight', 'body fat' etc.)",
"type": "text",
"placeholders": {
"name": {}
}
}
}

View File

@@ -99,7 +99,7 @@
"@comment": {
"description": "Comment, additional information"
},
"logIngredient": "Salvar no diário nutricional",
"logIngredient": "Salvar ingrediente no diário nutricional",
"@logIngredient": {},
"equipment": "Equipamento",
"@equipment": {
@@ -221,7 +221,7 @@
"@enterValue": {
"description": "Error message when the user hasn't entered a value on a required field"
},
"logMeal": "Registrar esta refeição",
"logMeal": "Registrar refeição para diário de nutrição",
"@logMeal": {},
"newEntry": "Nova entrada",
"@newEntry": {
@@ -677,5 +677,274 @@
"ingredientLogged": "Ingrediente registrado no diário",
"@ingredientLogged": {},
"noIngredientsDefined": "Nenhum ingrediente definido ainda",
"@noIngredientsDefined": {}
"@noIngredientsDefined": {},
"bench": "Banco",
"@bench": {
"description": "Generated entry for translation for server strings"
},
"goalFiber": "Meta de fibras",
"@goalFiber": {},
"overallChangeWeight": "Mudança geral",
"@overallChangeWeight": {
"description": "Overall change in weight, added for localization"
},
"goalTypeMeals": "De refeições",
"@goalTypeMeals": {
"description": "added for localization of Class GoalType's filed meals"
},
"goalTypeBasic": "Básico",
"@goalTypeBasic": {
"description": "added for localization of Class GoalType's filed basic"
},
"goalTypeAdvanced": "Avançado",
"@goalTypeAdvanced": {
"description": "added for localization of Class GoalType's filed advanced"
},
"indicatorRaw": "bruto",
"@indicatorRaw": {
"description": "added for localization of Class Indicator's field text"
},
"indicatorAvg": "média",
"@indicatorAvg": {
"description": "added for localization of Class Indicator's field text"
},
"seconds": "Segundos",
"@seconds": {
"description": "Generated entry for translation for server strings"
},
"textPromptTitle": "Pronto para iniciar?",
"@textPromptTitle": {},
"textPromptSubheading": "Aperte o botão de ação para começar",
"@textPromptSubheading": {},
"chest": "Peito",
"@chest": {
"description": "Generated entry for translation for server strings"
},
"max_reps": "Repetições Máximas",
"@max_reps": {
"description": "Generated entry for translation for server strings"
},
"pull_up_bar": "Barra de Pull-up",
"@pull_up_bar": {
"description": "Generated entry for translation for server strings"
},
"quads": "Quadríceps",
"@quads": {
"description": "Generated entry for translation for server strings"
},
"goalFat": "Meta de gordura",
"@goalFat": {},
"body_weight": "Peso Corporal",
"@body_weight": {
"description": "Generated entry for translation for server strings"
},
"kilometers": "Quilômetros",
"@kilometers": {
"description": "Generated entry for translation for server strings"
},
"kilometers_per_hour": "Quilômetros por hora",
"@kilometers_per_hour": {
"description": "Generated entry for translation for server strings"
},
"miles_per_hour": "Milhas por hora",
"@miles_per_hour": {
"description": "Generated entry for translation for server strings"
},
"minutes": "Minutos",
"@minutes": {
"description": "Generated entry for translation for server strings"
},
"sz_bar": "Barra Curvada",
"@sz_bar": {
"description": "Generated entry for translation for server strings"
},
"goalProtein": "Meta de proteína",
"@goalProtein": {},
"goalEnergy": "Meta energética",
"@goalEnergy": {},
"today": "Hoje",
"@today": {},
"arms": "Braços",
"@arms": {
"description": "Generated entry for translation for server strings"
},
"calves": "Panturrilha",
"@calves": {
"description": "Generated entry for translation for server strings"
},
"triceps": "Tríceps",
"@triceps": {
"description": "Generated entry for translation for server strings"
},
"goalCarbohydrates": "Meta de carbohidratos",
"@goalCarbohydrates": {},
"onlyLoggingHelpText": "Marque a caixa se você apenas quiser registrar suas calorias e não quer configurar um plano nutricional detalhado com refeições específicas",
"@onlyLoggingHelpText": {},
"kcalValue": "{value} kcal",
"@kcalValue": {
"description": "A value in kcal, e.g. 500 kcal",
"type": "text",
"placeholders": {
"value": {}
}
},
"percentValue": "{value} %",
"@percentValue": {
"description": "A value in percent, e.g. 10 %",
"type": "text",
"placeholders": {
"value": {}
}
},
"goalMacro": "Macro Objetivos",
"@goalMacro": {
"description": "The goal for macronutrients"
},
"selectMealToLog": "Selecione uma refeição para registrar no diário",
"@selectMealToLog": {},
"loggedToday": "Registrado hoje",
"@loggedToday": {},
"surplus": "excedente",
"@surplus": {
"description": "Caloric surplus (either planned or unplanned)"
},
"deficit": "déficit",
"@deficit": {
"description": "Caloric deficit (either planned or unplanned)"
},
"gValue": "{value} g",
"@gValue": {
"description": "A value in grams, e.g. 5 g",
"type": "text",
"placeholders": {
"value": {}
}
},
"abs": "Abdômen",
"@abs": {
"description": "Generated entry for translation for server strings"
},
"back": "Costas",
"@back": {
"description": "Generated entry for translation for server strings"
},
"barbell": "Barra",
"@barbell": {
"description": "Generated entry for translation for server strings"
},
"cardio": "Cardio",
"@cardio": {
"description": "Generated entry for translation for server strings"
},
"dumbbell": "Haltere",
"@dumbbell": {
"description": "Generated entry for translation for server strings"
},
"glutes": "Glúteos",
"@glutes": {
"description": "Generated entry for translation for server strings"
},
"gym_mat": "Tapete",
"@gym_mat": {
"description": "Generated entry for translation for server strings"
},
"hamstrings": "Tendões",
"@hamstrings": {
"description": "Generated entry for translation for server strings"
},
"kettlebell": "Kettlebell",
"@kettlebell": {
"description": "Generated entry for translation for server strings"
},
"lats": "Dorsal",
"@lats": {
"description": "Generated entry for translation for server strings"
},
"legs": "Pernas",
"@legs": {
"description": "Generated entry for translation for server strings"
},
"lower_back": "Lombar",
"@lower_back": {
"description": "Generated entry for translation for server strings"
},
"plates": "Anilhas",
"@plates": {
"description": "Generated entry for translation for server strings"
},
"repetitions": "Repetições",
"@repetitions": {
"description": "Generated entry for translation for server strings"
},
"shoulders": "Ombros",
"@shoulders": {
"description": "Generated entry for translation for server strings"
},
"until_failure": "Até a falha",
"@until_failure": {
"description": "Generated entry for translation for server strings"
},
"kg": "kg",
"@kg": {
"description": "Generated entry for translation for server strings"
},
"none__bodyweight_exercise_": "nenhum (exercício com peso corporal)",
"@none__bodyweight_exercise_": {
"description": "Generated entry for translation for server strings"
},
"lb": "lb",
"@lb": {
"description": "Generated entry for translation for server strings"
},
"chartAllTimeTitle": "{name} total",
"@chartAllTimeTitle": {
"description": "All-time chart of 'name' (e.g. 'weight', 'body fat' etc.)",
"type": "text",
"placeholders": {
"name": {}
}
},
"chart30DaysTitle": "{name} últimos 30 dias",
"@chart30DaysTitle": {
"description": "last 30 days chart of 'name' (e.g. 'weight', 'body fat' etc.)",
"type": "text",
"placeholders": {
"name": {}
}
},
"chartDuringPlanTitle": "{chartName} durante plano nutricional {planName}",
"@chartDuringPlanTitle": {
"description": "chart of 'chartName' (e.g. 'weight', 'body fat' etc.) logged during plan",
"type": "text",
"placeholders": {
"chartName": {},
"planName": {}
}
},
"settingsIngredientCacheDescription": "Cache de Ingredientes",
"@settingsIngredientCacheDescription": {},
"cacheWarning": "Devido ao cache pode demorar algum tempo até que mudanças estejam visíveis na aplicação.",
"@cacheWarning": {},
"swiss_ball": "Bola Suíça",
"@swiss_ball": {
"description": "Generated entry for translation for server strings"
},
"log": "Registro",
"@log": {
"description": "Log a specific meal (imperative form)"
},
"done": "Feito",
"@done": {},
"incline_bench": "Banco inclinado",
"@incline_bench": {
"description": "Generated entry for translation for server strings"
},
"miles": "Milhas",
"@miles": {
"description": "Generated entry for translation for server strings"
},
"biceps": "Bíceps",
"@biceps": {
"description": "Generated entry for translation for server strings"
}
}

View File

@@ -137,48 +137,50 @@ class MyApp extends StatelessWidget {
),
],
child: Consumer<AuthProvider>(
builder: (ctx, auth, _) => MaterialApp(
title: 'wger',
theme: wgerLightTheme,
darkTheme: wgerDarkTheme,
highContrastTheme: wgerLightThemeHc,
highContrastDarkTheme: wgerDarkThemeHc,
themeMode: ThemeMode.system,
home: auth.isAuth
? const HomeTabsScreen()
: FutureBuilder(
future: auth.tryAutoLogin(),
builder: (ctx, authResultSnapshot) =>
authResultSnapshot.connectionState == ConnectionState.waiting
? const SplashScreen()
: const AuthScreen(),
),
routes: {
DashboardScreen.routeName: (ctx) => const DashboardScreen(),
FormScreen.routeName: (ctx) => const FormScreen(),
GalleryScreen.routeName: (ctx) => const GalleryScreen(),
GymModeScreen.routeName: (ctx) => const GymModeScreen(),
HomeTabsScreen.routeName: (ctx) => const HomeTabsScreen(),
MeasurementCategoriesScreen.routeName: (ctx) => const MeasurementCategoriesScreen(),
MeasurementEntriesScreen.routeName: (ctx) => const MeasurementEntriesScreen(),
NutritionalPlansScreen.routeName: (ctx) => const NutritionalPlansScreen(),
NutritionalDiaryScreen.routeName: (ctx) => const NutritionalDiaryScreen(),
NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(),
LogMealsScreen.routeName: (ctx) => const LogMealsScreen(),
LogMealScreen.routeName: (ctx) => const LogMealScreen(),
WeightScreen.routeName: (ctx) => const WeightScreen(),
RoutineScreen.routeName: (ctx) => const RoutineScreen(),
RoutineEditScreen.routeName: (ctx) => const RoutineEditScreen(),
WorkoutLogsScreen.routeName: (ctx) => const WorkoutLogsScreen(),
RoutineListScreen.routeName: (ctx) => const RoutineListScreen(),
ExercisesScreen.routeName: (ctx) => const ExercisesScreen(),
ExerciseDetailScreen.routeName: (ctx) => const ExerciseDetailScreen(),
AddExerciseScreen.routeName: (ctx) => const AddExerciseScreen(),
AboutPage.routeName: (ctx) => const AboutPage(),
SettingsPage.routeName: (ctx) => const SettingsPage(),
},
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
builder: (ctx, auth, _) => Consumer<UserProvider>(
builder: (ctx, user, _) => MaterialApp(
title: 'wger',
theme: wgerLightTheme,
darkTheme: wgerDarkTheme,
highContrastTheme: wgerLightThemeHc,
highContrastDarkTheme: wgerDarkThemeHc,
themeMode: user.themeMode,
home: auth.isAuth
? const HomeTabsScreen()
: FutureBuilder(
future: auth.tryAutoLogin(),
builder: (ctx, authResultSnapshot) =>
authResultSnapshot.connectionState == ConnectionState.waiting
? const SplashScreen()
: const AuthScreen(),
),
routes: {
DashboardScreen.routeName: (ctx) => const DashboardScreen(),
FormScreen.routeName: (ctx) => const FormScreen(),
GalleryScreen.routeName: (ctx) => const GalleryScreen(),
GymModeScreen.routeName: (ctx) => const GymModeScreen(),
HomeTabsScreen.routeName: (ctx) => const HomeTabsScreen(),
MeasurementCategoriesScreen.routeName: (ctx) => const MeasurementCategoriesScreen(),
MeasurementEntriesScreen.routeName: (ctx) => const MeasurementEntriesScreen(),
NutritionalPlansScreen.routeName: (ctx) => const NutritionalPlansScreen(),
NutritionalDiaryScreen.routeName: (ctx) => const NutritionalDiaryScreen(),
NutritionalPlanScreen.routeName: (ctx) => const NutritionalPlanScreen(),
LogMealsScreen.routeName: (ctx) => const LogMealsScreen(),
LogMealScreen.routeName: (ctx) => const LogMealScreen(),
WeightScreen.routeName: (ctx) => const WeightScreen(),
RoutineScreen.routeName: (ctx) => const RoutineScreen(),
RoutineEditScreen.routeName: (ctx) => const RoutineEditScreen(),
WorkoutLogsScreen.routeName: (ctx) => const WorkoutLogsScreen(),
RoutineListScreen.routeName: (ctx) => const RoutineListScreen(),
ExercisesScreen.routeName: (ctx) => const ExercisesScreen(),
ExerciseDetailScreen.routeName: (ctx) => const ExerciseDetailScreen(),
AddExerciseScreen.routeName: (ctx) => const AddExerciseScreen(),
AboutPage.routeName: (ctx) => const AboutPage(),
SettingsPage.routeName: (ctx) => const SettingsPage(),
},
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
),
),
),
);

View File

@@ -15,6 +15,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:equatable/equatable.dart';
@@ -161,7 +162,15 @@ class Exercise extends Equatable {
equipment = baseData.equipment;
category = baseData.category;
translations = baseData.translations.map((e) {
e.language = languages.firstWhere((l) => l.id == e.languageId);
e.language = languages.firstWhere(
(l) => l.id == e.languageId,
// workaround for https://github.com/wger-project/flutter/issues/722
orElse: () {
log('Could not find language for translation ${e.languageId}');
return Language(id: e.languageId, shortName: 'unknown', fullName: 'unknown');
},
);
return e;
}).toList();
videos = baseData.videos;

View File

@@ -22,6 +22,7 @@ import 'dart:developer';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';

View File

@@ -298,20 +298,16 @@ class ExercisesProvider with ChangeNotifier {
Exercise exercise;
// TODO: this should be a .getSingleOrNull()!!! However, for some reason there
// are duplicates in the db. Perhaps a race condition so that two
// can be duplicates in the db. Perhaps a race condition so that two
// entries are written at the same time or something?
final exerciseResult =
await (database.select(database.exercises)..where((e) => e.id.equals(exerciseId))).get();
ExerciseTable? exerciseDb;
if (exerciseResult.length > 0) {
if (exerciseResult.isNotEmpty) {
exerciseDb = exerciseResult.first;
} else {
exerciseDb = null;
}
log(exerciseResult.toString());
// Exercise is already known locally
if (exerciseDb != null) {
final nextFetch = exerciseDb.lastFetched.add(const Duration(hours: EXERCISE_FETCH_HOURS));

View File

@@ -19,12 +19,20 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/models/user/profile.dart';
import 'package:wger/providers/base_provider.dart';
class UserProvider with ChangeNotifier {
ThemeMode themeMode = ThemeMode.system;
final WgerBaseProvider baseProvider;
UserProvider(this.baseProvider);
late SharedPreferencesAsync prefs;
UserProvider(this.baseProvider, {SharedPreferencesAsync? prefs}) {
this.prefs = prefs ?? SharedPreferencesAsync();
_loadThemeMode();
}
static const PROFILE_URL = 'userprofile';
static const VERIFY_EMAIL = 'verify-email';
@@ -36,6 +44,37 @@ class UserProvider with ChangeNotifier {
profile = null;
}
// Load theme mode from SharedPreferences
Future<void> _loadThemeMode() async {
final prefsDarkMode = await prefs.getBool(PREFS_USER_DARK_THEME);
print(prefsDarkMode);
if (prefsDarkMode == null) {
themeMode = ThemeMode.system;
} else {
themeMode = prefsDarkMode ? ThemeMode.dark : ThemeMode.light;
}
print(themeMode);
notifyListeners();
}
// Change mode on switch button click
void setThemeMode(ThemeMode mode) async {
themeMode = mode;
// Save to SharedPreferences
if (themeMode == ThemeMode.system) {
await prefs.remove(PREFS_USER_DARK_THEME);
} else {
await prefs.setBool(PREFS_USER_DARK_THEME, themeMode == ThemeMode.dark);
}
notifyListeners();
}
/// Fetch the current user's profile
Future<void> fetchAndSetProfile() async {
final userData = await baseProvider.fetch(baseProvider.makeUrl(PROFILE_URL));

View File

@@ -22,6 +22,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:wger/providers/exercises.dart';
import 'package:wger/providers/nutrition.dart';
import 'package:wger/providers/user.dart';
class SettingsPage extends StatelessWidget {
static String routeName = '/SettingsPage';
@@ -33,11 +34,11 @@ class SettingsPage extends StatelessWidget {
final i18n = AppLocalizations.of(context);
final exerciseProvider = Provider.of<ExercisesProvider>(context, listen: false);
final nutritionProvider = Provider.of<NutritionPlansProvider>(context, listen: false);
final userProvider = Provider.of<UserProvider>(context);
final i18n = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).settingsTitle),
),
appBar: AppBar(title: Text(i18n.settingsTitle)),
body: ListView(
children: [
ListTile(
@@ -64,7 +65,7 @@ class SettingsPage extends StatelessWidget {
),
),
ListTile(
title: Text(AppLocalizations.of(context).settingsIngredientCacheDescription),
title: Text(i18n.settingsIngredientCacheDescription),
trailing: IconButton(
key: const ValueKey('cacheIconIngredients'),
icon: const Icon(Icons.delete),
@@ -73,7 +74,7 @@ class SettingsPage extends StatelessWidget {
if (context.mounted) {
final snackBar = SnackBar(
content: Text(AppLocalizations.of(context).settingsCacheDeletedSnackbar),
content: Text(i18n.settingsCacheDeletedSnackbar),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
@@ -81,6 +82,35 @@ class SettingsPage extends StatelessWidget {
},
),
),
ListTile(
title: Text(i18n.themeMode),
trailing: DropdownButton<ThemeMode>(
key: const ValueKey('themeModeDropdown'),
value: userProvider.themeMode,
onChanged: (ThemeMode? newValue) {
if (newValue != null) {
userProvider.setThemeMode(newValue);
}
},
items: ThemeMode.values.map<DropdownMenuItem<ThemeMode>>((ThemeMode value) {
final label = (() {
switch (value) {
case ThemeMode.system:
return i18n.systemMode;
case ThemeMode.light:
return i18n.lightMode;
case ThemeMode.dark:
return i18n.darkMode;
}
})();
return DropdownMenuItem<ThemeMode>(
value: value,
child: Text(label),
);
}).toList(),
),
),
],
),
);

View File

@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
sha256: "03f6da266a27a4538a69295ec142cb5717d7d4e5727b84658b63e1e1509bac9c"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
version: "79.0.0"
_macros:
dependency: transitive
description: dart
@@ -18,18 +18,18 @@ packages:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
sha256: c9040fc56483c22a5e04a9f6a251313118b1a3c42423770623128fa484115643
url: "https://pub.dev"
source: hosted
version: "6.11.0"
version: "7.2.0"
archive:
dependency: transitive
description:
name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a"
url: "https://pub.dev"
source: hosted
version: "3.6.1"
version: "4.0.2"
args:
dependency: transitive
description:
@@ -58,34 +58,34 @@ packages:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.0.3"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "2.4.3"
build_runner:
dependency: "direct dev"
description:
@@ -114,42 +114,42 @@ packages:
dependency: transitive
description:
name: built_value
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2"
url: "https://pub.dev"
source: hosted
version: "8.9.2"
version: "8.9.3"
camera:
dependency: transitive
description:
name: camera
sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167"
sha256: "413d2b34fe28496c35c69ede5b232fb9dd5ca2c3a4cb606b14efc1c7546cc8cb"
url: "https://pub.dev"
source: hosted
version: "0.11.0+2"
version: "0.11.1"
camera_android_camerax:
dependency: transitive
description:
name: camera_android_camerax
sha256: e3627fdc2132d89212b8a8676679f5b07008c7e3d8ae00cea775c3397f9e742b
sha256: "7cc6adf1868bdcf4e63a56b24b41692dfbad2bec1cdceea451c77798f6a605c3"
url: "https://pub.dev"
source: hosted
version: "0.6.10"
version: "0.6.13"
camera_avfoundation:
dependency: transitive
description:
name: camera_avfoundation
sha256: "2e4c568f70e406ccb87376bc06b53d2f5bebaab71e2fbcc1a950e31449381bcf"
sha256: "3f81ee3e88a79b0b010f0233d42625926299551b05d5dc995267a0b35bc33247"
url: "https://pub.dev"
source: hosted
version: "0.9.17+5"
version: "0.9.18"
camera_platform_interface:
dependency: transitive
description:
name: camera_platform_interface
sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061
sha256: "953e7baed3a7c8fae92f7200afeb2be503ff1a17c3b4e4ed7b76f008c2810a31"
url: "https://pub.dev"
source: hosted
version: "2.8.0"
version: "2.9.0"
camera_web:
dependency: transitive
description:
@@ -170,10 +170,10 @@ packages:
dependency: transitive
description:
name: change
sha256: "65db7f966dc7e786687f49900a94c5f08b0eb9ca8c4a3e7eed3a55e980b455e2"
sha256: "3bda1ce98526b21927cdea05688563c7065fd7e66d1abbe176c354fef3b2057b"
url: "https://pub.dev"
source: hosted
version: "0.7.4"
version: "0.7.5"
characters:
dependency: transitive
description:
@@ -186,10 +186,10 @@ packages:
dependency: transitive
description:
name: charcode
sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
@@ -282,26 +282,26 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
version: "3.0.1"
drift:
dependency: "direct main"
description:
name: drift
sha256: af3941e4d544727b2eb80590eb64e9cb8d77cd68c7690265502ea6a2427aa621
sha256: "76f23535e19a9f2be92f954e74d8802e96f526e5195d7408c1a20f6659043941"
url: "https://pub.dev"
source: hosted
version: "2.23.1"
version: "2.24.0"
drift_dev:
dependency: "direct dev"
description:
name: drift_dev
sha256: fa98fdbb7303a1b5b2dc110cb516eda2253a5d291680f8cbc72b1af24099f7f9
sha256: d1d90b0d55b22de412b77186f3bf3179a4b7e2acc4c8fb3a7aaf28a01abc194b
url: "https://pub.dev"
source: hosted
version: "2.23.1"
version: "2.24.0"
equatable:
dependency: "direct main"
description:
@@ -338,10 +338,10 @@ packages:
dependency: transitive
description:
name: file_selector_linux
sha256: b2b91daf8a68ecfa4a01b778a6f52edef9b14ecd506e771488ea0f2e0784198b
sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
url: "https://pub.dev"
source: hosted
version: "0.9.3+1"
version: "0.9.3+2"
file_selector_macos:
dependency: transitive
description:
@@ -386,10 +386,10 @@ packages:
dependency: "direct main"
description:
name: flex_color_scheme
sha256: "90f4fe67b9561ae8a4af117df65a8ce9988624025667c54e6d304e65cff77d52"
sha256: "09bea5d776f694c5a67f2229f2aa500cc7cce369322dc6500ab01cf9ad1b4e1a"
url: "https://pub.dev"
source: hosted
version: "8.0.2"
version: "8.1.0"
flex_seed_scheme:
dependency: "direct main"
description:
@@ -497,18 +497,10 @@ packages:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e"
url: "https://pub.dev"
source: hosted
version: "2.0.23"
flutter_riverpod:
dependency: "direct main"
description:
name: flutter_riverpod
sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
url: "https://pub.dev"
source: hosted
version: "2.6.1"
version: "2.0.24"
flutter_staggered_grid_view:
dependency: "direct main"
description:
@@ -521,10 +513,10 @@ packages:
dependency: "direct main"
description:
name: flutter_svg
sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123"
sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b
url: "https://pub.dev"
source: hosted
version: "2.0.16"
version: "2.0.17"
flutter_svg_icons:
dependency: "direct main"
description:
@@ -571,10 +563,10 @@ packages:
dependency: "direct dev"
description:
name: freezed
sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e"
sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c"
url: "https://pub.dev"
source: hosted
version: "2.5.7"
version: "2.5.8"
freezed_annotation:
dependency: "direct main"
description:
@@ -608,10 +600,10 @@ packages:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.3"
golden_toolkit:
dependency: "direct dev"
description:
@@ -640,34 +632,34 @@ packages:
dependency: "direct main"
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.dev"
source: hosted
version: "1.2.2"
version: "1.3.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.1"
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
sha256: "8346ad4b5173924b5ddddab782fc7d8a6300178c8b1dc427775405a01701c4a6"
url: "https://pub.dev"
source: hosted
version: "4.3.0"
version: "4.5.2"
image_picker:
dependency: "direct main"
description:
@@ -680,10 +672,10 @@ packages:
dependency: transitive
description:
name: image_picker_android
sha256: "8faba09ba361d4b246dc0a17cb4289b3324c2b9f6db7b3d457ee69106a86bd32"
sha256: b62d34a506e12bb965e824b6db4fbf709ee4589cf5d3e99b45ab2287b008ee0c
url: "https://pub.dev"
source: hosted
version: "0.8.12+17"
version: "0.8.12+20"
image_picker_for_web:
dependency: transitive
description:
@@ -696,10 +688,10 @@ packages:
dependency: transitive
description:
name: image_picker_ios
sha256: "4f0568120c6fcc0aaa04511cb9f9f4d29fc3d0139884b1d06be88dcec7641d6b"
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
url: "https://pub.dev"
source: hosted
version: "0.8.12+1"
version: "0.8.12+2"
image_picker_linux:
dependency: transitive
description:
@@ -712,18 +704,18 @@ packages:
dependency: transitive
description:
name: image_picker_macos
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
url: "https://pub.dev"
source: hosted
version: "0.2.1+1"
version: "0.2.1+2"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
version: "2.10.1"
image_picker_windows:
dependency: transitive
description:
@@ -749,10 +741,10 @@ packages:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.4"
version: "1.0.5"
js:
dependency: transitive
description:
@@ -773,10 +765,10 @@ packages:
dependency: "direct dev"
description:
name: json_serializable
sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c
sha256: b0a98230538fe5d0b60a22fb6bf1b6cb03471b53e3324ff6069c591679dd59c9
url: "https://pub.dev"
source: hosted
version: "6.9.0"
version: "6.9.3"
leak_tracker:
dependency: transitive
description:
@@ -805,10 +797,10 @@ packages:
dependency: transitive
description:
name: lints
sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "5.1.1"
list_counter:
dependency: transitive
description:
@@ -837,10 +829,10 @@ packages:
dependency: transitive
description:
name: markdown
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1"
url: "https://pub.dev"
source: hosted
version: "7.2.2"
version: "7.3.0"
marker:
dependency: transitive
description:
@@ -885,10 +877,10 @@ packages:
dependency: "direct dev"
description:
name: mockito
sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917"
sha256: f99d8d072e249f719a5531735d146d8cf04c580d93920b04de75bef6dfb2daf6
url: "https://pub.dev"
source: hosted
version: "5.4.4"
version: "5.4.5"
multi_select_flutter:
dependency: "direct main"
description:
@@ -917,26 +909,26 @@ packages:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: da8d9ac8c4b1df253d1a328b7bf01ae77ef132833479ab40763334db13b91cce
sha256: "739e0a5c3c4055152520fa321d0645ee98e932718b4c8efeeb51451968fe0790"
url: "https://pub.dev"
source: hosted
version: "8.1.1"
version: "8.1.3"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66
sha256: a5ef9986efc7bf772f2696183a3992615baa76c1ffb1189318dd8803778fb05b
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.2"
path:
dependency: "direct main"
description:
@@ -965,18 +957,18 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2"
url: "https://pub.dev"
source: hosted
version: "2.2.12"
version: "2.2.15"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
@@ -1065,6 +1057,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.1"
posix:
dependency: transitive
description:
name: posix
sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
url: "https://pub.dev"
source: hosted
version: "6.0.1"
process:
dependency: transitive
description:
@@ -1085,18 +1085,18 @@ packages:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.5.0"
recase:
dependency: transitive
description:
@@ -1129,14 +1129,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.4.15"
riverpod:
dependency: transitive
description:
name: riverpod
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
url: "https://pub.dev"
source: hosted
version: "2.6.1"
shared_preferences:
dependency: "direct main"
description:
@@ -1149,18 +1141,18 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d"
sha256: "138b7bbbc7f59c56236e426c37afb8f78cbc57b094ac64c440e0bb90e380a4f5"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.2"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
version: "2.5.4"
shared_preferences_linux:
dependency: transitive
description:
@@ -1170,7 +1162,7 @@ packages:
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
dependency: "direct dev"
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
@@ -1197,18 +1189,18 @@ packages:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.1"
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.0.1"
simple_gesture_detector:
dependency: transitive
description:
@@ -1226,18 +1218,18 @@ packages:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
version: "2.0.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.4"
version: "1.3.5"
source_span:
dependency: transitive
description:
@@ -1250,26 +1242,26 @@ packages:
dependency: transitive
description:
name: sqlite3
sha256: bb174b3ec2527f9c5f680f73a89af8149dd99782fbb56ea88ad0807c5638f2ed
sha256: "35d3726fe18ab1463403a5cc8d97dbc81f2a0b08082e8173851363fcc97b6627"
url: "https://pub.dev"
source: hosted
version: "2.4.7"
version: "2.7.2"
sqlite3_flutter_libs:
dependency: "direct main"
description:
name: sqlite3_flutter_libs
sha256: "73016db8419f019e807b7a5e5fbf2a7bd45c165fed403b8e7681230f3a102785"
sha256: "50a7e3f294c741d3142eed0ff228e38498334e11e0ccb9d73e0496e005949e44"
url: "https://pub.dev"
source: hosted
version: "0.5.28"
version: "0.5.29"
sqlparser:
dependency: transitive
description:
name: sqlparser
sha256: "4cad4b2c5f63dc9ea1a8dcffb58cf762322bea5dd8836870164a65e913bdae41"
sha256: "27dd0a9f0c02e22ac0eb42a23df9ea079ce69b52bb4a3b478d64e0ef34a263ee"
url: "https://pub.dev"
source: hosted
version: "0.40.0"
version: "0.41.0"
stack_trace:
dependency: transitive
description:
@@ -1278,14 +1270,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.12.0"
state_notifier:
dependency: transitive
description:
name: state_notifier
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
url: "https://pub.dev"
source: hosted
version: "1.0.0"
stream_channel:
dependency: transitive
description:
@@ -1298,10 +1282,10 @@ packages:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@@ -1322,10 +1306,10 @@ packages:
dependency: "direct main"
description:
name: table_calendar
sha256: "4ca32b2fc919452c9974abd4c6ea611a63e33b9e4f0b8c38dba3ac1f4a6549d1"
sha256: "0c0c6219878b363a2d5f40c7afb159d845f253d061dc3c822aa0d5fe0f721982"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
version: "3.2.0"
term_glyph:
dependency: transitive
description:
@@ -1346,10 +1330,10 @@ packages:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
version: "1.0.2"
typed_data:
dependency: transitive
description:
@@ -1378,10 +1362,10 @@ packages:
dependency: transitive
description:
name: url_launcher_ios
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
url: "https://pub.dev"
source: hosted
version: "6.3.1"
version: "6.3.2"
url_launcher_linux:
dependency: transitive
description:
@@ -1394,10 +1378,10 @@ packages:
dependency: transitive
description:
name: url_launcher_macos
sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672"
sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
version: "3.2.2"
url_launcher_platform_interface:
dependency: transitive
description:
@@ -1410,42 +1394,42 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
version: "2.4.0"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4"
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
version: "3.1.4"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "773c9522d66d523e1c7b25dfb95cc91c26a1e17b107039cfe147285e92de7878"
sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7"
url: "https://pub.dev"
source: hosted
version: "1.1.14"
version: "1.1.15"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb"
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
url: "https://pub.dev"
source: hosted
version: "1.1.12"
version: "1.1.13"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: ab9ff38fc771e9ee1139320adbe3d18a60327370c218c60752068ebee4b49ab1
sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad"
url: "https://pub.dev"
source: hosted
version: "1.1.15"
version: "1.1.16"
vector_math:
dependency: transitive
description:
@@ -1482,18 +1466,18 @@ packages:
dependency: transitive
description:
name: video_player_android
sha256: "391e092ba4abe2f93b3e625bd6b6a6ec7d7414279462c1c0ee42b5ab8d0a0898"
sha256: "7018dbcb395e2bca0b9a898e73989e67c0c4a5db269528e1b036ca38bcca0d0b"
url: "https://pub.dev"
source: hosted
version: "2.7.16"
version: "2.7.17"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: cd5ab8a8bc0eab65ab0cea40304097edc46da574c8c1ecdee96f28cd8ef3792f
sha256: "8a4e73a3faf2b13512978a43cf1cdda66feeeb900a0527f1fbfd7b19cf3458d3"
url: "https://pub.dev"
source: hosted
version: "2.6.2"
version: "2.6.7"
video_player_platform_interface:
dependency: transitive
description:
@@ -1522,10 +1506,10 @@ packages:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
web:
dependency: transitive
description:
@@ -1546,10 +1530,10 @@ packages:
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.2"
webdriver:
dependency: transitive
description:
@@ -1562,10 +1546,10 @@ packages:
dependency: transitive
description:
name: win32
sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2"
sha256: "154360849a56b7b67331c21f09a386562d88903f90a1099c5987afc1912e1f29"
url: "https://pub.dev"
source: hosted
version: "5.8.0"
version: "5.10.0"
xdg_directories:
dependency: transitive
description:
@@ -1586,10 +1570,10 @@ packages:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.2"
version: "3.1.3"
sdks:
dart: ">=3.6.0 <4.0.0"
flutter: ">=3.27.0"

View File

@@ -39,13 +39,13 @@ dependencies:
drift: ^2.16.0
equatable: ^2.0.7
fl_chart: ^0.69.2
flex_color_scheme: ^8.0.2
flex_color_scheme: ^8.1.0
flex_seed_scheme: ^3.5.0
flutter_barcode_scanner: ^2.0.0
flutter_calendar_carousel: ^2.4.4
flutter_html: ^3.0.0-beta.2
flutter_staggered_grid_view: ^0.7.0
flutter_svg: ^2.0.16
flutter_svg: ^2.0.17
flutter_svg_icons: ^0.0.1
flutter_typeahead: ^5.2.0
flutter_zxing: ^1.8.2
@@ -57,13 +57,13 @@ dependencies:
intl: ^0.19.0
json_annotation: ^4.8.1
multi_select_flutter: ^4.1.3
package_info_plus: ^8.1.1
package_info_plus: ^8.1.3
path: ^1.9.0
path_provider: ^2.1.5
provider: ^6.1.2
rive: ^0.13.20
shared_preferences: ^2.3.5
sqlite3_flutter_libs: ^0.5.28
sqlite3_flutter_libs: ^0.5.29
table_calendar: ^3.0.8
url_launcher: ^6.3.1
version: ^3.0.2
@@ -87,6 +87,7 @@ dev_dependencies:
json_serializable: ^6.9.0
mockito: ^5.4.4
network_image_mock: ^2.1.1
shared_preferences_platform_interface: ^2.0.0
# Script to read out unused translations
#translations_cleaner: ^0.0.5

View File

@@ -22,22 +22,39 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/providers/base_provider.dart';
import 'package:wger/providers/exercises.dart';
import 'package:wger/providers/nutrition.dart';
import 'package:wger/providers/user.dart';
import 'package:wger/widgets/core/settings.dart';
import 'settings_test.mocks.dart';
@GenerateMocks([ExercisesProvider, NutritionPlansProvider])
@GenerateMocks([
ExercisesProvider,
NutritionPlansProvider,
UserProvider,
WgerBaseProvider,
SharedPreferencesAsync,
])
void main() {
final mockExerciseProvider = MockExercisesProvider();
final mockNutritionProvider = MockNutritionPlansProvider();
final mockSharedPreferences = MockSharedPreferencesAsync();
final mockUserProvider = MockUserProvider();
setUp(() {
when(mockUserProvider.themeMode).thenReturn(ThemeMode.system);
});
Widget createSettingsScreen({locale = 'en'}) {
return MultiProvider(
providers: [
ChangeNotifierProvider<NutritionPlansProvider>(create: (context) => mockNutritionProvider),
ChangeNotifierProvider<ExercisesProvider>(create: (context) => mockExerciseProvider),
ChangeNotifierProvider<UserProvider>(create: (context) => mockUserProvider),
],
child: MaterialApp(
locale: Locale(locale),
@@ -65,4 +82,35 @@ void main() {
verify(mockNutritionProvider.clearIngredientCache());
});
});
group('Theme settings', () {
test('Default theme is system', () async {
when(mockSharedPreferences.getBool(PREFS_USER_DARK_THEME)).thenAnswer((_) async => null);
final userProvider = await UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
expect(userProvider.themeMode, ThemeMode.system);
});
test('Loads light theme', () async {
when(mockSharedPreferences.getBool(PREFS_USER_DARK_THEME)).thenAnswer((_) async => false);
final userProvider = await UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
expect(userProvider.themeMode, ThemeMode.light);
});
test('Saves theme to prefs', () {
when(mockSharedPreferences.getBool(any)).thenAnswer((_) async => null);
final userProvider = UserProvider(MockWgerBaseProvider(), prefs: mockSharedPreferences);
userProvider.setThemeMode(ThemeMode.dark);
verify(mockSharedPreferences.setBool(PREFS_USER_DARK_THEME, true)).called(1);
expect(userProvider.themeMode, ThemeMode.dark);
});
testWidgets('Test changing the theme mode in preferences', (WidgetTester tester) async {
await tester.pumpWidget(createSettingsScreen());
await tester.tap(find.byKey(const ValueKey('themeModeDropdown')));
await tester.pumpAndSettle();
await tester.tap(find.text('Always light mode'));
verify(mockUserProvider.setThemeMode(ThemeMode.light)).called(1);
});
});
}

View File

@@ -21,6 +21,8 @@ import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart';
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
import 'package:wger/providers/base_provider.dart';
import 'package:wger/providers/user.dart';
@@ -29,6 +31,7 @@ import 'provider_test.mocks.dart';
@GenerateMocks([WgerBaseProvider])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late UserProvider userProvider;
late MockWgerBaseProvider mockWgerBaseProvider;
@@ -47,6 +50,7 @@ void main() {
);
setUp(() {
SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty();
mockWgerBaseProvider = MockWgerBaseProvider();
userProvider = UserProvider(mockWgerBaseProvider);