From fd1ee4295c3663b7b593be28340df0c6f53aacb7 Mon Sep 17 00:00:00 2001 From: Jackpkn Date: Mon, 6 Jan 2025 21:38:33 +0530 Subject: [PATCH] fix: improve Gym Mode navigation and state persistence --- lib/main.dart | 3 +- lib/providers/gym_state.dart | 60 ++++++++++++++++++++++++++++ lib/widgets/routines/forms.dart | 2 +- lib/widgets/routines/gym_mode.dart | 63 ++++++++++++++++-------------- pubspec.yaml | 1 + 5 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 lib/providers/gym_state.dart diff --git a/lib/main.dart b/lib/main.dart index ae27030f..0f3968f1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart' as riverpod; import 'package:provider/provider.dart'; import 'package:wger/core/locator.dart'; import 'package:wger/providers/add_exercise.dart'; @@ -66,7 +67,7 @@ void main() async { // Locator to initialize exerciseDB await ServiceLocator().configure(); // Application - runApp(const MyApp()); + runApp(const riverpod.ProviderScope(child: MyApp())); } class MyApp extends StatelessWidget { diff --git a/lib/providers/gym_state.dart b/lib/providers/gym_state.dart new file mode 100644 index 00000000..47c142d9 --- /dev/null +++ b/lib/providers/gym_state.dart @@ -0,0 +1,60 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:wger/models/exercises/exercise.dart'; + +const GYM_PAGE_KEY = 'gym_current_page'; + +final StateNotifierProvider gymStateProvider = StateNotifierProvider((ref) { + return GymStateNotifier(); +}); + +class GymState { + final Map exercisePages; + final bool showExercisePages; + final int currentPage; + + GymState({ + this.exercisePages = const {}, + this.showExercisePages = true, + this.currentPage = 0, + }); + + GymState copyWith({ + Map? exercisePages, + bool? showExercisePages, + int? currentPage, + }) { + return GymState( + exercisePages: exercisePages ?? this.exercisePages, + showExercisePages: showExercisePages ?? this.showExercisePages, + currentPage: currentPage ?? this.currentPage, + ); + } +} + +class GymStateNotifier extends StateNotifier { + final _prefs = SharedPreferences.getInstance(); + + GymStateNotifier() : super(GymState()) { + _loadSavedState(); + } + + Future _loadSavedState() async { + final SharedPreferences prefs = await _prefs; + final int savedPage = prefs.getInt(GYM_PAGE_KEY) ?? 0; + state = state.copyWith(currentPage: savedPage); + } + + Future setCurrentPage(int page) async { + final SharedPreferences prefs = await _prefs; + await prefs.setInt(GYM_PAGE_KEY, page); + state = state.copyWith(currentPage: page); + } + + void toggleExercisePages() { + state = state.copyWith(showExercisePages: !state.showExercisePages); + } + void setExercisePages(Map exercisePages) { + state = state.copyWith(exercisePages: exercisePages); + } +} \ No newline at end of file diff --git a/lib/widgets/routines/forms.dart b/lib/widgets/routines/forms.dart index 3cb75de1..8729983f 100644 --- a/lib/widgets/routines/forms.dart +++ b/lib/widgets/routines/forms.dart @@ -86,7 +86,7 @@ class ExerciseSetting extends StatelessWidget { ), ], ), - Flexible(flex: 2, child: RiRInputWidget(setting)), + Flexible(flex: 2, child: RiRInputWidget( setting.exerciseId, onChanged: (String value) { }, )), const SizedBox(height: 15), ], ), diff --git a/lib/widgets/routines/gym_mode.dart b/lib/widgets/routines/gym_mode.dart index cd932c89..9e5fb867 100644 --- a/lib/widgets/routines/gym_mode.dart +++ b/lib/widgets/routines/gym_mode.dart @@ -20,8 +20,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; +import 'package:provider/provider.dart'as provider; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/gym_mode.dart'; @@ -38,6 +39,7 @@ import 'package:wger/models/workouts/set_config_data.dart'; import 'package:wger/models/workouts/slot_data.dart'; import 'package:wger/models/workouts/slot_entry.dart'; import 'package:wger/providers/exercises.dart'; +import 'package:wger/providers/gym_state.dart'; import 'package:wger/providers/routines.dart'; import 'package:wger/theme/theme.dart'; import 'package:wger/widgets/core/core.dart'; @@ -47,7 +49,7 @@ import 'package:wger/widgets/routines/forms/reps_unit.dart'; import 'package:wger/widgets/routines/forms/rir.dart'; import 'package:wger/widgets/routines/forms/weight_unit.dart'; -class GymMode extends StatefulWidget { +class GymMode extends ConsumerStatefulWidget { final DayData _dayData; late final TimeOfDay _start; @@ -56,60 +58,61 @@ class GymMode extends StatefulWidget { } @override - _GymModeState createState() => _GymModeState(); + ConsumerState createState() => _GymModeState(); } -class _GymModeState extends State { + +class _GymModeState extends ConsumerState { var _totalElements = 1; /// Map with the first (navigation) page for each exercise final Map _exercisePages = {}; - final PageController _controller = PageController(initialPage: 0); + late final PageController _controller; @override void dispose() { _controller.dispose(); super.dispose(); + } @override void initState() { super.initState(); - // Calculate amount of elements for progress indicator - + final initialPage = ref.read(gymStateProvider).currentPage; + _controller = PageController(initialPage: initialPage); + Future.microtask(() => _calculatePages()); + } + void _calculatePages() { for (final slot in widget._dayData.slots) { _totalElements += slot.setConfigs.length; } - // Calculate the pages for the navigation - // - // This duplicates the code below in the getContent method, but it seems to - // be the easiest way + var currentPage = 1; + final Map exercisePages = {}; + for (final slot in widget._dayData.slots) { var firstPage = true; for (final config in slot.setConfigs) { - final exercise = Provider.of(context, listen: false) + final exercise = provider.Provider.of(context, listen: false) .findExerciseById(config.exerciseId); if (firstPage) { - _exercisePages[exercise] = currentPage; + exercisePages[exercise] = currentPage; currentPage++; } - - // Log Page - currentPage++; - - // Timer - currentPage++; + currentPage += 2; firstPage = false; } } + + ref.read(gymStateProvider.notifier).setExercisePages(exercisePages); } - // Returns the list of exercise overview, sets and pause pages List getContent() { - final exerciseProvider = Provider.of(context, listen: false); - final workoutProvider = Provider.of(context, listen: false); + final state = ref.watch(gymStateProvider); + final exerciseProvider = provider.Provider.of(context, listen: false); + final workoutProvider = provider.Provider.of(context, listen: false); var currentElement = 1; final List out = []; @@ -120,12 +123,12 @@ class _GymModeState extends State { final exercise = exerciseProvider.findExerciseById(config.exerciseId); currentElement++; - if (firstPage) { + if (firstPage && state.showExercisePages) { out.add(ExerciseOverview( _controller, exercise, ratioCompleted, - _exercisePages, + state.exercisePages, )); } @@ -136,25 +139,25 @@ class _GymModeState extends State { exercise, workoutProvider.findById(widget._dayData.day!.routineId), ratioCompleted, - _exercisePages, + state.exercisePages, )); - out.add(TimerWidget(_controller, ratioCompleted, _exercisePages)); + out.add(TimerWidget(_controller, ratioCompleted, state.exercisePages)); firstPage = false; } } return out; } - @override Widget build(BuildContext context) { return PageView( controller: _controller, + onPageChanged: (page) => ref.read(gymStateProvider.notifier).setCurrentPage(page), children: [ StartPage(_controller, widget._dayData.day!, _exercisePages), ...getContent(), SessionPage( - Provider.of(context, listen: false) + provider.Provider.of(context, listen: false) .findById(widget._dayData.day!.routineId), _controller, widget._start, @@ -468,7 +471,7 @@ class _LogPageState extends State { // Save the entry on the server try { - await Provider.of( + await provider.Provider.of( context, listen: false, ).addLog(widget._log); @@ -890,7 +893,7 @@ class _SessionPageState extends State { // Save the entry on the server try { - await Provider.of( + await provider.Provider.of( context, listen: false, ).addSession(_session); diff --git a/pubspec.yaml b/pubspec.yaml index 46df29b8..a8a6fe24 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,7 @@ dependencies: url_launcher: ^6.3.1 version: ^3.0.2 video_player: ^2.9.2 + flutter_riverpod: ^2.6.1 dependency_overrides: intl: ^0.19.0