Merge pull request #378 from freddyc2003/master

[wger-project/flutter#306] Create text prompt for empty screens
This commit is contained in:
Roland Geider
2023-04-22 12:19:37 +02:00
committed by GitHub
5 changed files with 260 additions and 226 deletions

View File

@@ -665,5 +665,7 @@
"swiss_ball": "Swiss Ball",
"triceps": "Triceps",
"until_failure": "Until Failure",
"none__bodyweight_exercise_": "none (bodyweight exercise)"
"none__bodyweight_exercise_": "none (bodyweight exercise)",
"textPromptTitle": "Ready to start?",
"textPromptSubheading": "Press the action button to begin"
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class TextPrompt extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
AppLocalizations.of(context).textPromptTitle,
style: Theme.of(context).textTheme.headlineMedium,
),
Padding(
padding: EdgeInsets.only(top: 12),
child: Text(AppLocalizations.of(context).textPromptSubheading),
),
],
),
);
}
}

View File

@@ -24,6 +24,7 @@ import 'package:provider/provider.dart';
import 'package:wger/helpers/platform.dart';
import 'package:wger/providers/gallery.dart';
import 'package:wger/screens/form_screen.dart';
import 'package:wger/widgets/core/text_prompt.dart';
import 'forms.dart';
@@ -38,77 +39,79 @@ class Gallery extends StatelessWidget {
padding: const EdgeInsets.all(5),
child: RefreshIndicator(
onRefresh: () => provider.fetchAndSetGallery(),
child: MasonryGridView.count(
crossAxisCount: 2,
mainAxisSpacing: 5,
crossAxisSpacing: 5,
itemCount: provider.images.length,
itemBuilder: (context, index) {
final currentImage = provider.images[index];
child: provider.images.length == 0
? TextPrompt()
: MasonryGridView.count(
crossAxisCount: 2,
mainAxisSpacing: 5,
crossAxisSpacing: 5,
itemCount: provider.images.length,
itemBuilder: (context, index) {
final currentImage = provider.images[index];
return GestureDetector(
onTap: () {
showModalBottomSheet(
builder: (context) => Container(
key: Key('image-${currentImage.id}-detail'),
padding: const EdgeInsets.all(10),
color: Colors.white,
child: Column(
children: [
Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentImage.date),
style: Theme.of(context).textTheme.headline5,
),
Expanded(
child: Image.network(currentImage.url!),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(currentImage.description),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
Provider.of<GalleryProvider>(context, listen: false)
.deleteImage(currentImage);
Navigator.of(context).pop();
}),
if (!isDesktop)
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
ImageForm(currentImage),
hasListView: true,
),
);
},
return GestureDetector(
onTap: () {
showModalBottomSheet(
builder: (context) => Container(
key: Key('image-${currentImage.id}-detail'),
padding: const EdgeInsets.all(10),
color: Colors.white,
child: Column(
children: [
Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentImage.date),
style: Theme.of(context).textTheme.headline5,
),
],
)
],
Expanded(
child: Image.network(currentImage.url!),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(currentImage.description),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
Provider.of<GalleryProvider>(context, listen: false)
.deleteImage(currentImage);
Navigator.of(context).pop();
}),
if (!isDesktop)
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
Navigator.pushNamed(
context,
FormScreen.routeName,
arguments: FormScreenArguments(
AppLocalizations.of(context).edit,
ImageForm(currentImage),
hasListView: true,
),
);
},
),
],
)
],
),
),
context: context,
);
},
child: FadeInImage(
key: Key('image-${currentImage.id}'),
placeholder: const AssetImage('assets/images/placeholder.png'),
image: NetworkImage(currentImage.url!),
fit: BoxFit.cover,
),
),
context: context,
);
},
child: FadeInImage(
key: Key('image-${currentImage.id}'),
placeholder: const AssetImage('assets/images/placeholder.png'),
image: NetworkImage(currentImage.url!),
fit: BoxFit.cover,
);
},
),
);
},
),
),
);
}

View File

