From fb742371f1ea0ed068fd04eeac36dbace1829180 Mon Sep 17 00:00:00 2001 From: Yair Chen Date: Sat, 11 Sep 2021 23:33:43 +0300 Subject: [PATCH] Implement ui for exercises list --- lib/main.dart | 2 + lib/models/exercises/category.dart | 10 +++ lib/providers/exercises.dart | 7 ++ lib/screens/exercises_screen.dart | 96 ++++++++++++++++++++++++++++ lib/screens/home_tabs_screen.dart | 4 +- lib/widgets/exercises/list_tile.dart | 59 +++++++++++++++++ 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 lib/screens/exercises_screen.dart create mode 100644 lib/widgets/exercises/list_tile.dart diff --git a/lib/main.dart b/lib/main.dart index 5d4cf3e52..b48f1424f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -28,6 +28,7 @@ import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/auth_screen.dart'; import 'package:wger/screens/dashboard.dart'; +import 'package:wger/screens/exercises_screen.dart'; import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/gallery_screen.dart'; import 'package:wger/screens/gym_mode.dart'; @@ -129,6 +130,7 @@ class MyApp extends StatelessWidget { WeightScreen.routeName: (ctx) => WeightScreen(), WorkoutPlanScreen.routeName: (ctx) => WorkoutPlanScreen(), WorkoutPlansScreen.routeName: (ctx) => WorkoutPlansScreen(), + ExercisesScreen.routeName: (ctx) => ExercisesScreen(), }, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, diff --git a/lib/models/exercises/category.dart b/lib/models/exercises/category.dart index 9b15dc1b2..081b09d55 100644 --- a/lib/models/exercises/category.dart +++ b/lib/models/exercises/category.dart @@ -41,4 +41,14 @@ class ExerciseCategory { // Boilerplate factory ExerciseCategory.fromJson(Map json) => _$ExerciseCategoryFromJson(json); Map toJson() => _$ExerciseCategoryToJson(this); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ExerciseCategory && other.id == id && other.name == name; + } + + @override + int get hashCode => id.hashCode ^ name.hashCode; } diff --git a/lib/providers/exercises.dart b/lib/providers/exercises.dart index 1c6adbfd7..a306228b4 100644 --- a/lib/providers/exercises.dart +++ b/lib/providers/exercises.dart @@ -59,6 +59,13 @@ class ExercisesProvider extends WgerBaseProvider with ChangeNotifier { return [..._exercises]; } + List findByCategory(ExerciseCategory? category) { + if (category == null) return this.items; + return this.items.where((exercise) => exercise.categoryObj == category).toList(); + } + + List get categories => _categories; + /// Returns an exercise Exercise findById(int exerciseId) { return _exercises.firstWhere((exercise) => exercise.id == exerciseId); diff --git a/lib/screens/exercises_screen.dart b/lib/screens/exercises_screen.dart new file mode 100644 index 000000000..25a76358f --- /dev/null +++ b/lib/screens/exercises_screen.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:wger/models/exercises/category.dart'; +import 'package:wger/providers/exercises.dart'; +import 'package:wger/widgets/core/app_bar.dart'; +import 'package:wger/widgets/exercises/list_tile.dart'; + +class ExercisesScreen extends StatefulWidget { + const ExercisesScreen({Key? key}) : super(key: key); + static const routeName = '/exercises'; + + @override + _ExercisesScreenState createState() => _ExercisesScreenState(); +} + +class _ExercisesScreenState extends State { + ExerciseCategory? _category; + + List> _categoryOptions() { + return Provider.of(context, listen: false) + .categories + .map>( + (category) { + return DropdownMenuItem( + child: Text(category.name), + value: category, + ); + }, + ).toList(); + } + + @override + Widget build(BuildContext context) { + final exercisesList = + Provider.of(context, listen: false).findByCategory(_category); + final size = MediaQuery.of(context).size; + + return Scaffold( + appBar: WgerAppBar('Exercises'), + body: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 15), + child: Row( + children: [ + Expanded( + child: DropdownButtonFormField( + hint: Text('All Exercises'), + value: _category, + items: _categoryOptions(), + decoration: InputDecoration( + contentPadding: const EdgeInsets.symmetric( + horizontal: 10, + ), + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.black), + ), + ), + onChanged: (ExerciseCategory? newCategory) { + setState(() { + _category = newCategory; + }); + }, + ), + ), + Row( + children: [ + IconButton(onPressed: () {}, icon: Icon(Icons.search)), + IconButton(onPressed: () {}, icon: Icon(Icons.filter_alt)), + ], + ) + ], + ), + ), + Expanded( + child: ListView.separated( + separatorBuilder: (context, index) { + return Divider( + thickness: 1, + ); + }, + itemCount: exercisesList.length, + itemBuilder: (context, index) { + final exercise = exercisesList[index]; + return Container( + height: size.height * 0.175, + child: ExerciseListTile(exercise: exercise), + ); + }, + ), + ) + ], + ), + ); + } +} diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index fe3e123d6..19b9bad05 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -30,6 +30,7 @@ import 'package:wger/providers/measurement.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/dashboard.dart'; +import 'package:wger/screens/exercises_screen.dart'; import 'package:wger/screens/gallery_screen.dart'; import 'package:wger/screens/nutritional_plans_screen.dart'; import 'package:wger/screens/weight_screen.dart'; @@ -63,7 +64,8 @@ class _HomeTabsScreenState extends State with SingleTickerProvid final _screenList = [ DashboardScreen(), WorkoutPlansScreen(), - NutritionScreen(), + // Replaced [NutritionScreen] for debugging purposes + ExercisesScreen(), WeightScreen(), GalleryScreen(), ]; diff --git a/lib/widgets/exercises/list_tile.dart b/lib/widgets/exercises/list_tile.dart new file mode 100644 index 000000000..8ba43d57a --- /dev/null +++ b/lib/widgets/exercises/list_tile.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:wger/models/exercises/exercise.dart'; +import 'package:wger/widgets/exercises/images.dart'; + +class ExerciseListTile extends StatelessWidget { + const ExerciseListTile({Key? key, required this.exercise}) : super(key: key); + + final Exercise exercise; + + @override + Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + final theme = Theme.of(context); + + return Row( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + width: size.width * 0.3, + child: Center( + child: ExerciseImageWidget( + image: exercise.getMainImage, + ), + ), + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 7), + decoration: BoxDecoration( + color: theme.primaryColorLight.withOpacity(0.15), + border: Border.all(color: Colors.grey[300]!), + borderRadius: BorderRadius.circular(5), + ), + child: Text( + exercise.categoryObj.name, + ), + ), + Text( + exercise.name, + style: theme.textTheme.headline5, + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + Text( + exercise.equipment.map((equipment) => equipment.name).join(", "), + ) + ], + ), + ) + ], + ); + } +}