mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-19 07:50:52 +01:00
Correctly handle the state
The problem was that some of the previous changes were asynchronous and would not always represent the actual current state. This solution works, but is a bit verbose and perhaps overly complicated?
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
|
||||
const GYM_PAGE_KEY = 'gym_current_page';
|
||||
const DEFAULT_DURATION = Duration(hours: 5);
|
||||
|
||||
final StateNotifierProvider<GymStateNotifier, GymState> gymStateProvider =
|
||||
StateNotifierProvider<GymStateNotifier, GymState>((ref) {
|
||||
@@ -15,56 +15,54 @@ class GymState {
|
||||
final bool showExercisePages;
|
||||
final int currentPage;
|
||||
final int? dayId;
|
||||
late DateTime validUntil;
|
||||
|
||||
const GymState({
|
||||
this.exercisePages = const {},
|
||||
this.showExercisePages = true,
|
||||
this.currentPage = 0,
|
||||
this.dayId = null,
|
||||
});
|
||||
GymState(
|
||||
{this.exercisePages = const {},
|
||||
this.showExercisePages = true,
|
||||
this.currentPage = 0,
|
||||
this.dayId = null,
|
||||
DateTime? validUntil}) {
|
||||
this.validUntil = validUntil ?? DateTime.now().add(DEFAULT_DURATION);
|
||||
}
|
||||
|
||||
GymState copyWith({
|
||||
Map<Exercise, int>? exercisePages,
|
||||
bool? showExercisePages,
|
||||
int? currentPage,
|
||||
int? dayId,
|
||||
DateTime? validUntil,
|
||||
}) {
|
||||
return GymState(
|
||||
exercisePages: exercisePages ?? this.exercisePages,
|
||||
showExercisePages: showExercisePages ?? this.showExercisePages,
|
||||
currentPage: currentPage ?? this.currentPage,
|
||||
dayId: dayId ?? this.dayId,
|
||||
validUntil: validUntil ?? this.validUntil.add(DEFAULT_DURATION),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GymState(currentPage: $currentPage, showExercisePages: $showExercisePages, exercisePages: ${exercisePages.length} exercises, dayId: $dayId)';
|
||||
return 'GymState('
|
||||
'currentPage: $currentPage, '
|
||||
'showExercisePages: $showExercisePages, '
|
||||
'exercisePages: ${exercisePages.length} exercises, '
|
||||
'dayId: $dayId, '
|
||||
'validUntil: $validUntil '
|
||||
')';
|
||||
}
|
||||
}
|
||||
|
||||
class GymStateNotifier extends StateNotifier<GymState> {
|
||||
final _prefs = SharedPreferences.getInstance();
|
||||
final _logger = Logger('GymStateNotifier');
|
||||
|
||||
GymStateNotifier() : super(const GymState()) {
|
||||
debugPrint('GymStateNotifier: Initializing');
|
||||
_loadSavedState();
|
||||
}
|
||||
GymStateNotifier() : super(GymState());
|
||||
|
||||
Future<void> _loadSavedState() async {
|
||||
debugPrint('GymStateNotifier: Loading saved state');
|
||||
final SharedPreferences prefs = await _prefs;
|
||||
final int savedPage = prefs.getInt(GYM_PAGE_KEY) ?? 0;
|
||||
debugPrint('GymStateNotifier: Loaded saved page: $savedPage');
|
||||
state = state.copyWith(currentPage: savedPage);
|
||||
}
|
||||
|
||||
Future<void> setCurrentPage(int page) async {
|
||||
debugPrint('GymStateNotifier: Setting page from ${state.currentPage} to $page');
|
||||
final SharedPreferences prefs = await _prefs;
|
||||
await prefs.setInt(GYM_PAGE_KEY, page);
|
||||
void setCurrentPage(int page) {
|
||||
// _logger.fine('Setting page from ${state.currentPage} to $page');
|
||||
state = state.copyWith(currentPage: page);
|
||||
debugPrint('GymStateNotifier: New state - $state');
|
||||
}
|
||||
|
||||
void toggleExercisePages() {
|
||||
@@ -72,26 +70,24 @@ class GymStateNotifier extends StateNotifier<GymState> {
|
||||
}
|
||||
|
||||
void setDayId(int dayId) {
|
||||
// _logger.fine('Setting day id from ${state.dayId} to $dayId');
|
||||
state = state.copyWith(dayId: dayId);
|
||||
}
|
||||
|
||||
void setExercisePages(Map<Exercise, int> exercisePages) {
|
||||
debugPrint('GymStateNotifier: Setting exercise pages - ${exercisePages.length} exercises');
|
||||
// _logger.fine('Setting exercise pages - ${exercisePages.length} exercises');
|
||||
state = state.copyWith(exercisePages: exercisePages);
|
||||
debugPrint(
|
||||
'GymStateNotifier: Exercise pages set - ${exercisePages.entries.map((e) => '${e.key.id}: ${e.value}').join(', ')}');
|
||||
debugPrint('GymStateNotifier: New state - $state');
|
||||
// _logger.fine(
|
||||
// 'Exercise pages set - ${exercisePages.entries.map((e) => '${e.key.id}: ${e.value}').join(', ')}');
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
debugPrint('GymStateNotifier: Clearing state');
|
||||
final SharedPreferences prefs = await _prefs;
|
||||
await prefs.remove(GYM_PAGE_KEY);
|
||||
void clear() {
|
||||
_logger.fine('Clearing state');
|
||||
state = state.copyWith(
|
||||
exercisePages: {},
|
||||
currentPage: 0,
|
||||
dayId: null,
|
||||
validUntil: DateTime.now().add(DEFAULT_DURATION),
|
||||
);
|
||||
debugPrint('GymStateNotifier: State cleared - $state');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ 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:logging/logging.dart';
|
||||
import 'package:provider/provider.dart' as provider;
|
||||
import 'package:wger/exceptions/http_exception.dart';
|
||||
import 'package:wger/helpers/consts.dart';
|
||||
@@ -54,6 +55,7 @@ class GymMode extends ConsumerStatefulWidget {
|
||||
final DayData _dayDataDisplay;
|
||||
final int _iteration;
|
||||
late final TimeOfDay _start;
|
||||
final _logger = Logger('GymMode');
|
||||
|
||||
GymMode(this._dayDataGym, this._dayDataDisplay, this._iteration) {
|
||||
_start = TimeOfDay.now();
|
||||
@@ -65,6 +67,8 @@ class GymMode extends ConsumerStatefulWidget {
|
||||
|
||||
class _GymModeState extends ConsumerState<GymMode> {
|
||||
var _totalElements = 1;
|
||||
late Future<int> _initData;
|
||||
bool _initialPageJumped = false;
|
||||
|
||||
/// Map with the first (navigation) page for each exercise
|
||||
final Map<Exercise, int> _exercisePages = {};
|
||||
@@ -79,52 +83,46 @@ class _GymModeState extends ConsumerState<GymMode> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initData = _loadGymState();
|
||||
_controller = PageController(initialPage: 0);
|
||||
_calculatePages();
|
||||
|
||||
// Initialize the controller with the current page
|
||||
final initialPage = ref.read(gymStateProvider).currentPage;
|
||||
_controller = PageController(initialPage: initialPage);
|
||||
|
||||
// Delay state modifications until after the widget tree is built
|
||||
Future.microtask(() {
|
||||
// Get the saved page and day from the provider
|
||||
final savedPage = ref.read(gymStateProvider).currentPage;
|
||||
final savedDayId = ref.read(gymStateProvider).dayId;
|
||||
debugPrint('Saved Page: $savedPage');
|
||||
debugPrint('Saved Day ID: $savedDayId');
|
||||
|
||||
// Set the dayId in the state
|
||||
ref.read(gymStateProvider.notifier).setDayId(widget._dayDataGym.day!.id!);
|
||||
debugPrint('Current Day ID: ${widget._dayDataGym.day!.id}');
|
||||
|
||||
// Check if the current day is different from the saved day
|
||||
if (widget._dayDataGym.day!.id != savedDayId) {
|
||||
// Reset the saved page to 0
|
||||
ref.read(gymStateProvider.notifier).setCurrentPage(0);
|
||||
debugPrint('Different day detected. Resetting to page 0.');
|
||||
} else {
|
||||
// Use the saved page
|
||||
ref.read(gymStateProvider.notifier).setCurrentPage(savedPage);
|
||||
debugPrint('Same day detected. Using saved page: $savedPage');
|
||||
}
|
||||
|
||||
// Calculate the pages
|
||||
_calculatePages();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.read(gymStateProvider.notifier).setExercisePages(_exercisePages);
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> _loadGymState() async {
|
||||
final validUntil = ref.read(gymStateProvider).validUntil;
|
||||
final currentPage = ref.read(gymStateProvider).currentPage;
|
||||
final savedDayId = ref.read(gymStateProvider).dayId;
|
||||
|
||||
final newDayId = widget._dayDataGym.day!.id!;
|
||||
final shouldReset =
|
||||
widget._dayDataGym.day!.id != savedDayId || validUntil.isBefore(DateTime.now());
|
||||
widget._logger.fine('Day ID mismatch or expired validUntil date. Resetting to page 0.');
|
||||
final initialPage = shouldReset ? 0 : currentPage;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
ref.read(gymStateProvider.notifier)
|
||||
..setDayId(newDayId)
|
||||
..setCurrentPage(initialPage);
|
||||
});
|
||||
|
||||
return initialPage;
|
||||
}
|
||||
|
||||
void _calculatePages() {
|
||||
// for (final slot in widget._dayDataGym.slots) {
|
||||
// _totalElements += slot.setConfigs.length;
|
||||
// }
|
||||
_totalElements = 1;
|
||||
for (final slot in widget._dayDataGym.slots) {
|
||||
_totalElements += slot.setConfigs.length;
|
||||
}
|
||||
_exercisePages.clear();
|
||||
var currentPage = 1;
|
||||
|
||||
for (final slot in widget._dayDataGym.slots) {
|
||||
var firstPage = true;
|
||||
for (final config in slot.setConfigs) {
|
||||
final exercise = provider.Provider.of<ExercisesProvider>(context, listen: false)
|
||||
.findExerciseById(config.exerciseId);
|
||||
final exercise = context.read<ExercisesProvider>().findExerciseById(config.exerciseId);
|
||||
|
||||
if (firstPage) {
|
||||
_exercisePages[exercise] = currentPage;
|
||||
@@ -134,8 +132,6 @@ class _GymModeState extends ConsumerState<GymMode> {
|
||||
firstPage = false;
|
||||
}
|
||||
}
|
||||
|
||||
ref.read(gymStateProvider.notifier).setExercisePages(_exercisePages);
|
||||
}
|
||||
|
||||
List<Widget> getContent() {
|
||||
@@ -181,34 +177,48 @@ class _GymModeState extends ConsumerState<GymMode> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> children = [
|
||||
StartPage(_controller, widget._dayDataDisplay, _exercisePages),
|
||||
...getContent(),
|
||||
SessionPage(
|
||||
provider.Provider.of<RoutinesProvider>(context, listen: false)
|
||||
.findById(widget._dayDataGym.day!.routineId),
|
||||
_controller,
|
||||
widget._start,
|
||||
_exercisePages,
|
||||
),
|
||||
];
|
||||
return PageView(
|
||||
controller: _controller,
|
||||
onPageChanged: (page) {
|
||||
// Update the current page
|
||||
ref.read(gymStateProvider.notifier).setCurrentPage(page);
|
||||
debugPrint('Current Page: $page');
|
||||
debugPrint('Total Pages: ${children.length}');
|
||||
// Check if the last page is reached
|
||||
if (page == children.length - 1) {
|
||||
ref.read(gymStateProvider.notifier).clear();
|
||||
debugPrint('Last page reached. Resetting state and navigation.');
|
||||
// Reset the navigation stack
|
||||
_controller.jumpToPage(0);
|
||||
}
|
||||
},
|
||||
children: children,
|
||||
);
|
||||
return FutureBuilder<int>(
|
||||
future: _initData,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(child: Text('Error: ${snapshot.error}'));
|
||||
} else {
|
||||
final initialPage = snapshot.data!;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!_initialPageJumped && _controller.hasClients) {
|
||||
_controller.jumpToPage(initialPage);
|
||||
setState(() => _initialPageJumped = true);
|
||||
}
|
||||
});
|
||||
|
||||
final List<Widget> children = [
|
||||
StartPage(_controller, widget._dayDataDisplay, _exercisePages),
|
||||
...getContent(),
|
||||
SessionPage(
|
||||
provider.Provider.of<RoutinesProvider>(context, listen: false)
|
||||
.findById(widget._dayDataGym.day!.routineId),
|
||||
_controller,
|
||||
widget._start,
|
||||
_exercisePages,
|
||||
),
|
||||
];
|
||||
|
||||
return PageView(
|
||||
controller: _controller,
|
||||
onPageChanged: (page) {
|
||||
ref.read(gymStateProvider.notifier).setCurrentPage(page);
|
||||
|
||||
// Check if the last page is reached
|
||||
if (page == children.length - 1) {
|
||||
ref.read(gymStateProvider.notifier).clear();
|
||||
}
|
||||
},
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user