401 lines
15 KiB
Dart
401 lines
15 KiB
Dart
import 'dart:convert';
|
|
import 'dart:ui';
|
|
import 'package:aitrainer_app/model/exercise_ability.dart';
|
|
import 'package:aitrainer_app/bloc/menu/menu_bloc.dart';
|
|
import 'package:aitrainer_app/library/custom_icon_icons.dart';
|
|
import 'package:aitrainer_app/util/enums.dart';
|
|
import 'package:aitrainer_app/util/track.dart';
|
|
import 'package:aitrainer_app/widgets/menu_search_bar.dart';
|
|
import 'package:aitrainer_app/util/app_language.dart';
|
|
import 'package:aitrainer_app/util/app_localization.dart';
|
|
import 'package:aitrainer_app/model/cache.dart';
|
|
import 'package:aitrainer_app/model/workout_menu_tree.dart';
|
|
import 'package:aitrainer_app/service/logging.dart';
|
|
import 'package:aitrainer_app/util/trans.dart';
|
|
import 'package:aitrainer_app/widgets/dialog_common.dart';
|
|
import 'package:badges/badges.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/painting.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:transparent_image/transparent_image.dart';
|
|
import 'package:aitrainer_app/library/image_cache.dart' as wt;
|
|
|
|
import 'dialog_html.dart';
|
|
import 'menu_info_widget.dart';
|
|
|
|
// ignore: must_be_immutable
|
|
class MenuPageWidget extends StatefulWidget {
|
|
int parent;
|
|
|
|
MenuPageWidget({this.parent});
|
|
|
|
@override
|
|
_MenuPageWidgetState createState() => _MenuPageWidgetState();
|
|
}
|
|
|
|
class _MenuPageWidgetState extends State<MenuPageWidget> with Trans, Logging {
|
|
final double baseWidth = 312;
|
|
final double baseHeight = 675.2;
|
|
bool isFirst = true;
|
|
bool wait = false;
|
|
MenuBloc menuBloc;
|
|
final scrollController = ScrollController();
|
|
|
|
@override
|
|
void initState() {
|
|
isFirst = true;
|
|
|
|
/// We require the initializers to run after the loading screen is rendered
|
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
|
menuBloc.add(MenuCreate());
|
|
});
|
|
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
menuBloc = BlocProvider.of<MenuBloc>(context);
|
|
setContext(context);
|
|
double cWidth = MediaQuery.of(context).size.width;
|
|
double cHeight = MediaQuery.of(context).size.height;
|
|
|
|
return CustomScrollView(
|
|
// Must add scrollController to sliver root
|
|
controller: scrollController,
|
|
scrollDirection: Axis.vertical,
|
|
slivers: buildMenuColumn(widget.parent, context, menuBloc, cWidth, cHeight));
|
|
}
|
|
|
|
List<Widget> buildMenuColumn(int parent, BuildContext context, MenuBloc menuBloc, double cWidth, double cHeight) {
|
|
final List<Widget> slivers = List();
|
|
|
|
bool isChild = menuBloc.menuTreeRepository.isChildAndGym(menuBloc.parent);
|
|
if (!isChild) {
|
|
slivers.add(getInfoWidget(context, menuBloc));
|
|
} else {
|
|
slivers.add(getFilterWidget(parent, menuBloc));
|
|
slivers.add(getFilterElements(menuBloc));
|
|
}
|
|
|
|
final List<Widget> _columnChildren = List();
|
|
|
|
if (menuBloc.getFilteredBranch(menuBloc.parent).isEmpty) {
|
|
_columnChildren.add(Container(
|
|
padding: EdgeInsets.only(top: 15.0),
|
|
child: Center(
|
|
child: Stack(alignment: Alignment.bottomLeft, children: [
|
|
Text(t("All Exercises has been filtered out"), style: GoogleFonts.inter(color: Colors.white)),
|
|
]))));
|
|
} else {
|
|
menuBloc.getFilteredBranch(menuBloc.parent).forEach((treeName, value) {
|
|
WorkoutMenuTree workoutTree = value;
|
|
_columnChildren.add(Container(
|
|
padding: EdgeInsets.only(top: 15.0, left: cWidth * 0.04, right: cWidth * 0.04),
|
|
//height: (cHeight / 3) - cWidth * 0.16,
|
|
child: Badge(
|
|
padding: EdgeInsets.all(8),
|
|
position: BadgePosition.bottomEnd(end: 0),
|
|
animationDuration: Duration(milliseconds: 500),
|
|
animationType: BadgeAnimationType.slide,
|
|
badgeColor: Colors.orange[800],
|
|
showBadge: workoutTree.base,
|
|
badgeContent: Text(t("base"),
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 12,
|
|
)),
|
|
child: Badge(
|
|
showBadge: workoutTree.nameEnglish == "One Rep Max" || workoutTree.nameEnglish == "Endurance",
|
|
animationDuration: Duration(milliseconds: 800),
|
|
animationType: BadgeAnimationType.fade,
|
|
badgeColor: Colors.blue[100],
|
|
badgeContent: GestureDetector(
|
|
onTap: () => {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return DialogHTML(
|
|
title: workoutTree.name,
|
|
htmlData: workoutTree.nameEnglish == "Endurance" ? t("Endurance_desc") : t("OneRepMax_desc"),
|
|
);
|
|
})
|
|
},
|
|
child: Icon(Icons.info_outline_rounded)),
|
|
child: Stack(alignment: Alignment.bottomLeft, children: [
|
|
FlatButton(
|
|
child: badgedIcon(workoutTree, cWidth, cHeight),
|
|
padding: EdgeInsets.only(left: 0.0, bottom: 0),
|
|
onPressed: () => menuClick(workoutTree, menuBloc, context),
|
|
),
|
|
Container(
|
|
padding: EdgeInsets.only(left: 15, bottom: 15, right: 15),
|
|
child: GestureDetector(
|
|
onTap: () => menuClick(workoutTree, menuBloc, context),
|
|
child: Text(
|
|
workoutTree.name,
|
|
maxLines: 4,
|
|
style: GoogleFonts.archivoBlack(color: workoutTree.color, fontSize: workoutTree.fontSize, height: 1.1),
|
|
),
|
|
),
|
|
),
|
|
])))));
|
|
});
|
|
}
|
|
/* LiveSliverList sliverList = LiveSliverList(
|
|
// And attach root sliver scrollController to widgets
|
|
controller: scrollController,
|
|
|
|
itemCount: _columnChildren.length,
|
|
reAnimateOnVisibility: false,
|
|
showItemDuration: Duration(milliseconds: 100),
|
|
itemBuilder: (BuildContext context, int index, Animation<double> animation) => FadeTransition(
|
|
opacity: animation,
|
|
child: _columnChildren[index],
|
|
),
|
|
*/
|
|
//delegate: SliverChildListDelegate(_columnChildren),
|
|
|
|
SliverList sliverList = SliverList(
|
|
delegate: SliverChildListDelegate(_columnChildren),
|
|
);
|
|
slivers.add(sliverList);
|
|
return slivers;
|
|
}
|
|
|
|
SliverGrid getFilterWidget(int parent, MenuBloc menuBloc) {
|
|
SliverGrid sliverList = SliverGrid(
|
|
delegate: SliverChildListDelegate([
|
|
Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [
|
|
Text(
|
|
t("Equipment Filter"),
|
|
textAlign: TextAlign.center,
|
|
style: GoogleFonts.archivoBlack(
|
|
fontSize: 24,
|
|
color: Colors.white,
|
|
shadows: <Shadow>[
|
|
Shadow(
|
|
offset: Offset(5.0, 5.0),
|
|
blurRadius: 12.0,
|
|
color: Colors.black54,
|
|
),
|
|
Shadow(
|
|
offset: Offset(-3.0, 3.0),
|
|
blurRadius: 12.0,
|
|
color: Colors.black54,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
])
|
|
]),
|
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 1,
|
|
mainAxisSpacing: 5.0,
|
|
crossAxisSpacing: 5.0,
|
|
childAspectRatio: 9,
|
|
));
|
|
|
|
return sliverList;
|
|
}
|
|
|
|
SliverGrid getFilterElements(MenuBloc menuBloc) {
|
|
List<Widget> list = List();
|
|
|
|
menuBloc.exerciseDeviceRepository.getGymDevices().forEach((element) {
|
|
String deviceName = AppLanguage().appLocal == Locale('en') ? element.name : element.nameTranslation;
|
|
ChoiceChip chip = ChoiceChip(
|
|
labelPadding: EdgeInsets.only(right: 5),
|
|
avatar: Icon(
|
|
Icons.remove_circle_outline,
|
|
color: Colors.orange,
|
|
size: 18,
|
|
),
|
|
label: Text(deviceName),
|
|
labelStyle: TextStyle(fontSize: 9, color: Colors.black),
|
|
selectedColor: Colors.white,
|
|
selected: menuBloc.selectedDevice(element.exerciseDeviceId),
|
|
backgroundColor: Colors.blue[100],
|
|
shadowColor: Colors.black54,
|
|
onSelected: (value) => menuBloc.add(MenuFilterExerciseType(deviceId: element.exerciseDeviceId)),
|
|
);
|
|
list.add(chip);
|
|
});
|
|
|
|
SliverGrid sliverList = SliverGrid(
|
|
delegate: SliverChildListDelegate(list),
|
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 3,
|
|
mainAxisSpacing: 1.0,
|
|
crossAxisSpacing: 1.0,
|
|
childAspectRatio: 3,
|
|
));
|
|
return sliverList;
|
|
}
|
|
|
|
SliverAppBar getInfoWidget(BuildContext context, MenuBloc menuBloc) {
|
|
menuBloc.setContext(context);
|
|
menuBloc.setMenuInfo();
|
|
|
|
SliverAppBar sliverAppBar = SliverAppBar(
|
|
backgroundColor: Colors.transparent,
|
|
title: Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
|
|
SizedBox(
|
|
width: 10,
|
|
),
|
|
GestureDetector(
|
|
onTap: () => {
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return DialogCommon(
|
|
title: menuBloc.infoTitle,
|
|
descriptions: menuBloc.infoText,
|
|
description2: menuBloc.infoText2,
|
|
description3: menuBloc.infoText3,
|
|
text: "OK",
|
|
onTap: () => {Navigator.of(context).pop()},
|
|
onCancel: () => {Navigator.of(context).pop()},
|
|
);
|
|
})
|
|
},
|
|
child: Icon(
|
|
CustomIcon.question_circle,
|
|
color: Colors.orange[400],
|
|
size: 40,
|
|
)),
|
|
MenuSearchBar(
|
|
listItems: menuBloc.menuTreeRepository.menuAsExercise,
|
|
onFind: (value) {
|
|
print("onFind: ${value.toJson()}");
|
|
if (Cache().userLoggedIn == null) {
|
|
Scaffold.of(context).showSnackBar(SnackBar(
|
|
backgroundColor: Colors.orange,
|
|
content: Text(AppLocalizations.of(context).translate('Please log in'), style: TextStyle(color: Colors.white))));
|
|
} else {
|
|
Track().track(TrackingEvent.search, eventValue: value.exerciseType.name);
|
|
menuBloc.ability = ExerciseAbility.oneRepMax;
|
|
Navigator.of(context).pushNamed('exerciseNewPage', arguments: value.exerciseType);
|
|
}
|
|
},
|
|
),
|
|
SizedBox(
|
|
width: 10,
|
|
),
|
|
]));
|
|
return sliverAppBar;
|
|
}
|
|
|
|
void menuClick(WorkoutMenuTree workoutTree, MenuBloc menuBloc, BuildContext context) {
|
|
if (workoutTree.child == false) {
|
|
menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id));
|
|
} else {
|
|
menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id));
|
|
if (Cache().userLoggedIn == null) {
|
|
Scaffold.of(context).showSnackBar(SnackBar(
|
|
backgroundColor: Colors.orange,
|
|
content: Text(AppLocalizations.of(context).translate('Please log in'), style: TextStyle(color: Colors.white))));
|
|
} else {
|
|
if (workoutTree.exerciseType.name == "Custom" && Cache().userLoggedIn.admin == 1) {
|
|
Navigator.of(context).pushNamed('exerciseCustomPage', arguments: workoutTree.exerciseType);
|
|
} else {
|
|
Navigator.of(context).pushNamed('exerciseNewPage', arguments: workoutTree.exerciseType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
dynamic getShape(WorkoutMenuTree workoutTree) {
|
|
bool base = workoutTree.base;
|
|
dynamic returnCode = (base == true)
|
|
? RoundedRectangleBorder(
|
|
side: BorderSide(width: 6, color: Colors.orangeAccent), borderRadius: BorderRadius.all(Radius.circular(24.0)))
|
|
: RoundedRectangleBorder(
|
|
side: BorderSide(width: 1, color: Colors.transparent), borderRadius: BorderRadius.all(Radius.circular(8.0)));
|
|
return returnCode;
|
|
}
|
|
|
|
Widget _getButtonImage(WorkoutMenuTree workoutTree, double cWidth, double cHeight) {
|
|
//print("_getButtonImage " + workoutTree.imageName);
|
|
String imageString = menuBloc.getImage(workoutTree.id, workoutTree.imageName);
|
|
Widget widget;
|
|
if (imageString != null) {
|
|
print(" -- get Image from MEMORY " + workoutTree.imageName);
|
|
widget = ClipRRect(
|
|
borderRadius: BorderRadius.circular(24.0),
|
|
child: Container(
|
|
color: Colors.transparent,
|
|
child: FadeInImage(
|
|
fadeInDuration: Duration(milliseconds: 100),
|
|
image: MemoryImage(base64Decode(imageString)),
|
|
placeholder: MemoryImage(kTransparentImage),
|
|
),
|
|
));
|
|
} else {
|
|
if (workoutTree.imageName.contains("https")) {
|
|
if (!wt.ImageCache().existsImageInMap(workoutTree.id, workoutTree.imageName)) {
|
|
print(" -- get Image from network " + workoutTree.imageName);
|
|
widget = ClipRRect(
|
|
borderRadius: BorderRadius.circular(24.0),
|
|
child: Container(
|
|
color: Colors.transparent,
|
|
child: FadeInImage(
|
|
fadeInDuration: Duration(milliseconds: 500),
|
|
image: NetworkImage(workoutTree.imageName),
|
|
placeholder: MemoryImage(kTransparentImage),
|
|
),
|
|
));
|
|
}
|
|
} else {
|
|
//print(" -- get Image from asset " + workoutTree.imageName);
|
|
widget = ClipRRect(
|
|
borderRadius: BorderRadius.circular(24.0),
|
|
child: Container(
|
|
color: Colors.transparent,
|
|
child: Image.asset(workoutTree.imageName),
|
|
/* FadeInImage(
|
|
fadeInDuration: Duration(milliseconds: 50),
|
|
image: AssetImage(workoutTree.imageName),
|
|
placeholder: MemoryImage(kTransparentImage),
|
|
), */
|
|
));
|
|
}
|
|
}
|
|
return widget;
|
|
}
|
|
|
|
Widget badgedIcon(WorkoutMenuTree workoutMenuTree, double cWidth, double cHeight) {
|
|
String badgeKey = workoutMenuTree.nameEnglish;
|
|
bool show = Cache().getBadges()[badgeKey] != null;
|
|
int counter = Cache().getBadges()[badgeKey] != null ? Cache().getBadges()[badgeKey] : 0;
|
|
Widget buttonImage = _getButtonImage(workoutMenuTree, cWidth, cHeight);
|
|
return Badge(
|
|
padding: EdgeInsets.all(8),
|
|
position: BadgePosition.topEnd(top: 3, end: 3),
|
|
animationDuration: Duration(milliseconds: 500),
|
|
animationType: BadgeAnimationType.slide,
|
|
badgeColor: Colors.red,
|
|
showBadge: show,
|
|
badgeContent: Text(counter.toString(),
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 16,
|
|
)),
|
|
child: buttonImage == null
|
|
? Container(
|
|
color: Colors.transparent,
|
|
)
|
|
: buttonImage,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
}
|
|
}
|