mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
fix: improve Gym Mode navigation and state persistence
This commit is contained in:
@@ -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 {
|
||||
|
||||
60
lib/providers/gym_state.dart
Normal file
60
lib/providers/gym_state.dart
Normal file
@@ -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<GymStateNotifier, GymState> gymStateProvider = StateNotifierProvider<GymStateNotifier, GymState>((ref) {
|
||||
return GymStateNotifier();
|
||||
});
|
||||
|
||||
class GymState {
|
||||
final Map<Exercise, int> exercisePages;
|
||||
final bool showExercisePages;
|
||||
final int currentPage;
|
||||
|
||||
GymState({
|
||||
this.exercisePages = const {},
|
||||
this.showExercisePages = true,
|
||||
this.currentPage = 0,
|
||||
});
|
||||
|
||||
GymState copyWith({
|
||||
Map<Exercise, int>? exercisePages,
|
||||
bool? showExercisePages,
|
||||
int? currentPage,
|
||||
}) {
|
||||
return GymState(
|
||||
exercisePages: exercisePages ?? this.exercisePages,
|
||||
showExercisePages: showExercisePages ?? this.showExercisePages,
|
||||
currentPage: currentPage ?? this.currentPage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GymStateNotifier extends StateNotifier<GymState> {
|
||||
final _prefs = SharedPreferences.getInstance();
|
||||
|
||||
GymStateNotifier() : super(GymState()) {
|
||||
_loadSavedState();
|
||||
}
|
||||
|
||||
Future<void> _loadSavedState() async {
|
||||
final SharedPreferences prefs = await _prefs;
|
||||
final int savedPage = prefs.getInt(GYM_PAGE_KEY) ?? 0;
|
||||
state = state.copyWith(currentPage: savedPage);
|
||||
}
|
||||
|
||||
Future<void> 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<Exercise, int> exercisePages) {
|
||||
state = state.copyWith(exercisePages: exercisePages);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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<GymMode> createState() => _GymModeState();
|
||||
}
|
||||
|
||||
class _GymModeState extends State<GymMode> {
|
||||
|
||||
class _GymModeState extends ConsumerState<GymMode> {
|
||||
var _totalElements = 1;
|
||||
|
||||
/// Map with the first (navigation) page for each exercise
|
||||
final Map<Exercise, int> _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<Exercise, int> exercisePages = {};
|
||||
|
||||
for (final slot in widget._dayData.slots) {
|
||||
var firstPage = true;
|
||||
for (final config in slot.setConfigs) {
|
||||
final exercise = Provider.of<ExercisesProvider>(context, listen: false)
|
||||
final exercise = provider.Provider.of<ExercisesProvider>(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<Widget> getContent() {
|
||||
final exerciseProvider = Provider.of<ExercisesProvider>(context, listen: false);
|
||||
final workoutProvider = Provider.of<RoutinesProvider>(context, listen: false);
|
||||
final state = ref.watch(gymStateProvider);
|
||||
final exerciseProvider = provider.Provider.of<ExercisesProvider>(context, listen: false);
|
||||
final workoutProvider = provider.Provider.of<RoutinesProvider>(context, listen: false);
|
||||
var currentElement = 1;
|
||||
final List<Widget> out = [];
|
||||
|
||||
@@ -120,12 +123,12 @@ class _GymModeState extends State<GymMode> {
|
||||
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<GymMode> {
|
||||
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<RoutinesProvider>(context, listen: false)
|
||||
provider.Provider.of<RoutinesProvider>(context, listen: false)
|
||||
.findById(widget._dayData.day!.routineId),
|
||||
_controller,
|
||||
widget._start,
|
||||
@@ -468,7 +471,7 @@ class _LogPageState extends State<LogPage> {
|
||||
|
||||
// Save the entry on the server
|
||||
try {
|
||||
await Provider.of<RoutinesProvider>(
|
||||
await provider.Provider.of<RoutinesProvider>(
|
||||
context,
|
||||
listen: false,
|
||||
).addLog(widget._log);
|
||||
@@ -890,7 +893,7 @@ class _SessionPageState extends State<SessionPage> {
|
||||
|
||||
// Save the entry on the server
|
||||
try {
|
||||
await Provider.of<RoutinesProvider>(
|
||||
await provider.Provider.of<RoutinesProvider>(
|
||||
context,
|
||||
listen: false,
|
||||
).addSession(_session);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user