Merge branch 'master' into feature/ios-screenshots

This commit is contained in:
Roland Geider
2025-12-01 14:21:03 +01:00
27 changed files with 498 additions and 317 deletions

View File

@@ -0,0 +1,36 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (c) 2020, 2025 wger Team
*
* wger Workout Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/material.dart';
import 'package:wger/helpers/material.dart';
class WidescreenWrapper extends StatelessWidget {
final Widget child;
const WidescreenWrapper({required this.child, super.key});
@override
Widget build(BuildContext context) {
return Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: MATERIAL_MD_BREAKPOINT),
child: child,
),
);
}
}

23
lib/helpers/material.dart Normal file
View File

@@ -0,0 +1,23 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* Copyright (c) 2020, 2025 wger Team
*
* wger Workout Manager is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// From https://m3.material.io/foundations/layout/applying-layout/window-size-classes
const MATERIAL_XS_BREAKPOINT = 600.0;
const MATERIAL_MD_BREAKPOINT = 840.0;
const MATERIAL_LG_BREAKPOINT = 1200.0;
const MATERIAL_XL_BREAKPOINT = 1600.0;

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/exceptions/http_exception.dart';
import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/errors.dart'; import 'package:wger/helpers/errors.dart';
@@ -151,55 +152,57 @@ class _AddExerciseStepperState extends State<AddExerciseStepper> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: EmptyAppBar(AppLocalizations.of(context).contributeExercise), appBar: EmptyAppBar(AppLocalizations.of(context).contributeExercise),
body: Stepper( body: WidescreenWrapper(
controlsBuilder: _controlsBuilder, child: Stepper(
steps: [ controlsBuilder: _controlsBuilder,
Step( steps: [
title: Text(AppLocalizations.of(context).baseData), Step(
content: Step1Basics(formkey: _keys[0]), title: Text(AppLocalizations.of(context).baseData),
), content: Step1Basics(formkey: _keys[0]),
Step( ),
title: Text(AppLocalizations.of(context).variations), Step(
content: Step2Variations(formkey: _keys[1]), title: Text(AppLocalizations.of(context).variations),
), content: Step2Variations(formkey: _keys[1]),
Step( ),
title: Text(AppLocalizations.of(context).description), Step(
content: Step3Description(formkey: _keys[2]), title: Text(AppLocalizations.of(context).description),
), content: Step3Description(formkey: _keys[2]),
Step( ),
title: Text(AppLocalizations.of(context).translation), Step(
content: Step4Translation(formkey: _keys[3]), title: Text(AppLocalizations.of(context).translation),
), content: Step4Translation(formkey: _keys[3]),
Step( ),
title: Text(AppLocalizations.of(context).images), Step(
content: Step5Images(formkey: _keys[4]), title: Text(AppLocalizations.of(context).images),
), content: Step5Images(formkey: _keys[4]),
Step(title: Text(AppLocalizations.of(context).overview), content: Step6Overview()), ),
], Step(title: Text(AppLocalizations.of(context).overview), content: Step6Overview()),
currentStep: _currentStep, ],
onStepContinue: () { currentStep: _currentStep,
if (_keys[_currentStep].currentState?.validate() ?? false) { onStepContinue: () {
_keys[_currentStep].currentState?.save(); if (_keys[_currentStep].currentState?.validate() ?? false) {
_keys[_currentStep].currentState?.save();
if (_currentStep != lastStepIndex) { if (_currentStep != lastStepIndex) {
setState(() { setState(() {
_currentStep += 1; _currentStep += 1;
}); });
}
} }
} },
}, onStepCancel: () => setState(() {
onStepCancel: () => setState(() { if (_currentStep != 0) {
if (_currentStep != 0) { _currentStep -= 1;
_currentStep -= 1; }
} }),
}), /*
/* onStepTapped: (int index) {
onStepTapped: (int index) { setState(() {
setState(() { _currentStep = index;
_currentStep = index; });
}); },
}, */
*/ ),
), ),
); );
} }

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/widgets/routines/plate_calculator.dart'; import 'package:wger/widgets/routines/plate_calculator.dart';
@@ -13,7 +14,7 @@ class ConfigurePlatesScreen extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text(i18n.selectAvailablePlates)), appBar: AppBar(title: Text(i18n.selectAvailablePlates)),
body: const ConfigureAvailablePlates(), body: const WidescreenWrapper(child: ConfigureAvailablePlates()),
); );
} }
} }

