From 30c7c5ad8b1eab6cb69bc1284fc82d64755bf717 Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Fri, 31 Oct 2025 12:46:22 +0100 Subject: [PATCH] Move more widgets to use the new providers --- lib/providers/exercise_state_notifier.dart | 43 ++++++++++++++++--- lib/providers/exercise_state_notifier.g.dart | 20 +-------- lib/providers/exercises.dart | 19 -------- .../add_exercise/steps/step_2_variations.dart | 17 ++++---- lib/widgets/exercises/exercises.dart | 21 ++++----- lib/widgets/routines/gym_mode/gym_mode.dart | 4 +- lib/widgets/routines/log.dart | 22 ++++++---- 7 files changed, 75 insertions(+), 71 deletions(-) diff --git a/lib/providers/exercise_state_notifier.dart b/lib/providers/exercise_state_notifier.dart index 40c25b8b..6f1628b3 100644 --- a/lib/providers/exercise_state_notifier.dart +++ b/lib/providers/exercise_state_notifier.dart @@ -152,12 +152,45 @@ final class ExerciseStateNotifier extends _$ExerciseStateNotifier { }).toList(); } - Exercise? getById(int id) { - try { - return state.exercises.firstWhere((e) => e.id == id); - } catch (_) { - return null; + List get allExercises { + return state.exercises; + } + + Exercise getById(int id) { + return state.exercises.firstWhere((e) => e.id == id); + } + + Map> get exerciseByVariation { + final Map> variations = {}; + + for (final exercise in state.exercises.where((e) => e.variationId != null)) { + if (!variations.containsKey(exercise.variationId)) { + variations[exercise.variationId!] = []; + } + + variations[exercise.variationId]!.add(exercise); } + + return variations; + } + + /// Find exercises by variation IDs + /// + /// exerciseIdToExclude: the ID of the exercise to exclude from the list of + /// returned exercises. Since this is typically called by one exercise, we are + /// not interested in seeing that same exercise returned in the list of variations. + /// If this parameter is not passed, all exercises are returned. + List findExercisesByVariationId(int? variationId, {int? exerciseIdToExclude}) { + if (variationId == null) { + return []; + } + + var out = state.exercises.where((base) => base.variationId == variationId).toList(); + + if (exerciseIdToExclude != null) { + out = out.where((e) => e.id != exerciseIdToExclude).toList(); + } + return out; } Future> searchExercise( diff --git a/lib/providers/exercise_state_notifier.g.dart b/lib/providers/exercise_state_notifier.g.dart index bbdc2bc0..2b9c12fc 100644 --- a/lib/providers/exercise_state_notifier.g.dart +++ b/lib/providers/exercise_state_notifier.g.dart @@ -1,21 +1,3 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (c) 2020, wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'exercise_state_notifier.dart'; @@ -92,7 +74,7 @@ final class ExerciseStateNotifierProvider } } -String _$exerciseStateNotifierHash() => r'2afd58c1f64a23010125795263121a745d2b17c0'; +String _$exerciseStateNotifierHash() => r'317a9ce4bccae9899e30048be1586a6c21a2a006'; abstract class _$ExerciseStateNotifier extends $Notifier { ExerciseState build(); diff --git a/lib/providers/exercises.dart b/lib/providers/exercises.dart index 92d30f27..7cad4a7d 100644 --- a/lib/providers/exercises.dart +++ b/lib/providers/exercises.dart @@ -106,25 +106,6 @@ class ExercisesProvider with ChangeNotifier { ); } - /// Find exercises by variation IDs - /// - /// exerciseIdToExclude: the ID of the exercise to exclude from the list of - /// returned exercises. Since this is typically called by one exercise, we are - /// not interested in seeing that same exercise returned in the list of variations. - /// If this parameter is not passed, all exercises are returned. - List findExercisesByVariationId(int? variationId, {int? exerciseIdToExclude}) { - if (variationId == null) { - return []; - } - - var out = exercises.where((base) => base.variationId == variationId).toList(); - - if (exerciseIdToExclude != null) { - out = out.where((e) => e.id != exerciseIdToExclude).toList(); - } - return out; - } - /// Find category by ID ExerciseCategory findCategoryById(int id) { return _categories.firstWhere( diff --git a/lib/widgets/add_exercise/steps/step_2_variations.dart b/lib/widgets/add_exercise/steps/step_2_variations.dart index 4fcb639b..72ab3b72 100644 --- a/lib/widgets/add_exercise/steps/step_2_variations.dart +++ b/lib/widgets/add_exercise/steps/step_2_variations.dart @@ -1,17 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:provider/provider.dart' as provider; import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/providers/add_exercise.dart'; -import 'package:wger/providers/exercises.dart'; +import 'package:wger/providers/exercise_state_notifier.dart'; -class Step2Variations extends StatelessWidget { +class Step2Variations extends ConsumerWidget { final GlobalKey formkey; const Step2Variations({required this.formkey}); @override - Widget build(BuildContext context) { - final exerciseProvider = context.read(); + Widget build(BuildContext context, WidgetRef ref) { + final exerciseProvider = ref.read(exerciseStateProvider.notifier); return Form( key: formkey, @@ -49,7 +50,7 @@ class Step2Variations extends StatelessWidget { ], ), ), - Consumer( + provider.Consumer( builder: (ctx, provider, __) => Switch( value: provider.variationId == key, onChanged: (state) => provider.variationId = key, @@ -59,7 +60,7 @@ class Step2Variations extends StatelessWidget { ), ), // Exercise bases without variations - ...exerciseProvider.exercises + ...exerciseProvider.allExercises .where((b) => b.variationId == null) .map( (base) => Row( @@ -82,7 +83,7 @@ class Step2Variations extends StatelessWidget { ], ), ), - Consumer( + provider.Consumer( builder: (ctx, provider, __) => Switch( value: provider.variationConnectToExercise == base.id, onChanged: (state) => provider.variationConnectToExercise = base.id, diff --git a/lib/widgets/exercises/exercises.dart b/lib/widgets/exercises/exercises.dart index 373515f0..77e0b44b 100644 --- a/lib/widgets/exercises/exercises.dart +++ b/lib/widgets/exercises/exercises.dart @@ -19,8 +19,8 @@ import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:provider/provider.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/i18n.dart'; import 'package:wger/helpers/platform.dart'; @@ -29,22 +29,24 @@ import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/exercises/image.dart'; import 'package:wger/models/exercises/muscle.dart'; import 'package:wger/models/exercises/translation.dart'; -import 'package:wger/providers/exercises.dart'; +import 'package:wger/providers/exercise_state_notifier.dart'; import 'package:wger/widgets/core/core.dart'; import 'package:wger/widgets/exercises/images.dart'; import 'package:wger/widgets/exercises/list_tile.dart'; import 'package:wger/widgets/exercises/videos.dart'; -class ExerciseDetail extends StatelessWidget { +class ExerciseDetail extends ConsumerWidget { final Exercise _exercise; - late Translation _translation; + late final Translation _translation; + late final ExerciseStateNotifier _exerciseStateNotifier; static const PADDING = 9.0; final CarouselController carouselController = CarouselController(); ExerciseDetail(this._exercise); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + _exerciseStateNotifier = ref.read(exerciseStateProvider.notifier); _translation = _exercise.getTranslation(Localizations.localeOf(context).languageCode); return SingleChildScrollView( @@ -81,11 +83,10 @@ class ExerciseDetail extends StatelessWidget { } List getVariations(BuildContext context) { - final variations = Provider.of(context, listen: false) - .findExercisesByVariationId( - _exercise.variationId, - exerciseIdToExclude: _exercise.id, - ); + final variations = _exerciseStateNotifier.findExercisesByVariationId( + _exercise.variationId, + exerciseIdToExclude: _exercise.id, + ); final List out = []; if (_exercise.variationId == null) { diff --git a/lib/widgets/routines/gym_mode/gym_mode.dart b/lib/widgets/routines/gym_mode/gym_mode.dart index a1e04856..a0e8c55d 100644 --- a/lib/widgets/routines/gym_mode/gym_mode.dart +++ b/lib/widgets/routines/gym_mode/gym_mode.dart @@ -118,7 +118,7 @@ class _GymModeState extends ConsumerState { for (final slot in widget._dayDataGym.slots) { var firstPage = true; for (final config in slot.setConfigs) { - final exercise = context.read().findExerciseById(config.exerciseId); + final exercise = ref.read(exerciseStateProvider.notifier).getById(config.exerciseId); if (firstPage) { _exercisePages[exercise] = currentPage; @@ -141,7 +141,7 @@ class _GymModeState extends ConsumerState { var firstPage = true; for (final config in slotData.setConfigs) { final ratioCompleted = currentElement / _totalElements; - final exercise = exercisesAsync.getById(config.exerciseId)!; + final exercise = exercisesAsync.getById(config.exerciseId); currentElement++; if (firstPage && state.showExercisePages) { diff --git a/lib/widgets/routines/log.dart b/lib/widgets/routines/log.dart index 9613ec1d..ef698f3d 100644 --- a/lib/widgets/routines/log.dart +++ b/lib/widgets/routines/log.dart @@ -17,6 +17,7 @@ */ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:wger/helpers/colors.dart'; import 'package:wger/helpers/date.dart'; @@ -26,31 +27,34 @@ import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/log.dart'; import 'package:wger/models/workouts/routine.dart'; import 'package:wger/models/workouts/session.dart'; +import 'package:wger/providers/network_provider.dart'; import 'package:wger/widgets/measurements/charts.dart'; import 'package:wger/widgets/routines/charts.dart'; import 'package:wger/widgets/routines/forms/session.dart'; -class SessionInfo extends StatefulWidget { +class SessionInfo extends ConsumerStatefulWidget { final WorkoutSession _session; const SessionInfo(this._session); @override - State createState() => _SessionInfoState(); + ConsumerState createState() => _SessionInfoState(); } -class _SessionInfoState extends State { +class _SessionInfoState extends ConsumerState { bool editMode = false; @override Widget build(BuildContext context) { final i18n = AppLocalizations.of(context); + final isOnline = ref.watch(networkStatusProvider); return Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ ListTile( + enabled: isOnline, title: Text( i18n.workoutSession, style: Theme.of(context).textTheme.headlineSmall, @@ -160,14 +164,16 @@ class ExerciseLogChart extends StatelessWidget { } } -class DayLogWidget extends StatelessWidget { +class DayLogWidget extends ConsumerWidget { final DateTime _date; final Routine _routine; const DayLogWidget(this._date, this._routine); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final isOnline = ref.watch(networkStatusProvider); + final sessionApi = _routine.sessions.firstWhere( (sessionApi) => sessionApi.session.date.isSameDayAs(_date), ); @@ -197,9 +203,9 @@ class DayLogWidget extends StatelessWidget { IconButton( icon: const Icon(Icons.delete), key: ValueKey('delete-log-${log.id}'), - onPressed: () { - showDeleteDialog(context, translation.name, log); - }, + onPressed: isOnline + ? () => showDeleteLogDialog(context, translation.name, log) + : null, ), ], ),