@@ -21,6 +21,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:intl/intl.dart';
import 'package:wger/providers/nutrition.dart';
import 'package:wger/screens/nutritional_plan_screen.dart';
import 'package:wger/widgets/core/text_prompt.dart';
class NutritionalPlansList extends StatelessWidget {
final NutritionPlansProvider _nutritionProvider;
@@ -31,87 +32,89 @@ class NutritionalPlansList extends StatelessWidget {
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () => _nutritionProvider.fetchAndSetAllPlansSparse(),
child: ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: _nutritionProvider.items.length,
itemBuilder: (context, index) {
final currentPlan = _nutritionProvider.items[index];
return Dismissible(
key: Key(currentPlan.id.toString()),
confirmDismiss: (direction) async {
// Delete workout from DB
final bool? res = await showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(currentPlan.description),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).errorColor),
),
onPressed: () {
// Confirmed, delete the workout
_nutritionProvider.deletePlan(currentPlan.id!);
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
child: _nutritionProvider.items.length == 0
? TextPrompt()
: ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: _nutritionProvider.items.length,
itemBuilder: (context, index) {
final currentPlan = _nutritionProvider.items[index];
return Dismissible(
key: Key(currentPlan.id.toString()),
confirmDismiss: (direction) async {
// Delete workout from DB
final bool? res = await showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(currentPlan.description),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
);
},
),
],
);
});
return res;
},
background: Container(
color: Theme.of(context).errorColor,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 4,
),
child: const Icon(
Icons.delete,
color: Colors.white,
),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).errorColor),
),
onPressed: () {
// Confirmed, delete the workout
_nutritionProvider.deletePlan(currentPlan.id!);
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
),
],
);
});
return res;
},
background: Container(
color: Theme.of(context).errorColor,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 4,
),
child: const Icon(
Icons.delete,
color: Colors.white,
),
),
direction: DismissDirection.endToStart,
child: Card(
child: ListTile(
onTap: () {
Navigator.of(context).pushNamed(
NutritionalPlanScreen.routeName,
arguments: currentPlan,
);
},
title: Text(currentPlan.getLabel(context)),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentPlan.creationDate),
),
),
),
);
},
),
direction: DismissDirection.endToStart,
child: Card(
child: ListTile(
onTap: () {
Navigator.of(context).pushNamed(
NutritionalPlanScreen.routeName,
arguments: currentPlan,
);
},
title: Text(currentPlan.getLabel(context)),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentPlan.creationDate),
),
),
),
);
},
),
);
}
}

View File

@@ -22,6 +22,7 @@ import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:wger/providers/workout_plans.dart';
import 'package:wger/screens/workout_plan_screen.dart';
import 'package:wger/widgets/core/text_prompt.dart';
class WorkoutPlansList extends StatelessWidget {
final WorkoutPlansProvider _workoutProvider;
@@ -32,88 +33,90 @@ class WorkoutPlansList extends StatelessWidget {
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () => _workoutProvider.fetchAndSetAllPlansSparse(),
child: ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: _workoutProvider.items.length,
itemBuilder: (context, index) {
final currentWorkout = _workoutProvider.items[index];
return Dismissible(
key: Key(currentWorkout.id.toString()),
confirmDismiss: (direction) async {
// Delete workout from DB
final res = await showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(currentWorkout.name),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).errorColor),
),
onPressed: () {
// Confirmed, delete the workout
Provider.of<WorkoutPlansProvider>(context, listen: false)
.deleteWorkout(currentWorkout.id!);
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
child: _workoutProvider.items.length == 0
? TextPrompt()
: ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: _workoutProvider.items.length,
itemBuilder: (context, index) {
final currentWorkout = _workoutProvider.items[index];
return Dismissible(
key: Key(currentWorkout.id.toString()),
confirmDismiss: (direction) async {
// Delete workout from DB
final res = await showDialog(
context: context,
builder: (BuildContext contextDialog) {
return AlertDialog(
content: Text(
AppLocalizations.of(context).confirmDelete(currentWorkout.name),
),
actions: [
TextButton(
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
onPressed: () => Navigator.of(contextDialog).pop(),
),
);
},
),
],
);
});
return res;
},
background: Container(
color: Theme.of(context).errorColor,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 4,
),
child: const Icon(
Icons.delete,
color: Colors.white,
),
),
direction: DismissDirection.endToStart,
child: Card(
child: ListTile(
onTap: () {
_workoutProvider.setCurrentPlan(currentWorkout.id!);
TextButton(
child: Text(
AppLocalizations.of(context).delete,
style: TextStyle(color: Theme.of(context).errorColor),
),
onPressed: () {
// Confirmed, delete the workout
Provider.of<WorkoutPlansProvider>(context, listen: false)
.deleteWorkout(currentWorkout.id!);
Navigator.of(context)
.pushNamed(WorkoutPlanScreen.routeName, arguments: currentWorkout);
},
title: Text(currentWorkout.name),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentWorkout.creationDate),
),
),
// Close the popup
Navigator.of(contextDialog).pop();
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context).successfullyDeleted,
textAlign: TextAlign.center,
),
),
);
},
),
],
);
});
return res;
},
background: Container(
color: Theme.of(context).errorColor,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 4,
),
child: const Icon(
Icons.delete,
color: Colors.white,
),
),
direction: DismissDirection.endToStart,
child: Card(
child: ListTile(
onTap: () {
_workoutProvider.setCurrentPlan(currentWorkout.id!);
Navigator.of(context)
.pushNamed(WorkoutPlanScreen.routeName, arguments: currentWorkout);
},
title: Text(currentWorkout.name),
subtitle: Text(
DateFormat.yMd(Localizations.localeOf(context).languageCode)
.format(currentWorkout.creationDate),
),
),
),
);
},
),
);
},
),
);
}
}