View File

@@ -17,6 +17,7 @@
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wger/helpers/material.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/widgets/core/app_bar.dart'; import 'package:wger/widgets/core/app_bar.dart';
import 'package:wger/widgets/dashboard/calendar.dart'; import 'package:wger/widgets/dashboard/calendar.dart';
@@ -32,18 +33,48 @@ class DashboardScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
final isMobile = width < MATERIAL_XS_BREAKPOINT;
late final int crossAxisCount;
if (width < MATERIAL_XS_BREAKPOINT) {
crossAxisCount = 1;
} else if (width < MATERIAL_MD_BREAKPOINT) {
crossAxisCount = 2;
} else if (width < MATERIAL_LG_BREAKPOINT) {
crossAxisCount = 3;
} else {
crossAxisCount = 4;
}
final items = [
const DashboardRoutineWidget(),
const DashboardNutritionWidget(),
const DashboardWeightWidget(),
const DashboardMeasurementWidget(),
const DashboardCalendarWidget(),
];
return Scaffold( return Scaffold(
appBar: MainAppBar(AppLocalizations.of(context).labelDashboard), appBar: MainAppBar(AppLocalizations.of(context).labelDashboard),
body: const SingleChildScrollView( body: Center(
padding: EdgeInsets.all(10), child: ConstrainedBox(
child: Column( constraints: BoxConstraints(maxWidth: MATERIAL_LG_BREAKPOINT.toDouble()),
children: [ child: isMobile
DashboardRoutineWidget(), ? ListView.builder(
DashboardNutritionWidget(), padding: const EdgeInsets.all(10),
DashboardWeightWidget(), itemBuilder: (context, index) => items[index],
DashboardMeasurementWidget(), itemCount: items.length,
DashboardCalendarWidget(), )
], : GridView.builder(
padding: const EdgeInsets.all(10),
itemBuilder: (context, index) => SingleChildScrollView(child: items[index]),
itemCount: items.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
childAspectRatio: 0.7,
),
),
), ),
), ),
); );

View File

@@ -17,6 +17,7 @@
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/exercises/exercise.dart';
import 'package:wger/widgets/exercises/exercises.dart'; import 'package:wger/widgets/exercises/exercises.dart';
@@ -33,9 +34,11 @@ class ExerciseDetailScreen extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(exercise.getTranslation(Localizations.localeOf(context).languageCode).name), title: Text(exercise.getTranslation(Localizations.localeOf(context).languageCode).name),
), ),
body: Padding( body: WidescreenWrapper(
padding: const EdgeInsets.symmetric(horizontal: 10), child: Padding(
child: ExerciseDetail(exercise), padding: const EdgeInsets.symmetric(horizontal: 10),
child: ExerciseDetail(exercise),
),
), ),
); );
} }

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/exercises/exercise.dart';
import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/exercises.dart';
@@ -23,21 +24,23 @@ class _ExercisesScreenState extends State<ExercisesScreen> {
return Scaffold( return Scaffold(
appBar: EmptyAppBar(AppLocalizations.of(context).exercises), appBar: EmptyAppBar(AppLocalizations.of(context).exercises),
body: Column( body: WidescreenWrapper(
children: [ child: Column(
const FilterRow(), children: [
Expanded( const FilterRow(),
child: exercisesList.isEmpty Expanded(
? const Center( child: exercisesList.isEmpty
child: SizedBox( ? const Center(
height: 30, child: SizedBox(
width: 30, height: 30,
child: CircularProgressIndicator(), width: 30,
), child: CircularProgressIndicator(),
) ),
: _ExercisesList(exerciseList: exercisesList), )
), : _ExercisesList(exerciseList: exercisesList),
], ),
],
),
), ),
); );
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
/// Arguments passed to the form screen /// Arguments passed to the form screen
class FormScreenArguments { class FormScreenArguments {
@@ -54,18 +55,20 @@ class FormScreen extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text(args.title)), appBar: AppBar(title: Text(args.title)),
body: args.hasListView body: WidescreenWrapper(
? Scrollable( child: args.hasListView
viewportBuilder: (BuildContext context, ViewportOffset position) => Padding( ? Scrollable(
padding: args.padding, viewportBuilder: (BuildContext context, ViewportOffset position) => Padding(
child: args.widget, padding: args.padding,
child: args.widget,
),
)
: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [Padding(padding: args.padding, child: args.widget)],
), ),
) ),
: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [Padding(padding: args.padding, child: args.widget)],
),
); );
} }
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/helpers/platform.dart'; import 'package:wger/helpers/platform.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/gallery.dart'; import 'package:wger/providers/gallery.dart';
@@ -52,8 +53,10 @@ class GalleryScreen extends StatelessWidget {
); );
}, },
), ),
body: Consumer<GalleryProvider>( body: WidescreenWrapper(
builder: (context, workoutProvider, child) => const Gallery(), child: Consumer<GalleryProvider>(
builder: (context, workoutProvider, child) => const Gallery(),
),
), ),
); );
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/providers/routines.dart'; import 'package:wger/providers/routines.dart';
import 'package:wger/widgets/routines/gym_mode/gym_mode.dart'; import 'package:wger/widgets/routines/gym_mode/gym_mode.dart';
@@ -51,8 +52,10 @@ class GymModeScreen extends StatelessWidget {
// backgroundColor: Theme.of(context).cardColor, // backgroundColor: Theme.of(context).cardColor,
// primary: false, // primary: false,
body: SafeArea( body: SafeArea(
child: Consumer<RoutinesProvider>( child: WidescreenWrapper(
builder: (context, value, child) => GymMode(dayDataGym, dayDataDisplay, args.iteration), child: Consumer<RoutinesProvider>(
builder: (context, value, child) => GymMode(dayDataGym, dayDataDisplay, args.iteration),
),
), ),
), ),
); );

