mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Merge pull request #378 from freddyc2003/master
[wger-project/flutter#306] Create text prompt for empty screens
This commit is contained in:
@@ -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"
|
||||
}
|
||||
|
||||
23
lib/widgets/core/text_prompt.dart
Normal file
23
lib/widgets/core/text_prompt.dart
Normal 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user