import 'dart:collection'; 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_image.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:ezanimation/ezanimation.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 'dialog_html.dart'; // ignore: must_be_immutable class MenuPageWidget extends StatefulWidget { int parent; MenuPageWidget({this.parent}); @override _MenuPageWidgetState createState() => _MenuPageWidgetState(); } class _MenuPageWidgetState extends State with Trans, Logging { final double baseWidth = 312; final double baseHeight = 675.2; MenuBloc menuBloc; final scrollController = ScrollController(); final bool activeExercisePlan = Cache().activeExercisePlan != null; final EzAnimation animation = EzAnimation(35.0, 10.0, Duration(seconds: 2), reverseCurve: Curves.linear); @override void initState() { if (activeExercisePlan) { animation.start(); animation.addStatusListener((status) { if (status == AnimationStatus.completed || status == AnimationStatus.dismissed) { animation.reverse(); } }); animation.addListener(() { //setState(() {}); }); } /// We require the initializers to run after the loading screen is rendered SchedulerBinding.instance.addPostFrameCallback((_) { menuBloc.add(MenuCreate()); }); super.initState(); } @override bool didUpdateWidget(MenuPageWidget oldWidget) { super.didUpdateWidget(oldWidget); scrollController.animateTo(40, duration: Duration(milliseconds: 300), curve: Curves.easeIn); return true; } @override Widget build(BuildContext context) { menuBloc = BlocProvider.of(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 buildMenuColumn(int parent, BuildContext context, MenuBloc menuBloc, double cWidth, double cHeight) { final List 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 _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(AppLocalizations.of(context).translate("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(AppLocalizations.of(context).translate("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" ? AppLocalizations.of(context).translate("Endurance_desc") : AppLocalizations.of(context).translate("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: 5, bottom: 5, right: 5), height: 80, child: Container( color: Colors.black.withOpacity(0.2), ), ), */ Container( padding: EdgeInsets.only(left: 15, bottom: 8, 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 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( AppLocalizations.of(context).translate("Equipment Filter"), textAlign: TextAlign.center, style: GoogleFonts.archivoBlack( fontSize: 24, color: Colors.white, shadows: [ 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 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: 4, mainAxisSpacing: 1.5, crossAxisSpacing: 1.5, childAspectRatio: 2.5, )); return sliverList; } SliverAppBar getInfoWidget(BuildContext context, MenuBloc menuBloc) { menuBloc.setContext(context); menuBloc.setMenuInfo(); SliverAppBar sliverAppBar = SliverAppBar( automaticallyImplyLeading: false, 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) { 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); } }, ), activeExercisePlan ? GestureDetector( onTap: () => showDialog( context: context, builder: (BuildContext context) { return DialogCommon( title: t("You have an active Test Set!"), descriptions: t("Press OK to continue"), text: "OK", onTap: () => { Navigator.of(context).pop(), Navigator.of(context).pushNamed("testSetExecute"), }, onCancel: () => { Navigator.of(context).pop(), }, ); }), child: AnimatedBuilder( animation: animation, builder: (context, snapshot) { return Center( child: Container( width: animation.value, height: animation.value, child: Image.asset("asset/image/pict_reps_volumen_db.png"), ), ); })) : Offstage(), SizedBox( width: 10, ), ])); return sliverAppBar; } void menuClick(WorkoutMenuTree workoutTree, MenuBloc menuBloc, BuildContext context) { 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.child == false) { if (ExerciseAbility.mini_test_set.equalsTo(menuBloc.ability) && workoutTree.parent != 0) { HashMap args = HashMap(); args['templateName'] = workoutTree.nameEnglish; args['templateNameTranslation'] = workoutTree.name; Navigator.of(context).pushNamed('testSetEdit', arguments: args); } menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id)); } else { menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id)); 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 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 = MenuImage(imageName: workoutMenuTree.imageName, workoutTreeId: workoutMenuTree.id); 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() { scrollController.dispose(); super.dispose(); } }