View File

@@ -21,6 +21,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:rive/rive.dart'; import 'package:rive/rive.dart';
import 'package:wger/helpers/material.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/auth.dart'; import 'package:wger/providers/auth.dart';
import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/body_weight.dart';
@@ -51,6 +52,7 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
late Future<void> _initialData; late Future<void> _initialData;
bool _errorHandled = false; bool _errorHandled = false;
int _selectedIndex = 0; int _selectedIndex = 0;
bool _isWideScreen = false;
@override @override
void initState() { void initState() {
@@ -59,6 +61,14 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
_initialData = _loadEntries(); _initialData = _loadEntries();
} }
@override
void didChangeDependencies() {
super.didChangeDependencies();
final double width = MediaQuery.of(context).size.width;
_isWideScreen = width > MATERIAL_XS_BREAKPOINT;
}
void _onItemTapped(int index) { void _onItemTapped(int index) {
setState(() { setState(() {
_selectedIndex = index; _selectedIndex = index;
@@ -141,6 +151,57 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final destinations = [
NavigationDestination(
icon: const Icon(Icons.home),
label: AppLocalizations.of(context).labelDashboard,
),
NavigationDestination(
icon: const Icon(Icons.fitness_center),
label: AppLocalizations.of(context).labelBottomNavWorkout,
),
NavigationDestination(
icon: const Icon(Icons.restaurant),
label: AppLocalizations.of(context).labelBottomNavNutrition,
),
NavigationDestination(
icon: const FaIcon(FontAwesomeIcons.weightScale, size: 20),
label: AppLocalizations.of(context).weight,
),
NavigationDestination(
icon: const Icon(Icons.photo_library),
label: AppLocalizations.of(context).gallery,
),
];
/// Navigation bar for narrow screens
Widget getNavigationBar() {
return NavigationBar(
destinations: destinations,
onDestinationSelected: _onItemTapped,
selectedIndex: _selectedIndex,
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
);
}
/// Navigation rail for wide screens
Widget getNavigationRail() {
return NavigationRail(
selectedIndex: _selectedIndex,
onDestinationSelected: _onItemTapped,
labelType: NavigationRailLabelType.all,
scrollable: true,
destinations: destinations
.map(
(d) => NavigationRailDestination(
icon: d.icon,
label: Text(d.label),
),
)
.toList(),
);
}
return FutureBuilder<void>( return FutureBuilder<void>(
future: _initialData, future: _initialData,
builder: (context, snapshot) { builder: (context, snapshot) {
@@ -173,34 +234,13 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
} }
return Scaffold( return Scaffold(
body: _screenList.elementAt(_selectedIndex), body: Row(
bottomNavigationBar: NavigationBar( children: [
destinations: [ if (_isWideScreen) getNavigationRail(),
NavigationDestination( Expanded(child: _screenList.elementAt(_selectedIndex)),
icon: const Icon(Icons.home),
label: AppLocalizations.of(context).labelDashboard,
),
NavigationDestination(
icon: const Icon(Icons.fitness_center),
label: AppLocalizations.of(context).labelBottomNavWorkout,
),
NavigationDestination(
icon: const Icon(Icons.restaurant),
label: AppLocalizations.of(context).labelBottomNavNutrition,
),
NavigationDestination(
icon: const FaIcon(FontAwesomeIcons.weightScale, size: 20),
label: AppLocalizations.of(context).weight,
),
NavigationDestination(
icon: const Icon(Icons.photo_library),
label: AppLocalizations.of(context).gallery,
),
], ],
onDestinationSelected: _onItemTapped,
selectedIndex: _selectedIndex,
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
), ),
bottomNavigationBar: _isWideScreen ? null : getNavigationBar(),
); );
}, },
); );

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/measurement.dart'; import 'package:wger/providers/measurement.dart';
import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/form_screen.dart';
@@ -46,8 +47,10 @@ class MeasurementCategoriesScreen extends StatelessWidget {
); );
}, },
), ),
body: Consumer<MeasurementProvider>( body: WidescreenWrapper(
builder: (context, provider, child) => const CategoriesList(), child: Consumer<MeasurementProvider>(
builder: (context, provider, child) => const CategoriesList(),
),
), ),
); );
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/measurement.dart'; import 'package:wger/providers/measurement.dart';
import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/form_screen.dart';
@@ -134,9 +135,11 @@ class MeasurementEntriesScreen extends StatelessWidget {
); );
}, },
), ),
body: SingleChildScrollView( body: WidescreenWrapper(
child: Consumer<MeasurementProvider>( child: SingleChildScrollView(
builder: (context, provider, child) => EntriesList(category), child: Consumer<MeasurementProvider>(
builder: (context, provider, child) => EntriesList(category),
),
), ),
), ),
); );

