diff --git a/lib/main.dart b/lib/main.dart index 36a1545..8ddb215 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,14 +4,11 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:logger/logger.dart'; +// import 'package:logger/logger.dart'; import 'package:openhiit/pages/home/home.dart'; +import 'package:openhiit/providers/workout_provider.dart'; import 'package:permission_handler/permission_handler.dart'; - -// Global logger instance for logging messages -var logger = Logger( - printer: PrettyPrinter(methodCount: 0), -); +import 'package:provider/provider.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -39,16 +36,17 @@ void main() async { class WorkoutTimer extends StatelessWidget { const WorkoutTimer({super.key}); - /// Application root. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'OpenHIIT', - debugShowCheckedModeBanner: false, - theme: ThemeData(), - darkTheme: ThemeData.dark(), // standard dark theme - themeMode: ThemeMode.system, - home: const MyHomePage(), - ); + return MultiProvider( + providers: [ChangeNotifierProvider(create: (_) => WorkoutProvider())], + child: MaterialApp( + title: 'OpenHIIT', + debugShowCheckedModeBanner: false, + theme: ThemeData(), + darkTheme: ThemeData.dark(), // standard dark theme + themeMode: ThemeMode.system, + home: const MyHomePage(), + )); } } diff --git a/lib/pages/active_timer/workout.dart b/lib/pages/active_timer/workout.dart index 8083907..e2e07b1 100644 --- a/lib/pages/active_timer/workout.dart +++ b/lib/pages/active_timer/workout.dart @@ -18,20 +18,24 @@ import '../../models/lists/list_model_animated.dart'; import '../../models/lists/list_tile_model.dart'; class StartWorkout extends StatelessWidget { - const StartWorkout({super.key}); + const StartWorkout({super.key, required this.workout}); + + final Workout workout; @override Widget build(BuildContext context) { - return const Scaffold( + return Scaffold( body: Center( - child: CountDownTimer(), + child: CountDownTimer(workout: workout), ), ); } } class CountDownTimer extends StatefulWidget { - const CountDownTimer({super.key}); + const CountDownTimer({super.key, required this.workout}); + + final Workout workout; @override CountDownTimerState createState() => CountDownTimerState(); @@ -160,12 +164,10 @@ class CountDownTimerState extends State WidgetsBinding.instance.renderViews.first.automaticSystemUiAdjustment = false; - Workout workoutArgument = - ModalRoute.of(context)!.settings.arguments as Workout; + Workout workout = widget.workout; - List exercises = workoutArgument.exercises != "" - ? jsonDecode(workoutArgument.exercises) - : []; + List exercises = + workout.exercises != "" ? jsonDecode(workout.exercises) : []; final GlobalKey listKey = GlobalKey(); @@ -177,7 +179,7 @@ class CountDownTimerState extends State shouldReset = false; intervalInfo = ListModel( listKey: listKey, - initialItems: listItems(exercises, workoutArgument), + initialItems: listItems(exercises, workout), removedItemBuilder: _buildRemovedItem, ); intervalTotal = intervalInfo.length; @@ -296,19 +298,19 @@ class CountDownTimerState extends State return Countdown( controller: _workoutController, - iterations: workoutArgument.iterations, - workSeconds: workoutArgument.workTime, - restSeconds: workoutArgument.restTime, - breakSeconds: workoutArgument.breakTime, - getreadySeconds: workoutArgument.getReadyTime, - warmupSeconds: workoutArgument.warmupTime, - cooldownSeconds: workoutArgument.cooldownTime, - workSound: workoutArgument.workSound, - restSound: workoutArgument.restSound, - completeSound: workoutArgument.completeSound, - countdownSound: workoutArgument.countdownSound, - halfwaySound: workoutArgument.halfwaySound, - numberOfWorkIntervals: workoutArgument.numExercises, + iterations: workout.iterations, + workSeconds: workout.workTime, + restSeconds: workout.restTime, + breakSeconds: workout.breakTime, + getreadySeconds: workout.getReadyTime, + warmupSeconds: workout.warmupTime, + cooldownSeconds: workout.cooldownTime, + workSound: workout.workSound, + restSound: workout.restSound, + completeSound: workout.completeSound, + countdownSound: workout.countdownSound, + halfwaySound: workout.halfwaySound, + numberOfWorkIntervals: workout.numExercises, onFinished: () { WidgetsBinding.instance.addPostFrameCallback((_) { if (intervalInfo.length == 1) { @@ -333,11 +335,11 @@ class CountDownTimerState extends State if (timerData.status == "complete" && restart == false) { done = true; } else if (timerData.status == "start" && - timerData.iterations == workoutArgument.iterations) { + timerData.iterations == workout.iterations) { currentWorkInterval = 0; ListModel intervalList = ListModel( listKey: listKey, - initialItems: listItems(exercises, workoutArgument), + initialItems: listItems(exercises, workout), removedItemBuilder: _buildRemovedItem, ); @@ -437,7 +439,7 @@ class CountDownTimerState extends State timerText( timerData.currentMicroSeconds .toString(), - workoutArgument), + workout), maxLines: 1, minFontSize: 20, maxFontSize: 20000, @@ -712,7 +714,7 @@ class CountDownTimerState extends State child: AutoSizeText( timerText( timerData.currentMicroSeconds.toString(), - workoutArgument), + workout), maxLines: 1, minFontSize: 20, maxFontSize: 20000, diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index 569402a..f4fbbb3 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -5,13 +5,15 @@ import 'package:openhiit/constants/snackbars.dart'; import 'package:openhiit/models/workout_type.dart'; import 'package:openhiit/pages/select_timer/select_timer.dart'; import 'package:openhiit/pages/view_workout/view_workout.dart'; -import 'package:openhiit/pages/view_workout/widgets/fab_column.dart'; +import 'package:openhiit/pages/home/widgets/fab_column.dart'; +import 'package:openhiit/providers/workout_provider.dart'; import 'package:openhiit/utils/database/database_manager.dart'; import 'package:openhiit/utils/functions.dart'; import 'package:openhiit/utils/import_export/local_file_util.dart'; import 'package:openhiit/widgets/home/export_bottom_sheet.dart'; import 'package:openhiit/widgets/home/timer_list_tile.dart'; import 'package:openhiit/widgets/loader.dart'; +import 'package:provider/provider.dart'; import 'package:share_plus/share_plus.dart'; import 'package:sqflite/sqflite.dart'; @@ -31,16 +33,14 @@ class _MyHomePageState extends State { /// List reorderableWorkoutList = []; - /// The initial list of workouts to be loaded fresh - /// from the DB. - /// - late Future> workouts; + late WorkoutProvider workoutProvider; /// Initialize... @override void initState() { super.initState(); - workouts = DatabaseManager().lists(DatabaseManager().initDB()); + + workoutProvider = Provider.of(context, listen: false); } // --- @@ -95,22 +95,11 @@ class _MyHomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => const ViewWorkout(), - - /// Pass the [tappedWorkout] as an argument to - /// the ViewWorkout page. - settings: RouteSettings( - arguments: tappedWorkout, + builder: (context) => ViewWorkout( + workout: tappedWorkout, ), ), - ).then((value) { - /// When we come back to the hompage, refresh the - /// list of workouts by reloading from the DB. - /// - setState(() { - workouts = DatabaseManager().lists(DatabaseManager().initDB()); - }); - }); + ); } // --- @@ -225,14 +214,7 @@ class _MyHomePageState extends State { Navigator.push( context, MaterialPageRoute(builder: (context) => const SelectTimer()), - ).then((value) { - /// When we come back to the hompage, refresh the - /// list of workouts by reloading from the DB. - /// - setState(() { - workouts = DatabaseManager().lists(DatabaseManager().initDB()); - }); - }); + ); } // --- @@ -253,7 +235,7 @@ class _MyHomePageState extends State { exporting = true; }); - List loadedWorkouts = await workouts; + List loadedWorkouts = workoutProvider.workouts; LocalFileUtil fileUtil = LocalFileUtil(); @@ -297,7 +279,7 @@ class _MyHomePageState extends State { setState(() { exporting = true; }); - List loadedWorkouts = await workouts; + List loadedWorkouts = workoutProvider.workouts; LocalFileUtil fileUtil = LocalFileUtil(); @@ -398,7 +380,7 @@ class _MyHomePageState extends State { padding: const EdgeInsets.all(8.0), child: SizedBox( child: FutureBuilder( - future: workouts, + future: workoutProvider.loadWorkoutData(), builder: (BuildContext context, AsyncSnapshot snapshot) { /// When [workouts] has successfully loaded. diff --git a/lib/pages/set_sounds.dart/widgets/sound_dropdown.dart b/lib/pages/set_sounds.dart/widgets/sound_dropdown.dart index 3755f80..e3deca9 100644 --- a/lib/pages/set_sounds.dart/widgets/sound_dropdown.dart +++ b/lib/pages/set_sounds.dart/widgets/sound_dropdown.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:soundpool/soundpool.dart'; -import '../constants/sound_name_map.dart'; +import '../constants/sounds.dart'; /// Possible interval states // enum IntervalStates { start, work, rest, complete } diff --git a/lib/pages/view_workout/view_workout.dart b/lib/pages/view_workout/view_workout.dart index 63f70f2..7b6c64e 100644 --- a/lib/pages/view_workout/view_workout.dart +++ b/lib/pages/view_workout/view_workout.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:io'; +import 'package:openhiit/pages/home/home.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:uuid/uuid.dart'; import 'package:flutter/material.dart'; @@ -15,7 +16,8 @@ import '../../models/lists/list_tile_model.dart'; import '../active_timer/workout.dart'; class ViewWorkout extends StatefulWidget { - const ViewWorkout({super.key}); + final Workout workout; + const ViewWorkout({super.key, required this.workout}); @override ViewWorkoutState createState() => ViewWorkoutState(); } @@ -65,9 +67,7 @@ class ViewWorkoutState extends State { @override Widget build(BuildContext context) { - /// Extracting the Workout object from the route arguments. - /// - Workout workout = ModalRoute.of(context)!.settings.arguments as Workout; + Workout workout = widget.workout; /// Parsing the exercises data from the Workout object. /// @@ -111,7 +111,7 @@ class ViewWorkoutState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => const CountDownTimer(), + builder: (context) => CountDownTimer(workout: workout), settings: RouteSettings( arguments: workout, ), @@ -128,8 +128,14 @@ class ViewWorkoutState extends State { ? 40 : 80, onDelete: () { - deleteList(workout).then((value) => Navigator.pop(context)); - Navigator.of(context).pop(); + deleteList(workout).then((value) { + if (context.mounted) { + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (_) => const MyHomePage()), + (route) => false); + } + }); }, onEdit: () { Workout workoutCopy = workout.copy(); diff --git a/lib/providers/workout_provider.dart b/lib/providers/workout_provider.dart new file mode 100644 index 0000000..c0bdaa7 --- /dev/null +++ b/lib/providers/workout_provider.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:openhiit/models/workout_type.dart'; +import 'package:openhiit/utils/database/database_manager.dart'; + +class WorkoutProvider extends ChangeNotifier { + List _workouts = []; + + List get workouts => _workouts; + + Future> loadWorkoutData() async { + var dbManager = DatabaseManager(); + return dbManager.lists(dbManager.initDB()).then((workouts) { + _workouts = workouts; + return _workouts; + }).whenComplete(() { + notifyListeners(); + }); + } + + Future updateWorkout(Workout workout) async { + var dbManager = DatabaseManager(); + return dbManager + .updateList(workout, await DatabaseManager().initDB()) + .then((_) { + var updated = false; + for (var i = 0; i < _workouts.length; i++) { + if (_workouts[i].id == workout.id) { + _workouts[i] = workout.copy(); + updated = true; + break; + } + } + if (!updated) { + throw Exception('Unable to find workout with ID: ${workout.id}'); + } + }).whenComplete(() => notifyListeners()); + } + + Future addWorkout(Workout workout) async { + var dbManager = DatabaseManager(); + return dbManager + .insertList(workout, await DatabaseManager().initDB()) + .then((val) { + _workouts.add(workout); + }).whenComplete(() => notifyListeners()); + } + + Future deleteWorkout(Workout workout) async { + var dbManager = DatabaseManager(); + return dbManager + .deleteList(workout.id, DatabaseManager().initDB()) + .then((_) { + _workouts.removeWhere((workout) => workout.id == workout.id); + }).whenComplete(() => notifyListeners()); + } + + void sort( + Comparable Function(Workout workout) getField, + bool ascending, + ) { + workouts.sort((a, b) { + final aValue = getField(a); + final bValue = getField(b); + return ascending + ? Comparable.compare(aValue, bValue) + : Comparable.compare(bValue, aValue); + }); + notifyListeners(); + } +} diff --git a/pubspec.lock b/pubspec.lock index 772b75b..2cb7cb7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -520,6 +520,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" numberpicker: dependency: "direct main" description: @@ -712,6 +720,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 719cbf4..9b42937 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,7 @@ dependencies: file_saver: ^0.2.13 permission_handler: ^11.3.1 shared_preferences: ^2.2.3 + provider: ^6.1.2 flutter_launcher_icons: android: "launcher_icon"