View File

@@ -19,6 +19,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/models/nutrition/nutritional_plan.dart'; import 'package:wger/models/nutrition/nutritional_plan.dart';
import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/nutrition.dart';
import 'package:wger/widgets/nutrition/nutritional_diary_detail.dart'; import 'package:wger/widgets/nutrition/nutritional_diary_detail.dart';
@@ -46,11 +47,13 @@ class NutritionalDiaryScreen extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(DateFormat.yMd(Localizations.localeOf(context).languageCode).format(args.date)), title: Text(DateFormat.yMd(Localizations.localeOf(context).languageCode).format(args.date)),
), ),
body: Consumer<NutritionPlansProvider>( body: WidescreenWrapper(
builder: (context, nutritionProvider, child) => SingleChildScrollView( child: Consumer<NutritionPlansProvider>(
child: Padding( builder: (context, nutritionProvider, child) => SingleChildScrollView(
padding: const EdgeInsets.all(8.0), child: Padding(
child: NutritionalDiaryDetailWidget(args.plan, args.date), padding: const EdgeInsets.all(8.0),
child: NutritionalDiaryDetailWidget(args.plan, args.date),
),
), ),
), ),
), ),

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/nutrition.dart';
import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/form_screen.dart';
@@ -48,8 +49,10 @@ class NutritionalPlansScreen extends StatelessWidget {
); );
}, },
), ),
body: Consumer<NutritionPlansProvider>( body: WidescreenWrapper(
builder: (context, nutritionProvider, child) => NutritionalPlansList(nutritionProvider), child: Consumer<NutritionPlansProvider>(
builder: (context, nutritionProvider, child) => NutritionalPlansList(nutritionProvider),
),
), ),
); );
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/providers/routines.dart'; import 'package:wger/providers/routines.dart';
import 'package:wger/widgets/core/app_bar.dart'; import 'package:wger/widgets/core/app_bar.dart';
import 'package:wger/widgets/routines/routine_edit.dart'; import 'package:wger/widgets/routines/routine_edit.dart';
@@ -34,7 +35,7 @@ class RoutineEditScreen extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: EmptyAppBar(routine.name), appBar: EmptyAppBar(routine.name),
body: RoutineEdit(routine), body: WidescreenWrapper(child: RoutineEdit(routine)),
); );
} }
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/models/workouts/routine.dart'; import 'package:wger/models/workouts/routine.dart';
import 'package:wger/providers/routines.dart'; import 'package:wger/providers/routines.dart';
@@ -49,8 +50,10 @@ class RoutineListScreen extends StatelessWidget {
}, },
child: const Icon(Icons.add, color: Colors.white), child: const Icon(Icons.add, color: Colors.white),
), ),
body: Consumer<RoutinesProvider>( body: WidescreenWrapper(
builder: (context, workoutProvider, child) => RoutinesList(workoutProvider), child: Consumer<RoutinesProvider>(
builder: (context, workoutProvider, child) => RoutinesList(workoutProvider),
),
), ),
); );
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/providers/routines.dart'; import 'package:wger/providers/routines.dart';
import 'package:wger/widgets/core/app_bar.dart'; import 'package:wger/widgets/core/app_bar.dart';
import 'package:wger/widgets/routines/workout_logs.dart'; import 'package:wger/widgets/routines/workout_logs.dart';
@@ -34,7 +35,7 @@ class WorkoutLogsScreen extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: EmptyAppBar(routine.name), appBar: EmptyAppBar(routine.name),
body: WorkoutLogs(routine), body: WidescreenWrapper(child: WorkoutLogs(routine)),
); );
} }
} }

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/providers/routines.dart'; import 'package:wger/providers/routines.dart';
import 'package:wger/widgets/routines/app_bar.dart'; import 'package:wger/widgets/routines/app_bar.dart';
import 'package:wger/widgets/routines/routine_detail.dart'; import 'package:wger/widgets/routines/routine_detail.dart';
@@ -36,9 +37,11 @@ class RoutineScreen extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: RoutineDetailAppBar(routine), appBar: RoutineDetailAppBar(routine),
body: SingleChildScrollView( body: WidescreenWrapper(
child: Consumer<RoutinesProvider>( child: SingleChildScrollView(
builder: (context, value, child) => RoutineDetail(routine), child: Consumer<RoutinesProvider>(
builder: (context, value, child) => RoutineDetail(routine),
),
), ),
), ),
); );

View File

@@ -18,6 +18,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/body_weight.dart';
import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/form_screen.dart';
@@ -47,9 +48,11 @@ class WeightScreen extends StatelessWidget {
); );
}, },
), ),
body: SingleChildScrollView( body: WidescreenWrapper(
child: Consumer<BodyWeightProvider>( child: SingleChildScrollView(
builder: (context, provider, child) => WeightOverview(provider), child: Consumer<BodyWeightProvider>(
builder: (context, provider, child) => WeightOverview(provider),
),
), ),
), ),
); );

View File

@@ -19,6 +19,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/misc.dart'; import 'package:wger/helpers/misc.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
@@ -53,164 +54,166 @@ class AboutPage extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text(i18n.aboutPageTitle)), appBar: AppBar(title: Text(i18n.aboutPageTitle)),
body: SingleChildScrollView( body: WidescreenWrapper(
padding: const EdgeInsets.all(20.0), child: SingleChildScrollView(
child: Column( padding: const EdgeInsets.all(20.0),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Row( children: [
mainAxisAlignment: MainAxisAlignment.center, Row(
children: [ mainAxisAlignment: MainAxisAlignment.center,
Image.asset( children: [
'assets/images/logo.png', Image.asset(
width: 60, 'assets/images/logo.png',
semanticLabel: 'wger logo', width: 60,
), semanticLabel: 'wger logo',
const SizedBox(width: 20), ),
Column( const SizedBox(width: 20),
crossAxisAlignment: CrossAxisAlignment.start, Column(
mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start,
children: [ mainAxisAlignment: MainAxisAlignment.center,
const Text( children: [
'wger', const Text(
style: TextStyle( 'wger',
fontSize: 23, style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 23,
fontWeight: FontWeight.w600,
),
), ),
), Text(
Text( 'App: ${authProvider.applicationVersion?.version ?? 'N/A'}\n'
'App: ${authProvider.applicationVersion?.version ?? 'N/A'}\n' 'Server: ${authProvider.serverVersion ?? 'N/A'}',
'Server: ${authProvider.serverVersion ?? 'N/A'}',
style: Theme.of(context).textTheme.bodySmall,
),
Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text(
'\u{a9} ${today.year} wger contributors',
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
), Padding(
], padding: const EdgeInsets.only(top: 4.0),
), child: Text(
], '\u{a9} ${today.year} wger contributors',
), style: Theme.of(context).textTheme.bodySmall,
),
_buildSectionSpacer(), ),
_buildSectionHeader(context, i18n.aboutWhySupportTitle), ],
Text(i18n.aboutDescription),
_buildSectionSpacer(),
_buildSectionHeader(context, i18n.aboutContributeTitle),
Text(i18n.aboutContributeText),
ListTile(
leading: const Icon(Icons.bug_report),
title: Text(i18n.aboutBugsListTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(GITHUB_ISSUES_URL, context),
),
ListTile(
leading: const Icon(Icons.translate),
title: Text(i18n.aboutTranslationListTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(WEBLATE_URL, context),
),
ListTile(
leading: const Icon(Icons.code),
title: Text(i18n.aboutSourceListTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(GITHUB_PROJECT_URL, context),
),
ListTile(
leading: const FaIcon(FontAwesomeIcons.dumbbell, size: 18),
title: Text(i18n.contributeExercise),
contentPadding: EdgeInsets.zero,
onTap: () => Navigator.of(context).pushNamed(AddExerciseScreen.routeName),
),
_buildSectionSpacer(),
_buildSectionHeader(context, i18n.aboutDonateTitle),
Text(i18n.aboutDonateText),
const SizedBox(height: 15),
// Using Wrap for buttons to handle different screen sizes potentially
Center(
child: Wrap(
spacing: 10.0,
runSpacing: 10.0,
alignment: WrapAlignment.center,
children: [
ElevatedButton.icon(
icon: const FaIcon(FontAwesomeIcons.mugHot, size: 18),
label: const Text('Buy me a coffee'),
onPressed: () => launchURL(BUY_ME_A_COFFEE_URL, context),
),
ElevatedButton.icon(
icon: const FaIcon(FontAwesomeIcons.solidHeart, size: 18),
label: const Text('Liberapay'),
onPressed: () => launchURL(LIBERAPAY_URL, context),
),
ElevatedButton.icon(
icon: const FaIcon(FontAwesomeIcons.github, size: 18),
label: const Text('GitHub Sponsors'),
onPressed: () => launchURL(GITHUB_SPONSORS_URL, context),
), ),
], ],
), ),
),
_buildSectionSpacer(), _buildSectionSpacer(),
_buildSectionHeader(context, i18n.aboutJoinCommunityTitle), _buildSectionHeader(context, i18n.aboutWhySupportTitle),
ListTile( Text(i18n.aboutDescription),
leading: const FaIcon(FontAwesomeIcons.discord),
trailing: const Icon(Icons.arrow_outward),
title: Text(i18n.aboutDiscordTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(DISCORD_URL, context),
),
ListTile(
leading: const FaIcon(FontAwesomeIcons.mastodon),
trailing: const Icon(Icons.arrow_outward),
title: Text(i18n.aboutMastodonTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(MASTODON_URL, context),
),
_buildSectionSpacer(), _buildSectionSpacer(),
_buildSectionHeader(context, i18n.others), _buildSectionHeader(context, i18n.aboutContributeTitle),
Text(i18n.aboutContributeText),
ListTile(
leading: const Icon(Icons.bug_report),
title: Text(i18n.aboutBugsListTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(GITHUB_ISSUES_URL, context),
),
ListTile(
leading: const Icon(Icons.translate),
title: Text(i18n.aboutTranslationListTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(WEBLATE_URL, context),
),
ListTile(
leading: const Icon(Icons.code),
title: Text(i18n.aboutSourceListTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(GITHUB_PROJECT_URL, context),
),
ListTile(
leading: const FaIcon(FontAwesomeIcons.dumbbell, size: 18),
title: Text(i18n.contributeExercise),
contentPadding: EdgeInsets.zero,
onTap: () => Navigator.of(context).pushNamed(AddExerciseScreen.routeName),
),
ListTile( _buildSectionSpacer(),
leading: const Icon(Icons.article), _buildSectionHeader(context, i18n.aboutDonateTitle),
trailing: const Icon(Icons.chevron_right), Text(i18n.aboutDonateText),
title: Text(i18n.applicationLogs), const SizedBox(height: 15),
contentPadding: EdgeInsets.zero, // Using Wrap for buttons to handle different screen sizes potentially
onTap: () { Center(
Navigator.of(context).pushNamed(LogOverviewPage.routeName); child: Wrap(
}, spacing: 10.0,
), runSpacing: 10.0,
ListTile( alignment: WrapAlignment.center,
leading: const Icon(Icons.article), children: [
trailing: const Icon(Icons.chevron_right), ElevatedButton.icon(
title: const Text('View Licenses'), icon: const FaIcon(FontAwesomeIcons.mugHot, size: 18),
contentPadding: EdgeInsets.zero, label: const Text('Buy me a coffee'),
onTap: () { onPressed: () => launchURL(BUY_ME_A_COFFEE_URL, context),
showLicensePage(
context: context,
applicationName: 'wger',
applicationVersion:
'App: ${authProvider.applicationVersion?.version ?? 'N/A'} '
'Server: ${authProvider.serverVersion ?? 'N/A'}',
applicationLegalese: '\u{a9} ${today.year} wger contributors',
applicationIcon: Padding(
padding: const EdgeInsets.only(top: 10),
child: Image.asset(
'assets/images/logo.png',
width: 60,
semanticLabel: 'wger logo',
), ),
), ElevatedButton.icon(
); icon: const FaIcon(FontAwesomeIcons.solidHeart, size: 18),
}, label: const Text('Liberapay'),
), onPressed: () => launchURL(LIBERAPAY_URL, context),
], ),
ElevatedButton.icon(
icon: const FaIcon(FontAwesomeIcons.github, size: 18),
label: const Text('GitHub Sponsors'),
onPressed: () => launchURL(GITHUB_SPONSORS_URL, context),
),
],
),
),
_buildSectionSpacer(),
_buildSectionHeader(context, i18n.aboutJoinCommunityTitle),
ListTile(
leading: const FaIcon(FontAwesomeIcons.discord),
trailing: const Icon(Icons.arrow_outward),
title: Text(i18n.aboutDiscordTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(DISCORD_URL, context),
),
ListTile(
leading: const FaIcon(FontAwesomeIcons.mastodon),
trailing: const Icon(Icons.arrow_outward),
title: Text(i18n.aboutMastodonTitle),
contentPadding: EdgeInsets.zero,
onTap: () => launchURL(MASTODON_URL, context),
),
_buildSectionSpacer(),
_buildSectionHeader(context, i18n.others),
ListTile(
leading: const Icon(Icons.article),
trailing: const Icon(Icons.chevron_right),
title: Text(i18n.applicationLogs),
contentPadding: EdgeInsets.zero,
onTap: () {
Navigator.of(context).pushNamed(LogOverviewPage.routeName);
},
),
ListTile(
leading: const Icon(Icons.article),
trailing: const Icon(Icons.chevron_right),
title: const Text('View Licenses'),
contentPadding: EdgeInsets.zero,
onTap: () {
showLicensePage(
context: context,
applicationName: 'wger',
applicationVersion:
'App: ${authProvider.applicationVersion?.version ?? 'N/A'} '
'Server: ${authProvider.serverVersion ?? 'N/A'}',
applicationLegalese: '\u{a9} ${today.year} wger contributors',
applicationIcon: Padding(
padding: const EdgeInsets.only(top: 10),
child: Image.asset(
'assets/images/logo.png',
width: 60,
semanticLabel: 'wger logo',
),
),
);
},
),
],
),
), ),
), ),
); );

View File

@@ -18,6 +18,7 @@
//import 'package:drift/drift.dart'; //import 'package:drift/drift.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:wger/core/wide_screen_wrapper.dart';
import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/l10n/generated/app_localizations.dart';
import 'package:wger/screens/configure_plates_screen.dart'; import 'package:wger/screens/configure_plates_screen.dart';
import 'package:wger/widgets/core/settings/exercise_cache.dart'; import 'package:wger/widgets/core/settings/exercise_cache.dart';
@@ -35,23 +36,28 @@ class SettingsPage extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text(i18n.settingsTitle)), appBar: AppBar(title: Text(i18n.settingsTitle)),
body: ListView( body: WidescreenWrapper(
children: [ child: ListView(
ListTile( children: [
title: Text(i18n.settingsCacheTitle, style: Theme.of(context).textTheme.headlineSmall), ListTile(
), title: Text(
const SettingsExerciseCache(), i18n.settingsCacheTitle,
const SettingsIngredientCache(), style: Theme.of(context).textTheme.headlineSmall,
ListTile(title: Text(i18n.others, style: Theme.of(context).textTheme.headlineSmall)), ),
const SettingsTheme(), ),
ListTile( const SettingsExerciseCache(),
title: Text(i18n.selectAvailablePlates), const SettingsIngredientCache(),
onTap: () { ListTile(title: Text(i18n.others, style: Theme.of(context).textTheme.headlineSmall)),
Navigator.of(context).pushNamed(ConfigurePlatesScreen.routeName); const SettingsTheme(),
}, ListTile(
trailing: const Icon(Icons.chevron_right), title: Text(i18n.selectAvailablePlates),
), onTap: () {
], Navigator.of(context).pushNamed(ConfigurePlatesScreen.routeName);
},
trailing: const Icon(Icons.chevron_right),
),
],
),
), ),
); );
} }

View File

@@ -84,17 +84,17 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin
SPEC CHECKSUMS: SPEC CHECKSUMS:
file_selector_macos: 6280b52b459ae6c590af5d78fc35c7267a3c4b31 file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7
flutter_zxing: 91e9d17c79c60860450e8879cced0ec54f6a2601 flutter_zxing: 91e9d17c79c60860450e8879cced0ec54f6a2601
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
package_info_plus: f0052d280d17aa382b932f399edf32507174e870 package_info_plus: f0052d280d17aa382b932f399edf32507174e870
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
rive_common: ea79040f86acf053a2d5a75a2506175ee39796a5 rive_common: ea79040f86acf053a2d5a75a2506175ee39796a5
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1 sqlite3_flutter_libs: 83f8e9f5b6554077f1d93119fe20ebaa5f3a9ef1
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
PODFILE CHECKSUM: 0d3963a09fc94f580682bd88480486da345dc3f0 PODFILE CHECKSUM: 0d3963a09fc94f580682bd88480486da345dc3f0

View File

@@ -365,18 +365,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flex_color_scheme name: flex_color_scheme
sha256: "6e713c27a2ebe63393a44d4bf9cdd2ac81e112724a4c69905fc41cbf231af11d" sha256: ab854146f201d2d62cc251fd525ef023b84182c4a0bfe4ae4c18ffc505b412d3
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.3.1" version: "8.4.0"
flex_seed_scheme: flex_seed_scheme:
dependency: "direct main" dependency: "direct main"
description: description:
name: flex_seed_scheme name: flex_seed_scheme
sha256: "828291a5a4d4283590541519d8b57821946660ac61d2e07d955f81cfcab22e5d" sha256: a3183753bbcfc3af106224bff3ab3e1844b73f58062136b7499919f49f3667e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.6.1" version: "4.0.1"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@@ -1197,10 +1197,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: sqlite3_flutter_libs name: sqlite3_flutter_libs
sha256: "69c80d812ef2500202ebd22002cbfc1b6565e9ff56b2f971e757fac5d42294df" sha256: "1e800ebe7f85a80a66adacaa6febe4d5f4d8b75f244e9838a27cb2ffc7aec08d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.40" version: "0.5.41"
sqlparser: sqlparser:
dependency: transitive dependency: transitive
description: description:

View File

@@ -40,7 +40,7 @@ dependencies:
equatable: ^2.0.7 equatable: ^2.0.7
fl_chart: ^1.1.1 fl_chart: ^1.1.1
flex_color_scheme: ^8.3.1 flex_color_scheme: ^8.3.1
flex_seed_scheme: ^3.6.1 flex_seed_scheme: ^4.0.1
flutter_html: ^3.0.0 flutter_html: ^3.0.0
flutter_staggered_grid_view: ^0.7.0 flutter_staggered_grid_view: ^0.7.0
flutter_svg: ^2.2.3 flutter_svg: ^2.2.3
@@ -61,7 +61,7 @@ dependencies:
provider: ^6.1.5 provider: ^6.1.5
rive: ^0.13.20 rive: ^0.13.20
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
sqlite3_flutter_libs: ^0.5.40 sqlite3_flutter_libs: ^0.5.41
table_calendar: ^3.0.8 table_calendar: ^3.0.8
url_launcher: ^6.3.2 url_launcher: ^6.3.2
version: ^3.0.2 version: ^3.0.2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB