diff --git a/asset/image/Congrats_N1.jpg b/asset/image/Congrats_N1.jpg new file mode 100644 index 0000000..12bf82e Binary files /dev/null and b/asset/image/Congrats_N1.jpg differ diff --git a/asset/image/development_muscles.jpg b/asset/image/development_muscles.jpg new file mode 100644 index 0000000..c2a86b2 Binary files /dev/null and b/asset/image/development_muscles.jpg differ diff --git a/asset/image/exercise_log.jpg b/asset/image/exercise_log.jpg new file mode 100644 index 0000000..b2833db Binary files /dev/null and b/asset/image/exercise_log.jpg differ diff --git a/asset/menu/Back_pullup.png b/asset/menu/Back_pullup.png new file mode 100644 index 0000000..3b3f21b Binary files /dev/null and b/asset/menu/Back_pullup.png differ diff --git a/asset/menu/biceps.jpg b/asset/menu/biceps.jpg new file mode 100644 index 0000000..b70b552 Binary files /dev/null and b/asset/menu/biceps.jpg differ diff --git a/asset/menu/cable triceps.png b/asset/menu/cable triceps.png new file mode 100644 index 0000000..fe1968e Binary files /dev/null and b/asset/menu/cable triceps.png differ diff --git a/asset/menu/calf.png b/asset/menu/calf.png new file mode 100644 index 0000000..e096ec3 Binary files /dev/null and b/asset/menu/calf.png differ diff --git a/asset/menu/legpress.jpg b/asset/menu/legpress.jpg new file mode 100644 index 0000000..70d9160 Binary files /dev/null and b/asset/menu/legpress.jpg differ diff --git a/asset/menu/shoulder press.png b/asset/menu/shoulder press.png new file mode 100644 index 0000000..0d74ea8 Binary files /dev/null and b/asset/menu/shoulder press.png differ diff --git a/asset/menu/squat.jpg b/asset/menu/squat.jpg new file mode 100644 index 0000000..a004bbb Binary files /dev/null and b/asset/menu/squat.jpg differ diff --git a/asset/menu/tricdip.jpg b/asset/menu/tricdip.jpg new file mode 100644 index 0000000..e2ef396 Binary files /dev/null and b/asset/menu/tricdip.jpg differ diff --git a/i18n/en.json b/i18n/en.json index a75eadf..009cf5a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -170,5 +170,29 @@ "Custom Exercise Plan": "Custom Exercise Plan", "Select manually the exercises what you would like to have in your plan. At the end don't forget to save.": "Select manually the exercises what you would like to have in your plan. At the end don't forget to save.", - "In this list you will find all your executed exercises grouped by the date.": "In this list you will find all your executed exercises grouped by the date." + "In this list you will find all your executed exercises grouped by the date.": "In this list you will find all your executed exercises grouped by the date.", + + "Persistence!": "Persistence!", + "Greetings!": "Greetings!", + "The purpose is to measure you physical condition": "The purpose is to measure you physical condition.", + "The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf.": "The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf.", + "Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise.": "Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise.", + "Please continue your tests with a": "Please continue your tests with a", + "I suggest begin your tests with a": "I suggest begin your tests with a 'chest' exercise", + "Nice! This is a good start": "Nice! This is a good start", + "Go on!":"Go on!", + "You are on track": "You are on track", + "Not so much left": "Not so much left", + "Almost!": "Almost done!", + "You have only 1-2 exercise left to finish!": "You have only 1-2 exercise left to finish!", + "exercise!": "exercise!", + "Chest": "Chest", + "Back": "Back", + "Thigh": "Thigh", + "Calf": "Calf", + + "Bring me there": "Bring me there", + + "My Body Development": "My Body Development", + "You see here your whole body development by muscle groups.": "You see here your whole body development by muscle groups." } \ No newline at end of file diff --git a/i18n/hu.json b/i18n/hu.json index 4e4b956..db9f2ee 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -169,5 +169,29 @@ "Select the muscle type and tap on the exercise. One the next page enter the weight and repeat.": "Válaszd ki az izomcsoporton belül a gyakorlatot, és a következő oldalon add meg a súlyt és az ismétlés számot.", "Custom Exercise Plan": "Egyedi edzésterv", "Select manually the exercises what you would like to have in your plan. At the end don't forget to save.": "Válaszd ki a gyakorlatokat, amelyeket szeretnél végrehajtani a tervedben. Utána ne felejtsd el elmenteni.", - "In this list you will find all your executed exercises grouped by the date.": "Ebben a listában találod az eddig végrehajtott gyakorlataid dátum szerint csoportosítva." + "In this list you will find all your executed exercises grouped by the date.": "Ebben a listában találod az eddig végrehajtott gyakorlataid dátum szerint csoportosítva.", + + "Persistence!": "Kitartás!", + "Greetings!": "Üdvözöllek!", + "The purpose is to measure you physical condition": "A cél a jelenlegi fizikai kondíciód felmérése.", + "The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf.": "A gyakorlatok javasolt végrehajtási sorrendje: mell - bicepsz - tricepsz - hát - váll - has - comb - vádli.", + "Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise.": "Menj a menüben az Erő - Max Erő - Mell menüpontba, és válaszd ki a kedvenc gyakorlatod.", + "Please continue your tests with a": "Kérlek folytasd a gyakorlatokat egy", + "I suggest begin your tests with a": "Azt javaslom, kezdd a teszteket egy 'mell' gyakorlattal", + "Nice! This is a good start": "Nagyon jó! Ez egy kitűnő start a teszthez", + "Go on!":"Gyerünk tovább!", + "You are on track": "Jó úton haladsz", + "Not so much left": "Már nincs sok hátra", + "Almost!": "Majdnem a végén vagy!", + "You have only 1-2 exercise left to finish!": "Már csak 1-2 gyakorlat van a végéig!", + "exercise!": "gyakorlattal!", + "Chest": "mell", + "Back": "hát", + "Thigh": "comb", + "Calf": "vádli", + + "Bring me there": "Vigyél oda", + + "My Body Development": "Testem fejlődése", + "You see here your whole body development by muscle groups.": "Itt láthatod a tested fejlődését izomcsoportonként" } \ No newline at end of file diff --git a/lib/bloc/body_development/body_development_bloc.dart b/lib/bloc/body_development/body_development_bloc.dart new file mode 100644 index 0000000..792358e --- /dev/null +++ b/lib/bloc/body_development/body_development_bloc.dart @@ -0,0 +1,43 @@ +import 'dart:async'; +import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:aitrainer_app/repository/workout_tree_repository.dart'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:meta/meta.dart'; + +part 'body_development_event.dart'; + +part 'body_development_state.dart'; + +class BodyDevelopmentBloc extends Bloc { + final WorkoutTreeRepository workoutTreeRepository; + final ExerciseRepository exerciseRepository = ExerciseRepository(); + + List radarTicks = List(); + List radarFeatures = List(); + List> radarData = List(); + + @override + BodyDevelopmentBloc({this.workoutTreeRepository}): super(BodyDevelopmentInitial()); + + Future getData() async{ + radarTicks = [20, 40, 60, 80, 100]; + radarFeatures = ["Mell", "Bicepsz", "Tricepsz", "Hát", "Váll"]; + radarData = [ + [80, 95, 45, 67, 83] + ]; + } + + @override + Stream mapEventToState(BodyDevelopmentEvent event) async* { + try { + if (event is BodyDevelopmentLoad) { + yield BodyDevelopmentLoading(); + await getData(); + yield BodyDevelopmentReady(); + } + } on Exception catch (e) { + yield BodyDevelopmentError(message: e.toString()); + } + } +} diff --git a/lib/bloc/body_development/body_development_event.dart b/lib/bloc/body_development/body_development_event.dart new file mode 100644 index 0000000..7e55235 --- /dev/null +++ b/lib/bloc/body_development/body_development_event.dart @@ -0,0 +1,12 @@ +part of 'body_development_bloc.dart'; + +@immutable +abstract class BodyDevelopmentEvent extends Equatable{ + const BodyDevelopmentEvent(); + @override + List get props => []; +} + +class BodyDevelopmentLoad extends BodyDevelopmentEvent { + const BodyDevelopmentLoad(); +} diff --git a/lib/bloc/body_development/body_development_state.dart b/lib/bloc/body_development/body_development_state.dart new file mode 100644 index 0000000..8f5b881 --- /dev/null +++ b/lib/bloc/body_development/body_development_state.dart @@ -0,0 +1,30 @@ +part of 'body_development_bloc.dart'; + +@immutable +abstract class BodyDevelopmentState extends Equatable { + const BodyDevelopmentState(); + + @override + List get props => []; +} + +class BodyDevelopmentInitial extends BodyDevelopmentState { + const BodyDevelopmentInitial(); +} + +class BodyDevelopmentLoading extends BodyDevelopmentState { + const BodyDevelopmentLoading(); +} + +class BodyDevelopmentReady extends BodyDevelopmentState { + const BodyDevelopmentReady(); +} + +class BodyDevelopmentError extends BodyDevelopmentState { + final String message; + const BodyDevelopmentError({this.message}); + + @override + List get props => [message]; +} + diff --git a/lib/bloc/customer_change_form_bloc.dart b/lib/bloc/customer_change_form_bloc.dart index 0ccac4b..4c497bf 100644 --- a/lib/bloc/customer_change_form_bloc.dart +++ b/lib/bloc/customer_change_form_bloc.dart @@ -95,6 +95,7 @@ class CustomerChangeFormBloc extends FormBloc { birthYearField.close(); weightField.close(); genderField.close(); + goalField.close(); return super.close(); } diff --git a/lib/bloc/development_by_muscle/development_by_muscle_bloc.dart b/lib/bloc/development_by_muscle/development_by_muscle_bloc.dart index cb13553..9a69727 100644 --- a/lib/bloc/development_by_muscle/development_by_muscle_bloc.dart +++ b/lib/bloc/development_by_muscle/development_by_muscle_bloc.dart @@ -8,6 +8,7 @@ import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/repository/workout_tree_repository.dart'; import 'package:aitrainer_app/util/calculate.dart'; import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/util/group_data.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; @@ -20,19 +21,240 @@ part 'development_by_muscle_event.dart'; part 'development_by_muscle_state.dart'; -class DateRate { - static String daily = "daily"; - static String weekly = "weekly"; - static String monthly = "monthly"; - static String yearly = "yearly"; -} - class DiagramType { static String sumMass = "sumMass"; static String oneRepMax = "oneRepMax"; static String percent = "percent"; } +/* + =========== GROUPDATE CLASS + */ + +class GroupDate extends GroupData with Calculate, Common { + final List inputList; + final List outputList; + + String _origDatePart; + int _origExerciseTypeId; + Exercise _origExercise; + + double _sumQuantity; + double _maxQuantity; + int _countExercises; + + String diagramType; + String dateRate; + + GroupDate({this.inputList, this.outputList}); + + + + double getQuantityByDate(Exercise exercise) { + double sum = 0; + if ( this.diagramType == DiagramType.sumMass ) { + if ( exercise.unitQuantity != null ) { + sum = exercise.quantity * exercise.unitQuantity; + } else { + sum = exercise.quantity; + } + } else if ( this.diagramType == DiagramType.oneRepMax || this.diagramType == DiagramType.percent ) { + if ( exercise.unitQuantity != null ) { + sum = calculate1RM(exercise.quantity, exercise.unitQuantity); + } else { + sum = exercise.quantity; + } + } + return sum; + } + + @override + void addTempData(Exercise exercise) { + double newQuantity = getQuantityByDate(exercise); + _sumQuantity += newQuantity; + if (_maxQuantity < newQuantity) { + _maxQuantity = newQuantity; + } + _countExercises++; + _origDatePart = getDatePart(exercise.dateAdd, dateRate); + _origExerciseTypeId = exercise.exerciseTypeId; + _origExercise = exercise; + } + + @override + bool checkNewType(Exercise exercise) { + String exerciseDatePart = getDatePart(exercise.dateAdd, dateRate); + return _origDatePart != exerciseDatePart || _origExerciseTypeId != exercise.exerciseTypeId; + } + + @override + void iteration() { + this.resetTemp(); + Exercise tempExercise; + inputList.forEach((element) { + tempExercise = element; + if ( this.checkNewType(element)) { + if ( _origDatePart == null ) { + this.addTempData(element); + } else { + this.temp2Output(_origExercise); + this.resetTemp(); + this.addTempData(element); + } + } else { + this.addTempData(element); + } + }); + if ( tempExercise != null) { + this.temp2Output(tempExercise); + } + } + + @override + void temp2Output(Exercise exercise) { + Exercise newExercise = exercise.copy(); + newExercise.datePart = _origDatePart; + if (this.diagramType == DiagramType.oneRepMax || this.diagramType == DiagramType.percent) { + newExercise.calculated = _maxQuantity; + } else { + newExercise.calculated = _sumQuantity / _countExercises; + } + outputList.add(newExercise); + } + + @override + void resetTemp() { + _countExercises = 0; + _sumQuantity = 0; + _maxQuantity = 0; + } +} + +/* + =========== CHART DATA CLASS + */ +class GroupChart extends GroupData with Calculate { + + final List inputList; + LinkedHashMap outputList = LinkedHashMap(); + + int _origExerciseTypeId; + double _minData = 999999999999; + double _maxData = 0; + List _chartData; + double _basePercent; + + String diagramType = DiagramType.sumMass; + + GroupChart({this.inputList, this.outputList}); + + double getBasePercent(Exercise exercise) { + if ( exercise.unitQuantity != null ) { + this._basePercent = calculate1RM(exercise.quantity, exercise.unitQuantity); + } else { + this._basePercent = exercise.quantity; + } + return this._basePercent; + } + + double getDiagramValue(Exercise exercise) { + double value = 0; + + if ( diagramType == DiagramType.percent) { + if ( exercise.unitQuantity != null ) { + double oneRepMax = calculate1RM(exercise.quantity, exercise.unitQuantity); + value = (oneRepMax / this._basePercent) * 100; + } else { + value = (exercise.quantity / this._basePercent ) * 100; + } + } else if ( diagramType == DiagramType.oneRepMax || diagramType == DiagramType.sumMass) { + value = exercise.calculated; + } + return value; + } + + @override + void addTempData(Exercise exercise) { + + double diagramValue = this.getDiagramValue(exercise); + + if (_maxData < diagramValue) { + _maxData = diagramValue; + } + if (_minData > diagramValue) { + _minData = diagramValue; + } + + BarChartGroupData data = BarChartGroupData( + x: exercise.dateAdd.millisecondsSinceEpoch, + barRods: [ + BarChartRodData(y: diagramValue, width: 12, color: Colors.lightBlue) + ] + ); + _chartData.add(data); + _origExerciseTypeId = exercise.exerciseTypeId; + + } + + @override + bool checkNewType(Exercise exercise) { + return _origExerciseTypeId != exercise.exerciseTypeId; + } + + @override + void iteration() { + + this.resetTemp(); + Exercise tempExercise; + inputList.forEach((element) { + tempExercise = element; + + if ( this.checkNewType(element)) { + if ( _origExerciseTypeId == null ) { + _basePercent = getBasePercent(element); + this.addTempData(element); + } else { + this.temp2Output(element); + this.resetTemp(); + _basePercent = getBasePercent(element); + this.addTempData(element); + } + } else { + this.addTempData(element); + } + }); + if ( tempExercise != null) { + this.temp2Output(tempExercise); + } + } + + @override + void temp2Output(Exercise exercise) { + double dInterval = ((_maxData + _minData) / 8).roundToDouble(); + int gridInterval = (dInterval / 3).floor(); + + ChartDataExtended extended; + if ( outputList[_origExerciseTypeId] == null ) { + extended = ChartDataExtended( + data: _chartData, interval: dInterval, gridInterval: gridInterval); + outputList[_origExerciseTypeId] = extended; + } else { + extended = outputList[_origExerciseTypeId]; + _chartData.forEach((element) { + extended.data.add(element); + }); + } + } + + @override + void resetTemp() { + _minData = 999999999999; + _maxData = 0; + _chartData = List(); + } + +} + class ChartDataExtended { final List data; final double interval; @@ -40,16 +262,29 @@ class ChartDataExtended { const ChartDataExtended({this.data, this.interval, this.gridInterval}); - Map toJson() => - { - "data": data.toString(), - "interval" : interval.toString(), - "gridInterval" : gridInterval, + Map toJson() { + List listBarChartData = List(); + data.forEach((element) { + element.barRods.forEach((rods) { + var barChartData = { + 'x': element.x, + 'y': rods.y, + }; + listBarChartData.add(barChartData); + }); + + }); + var chartData = { + "data": listBarChartData.toString(), + "interval": interval.toString(), + "gridInterval": gridInterval, }; + return chartData; + } } -class DevelopmentByMuscleBloc extends Bloc with Calculate, Common { +class DevelopmentByMuscleBloc extends Bloc with Calculate { final WorkoutTreeRepository workoutTreeRepository; final ExerciseRepository exerciseRepository = ExerciseRepository(); LinkedHashMap listChartData = LinkedHashMap(); @@ -81,57 +316,21 @@ class DevelopmentByMuscleBloc extends Bloc exercises = exerciseRepository.getExerciseList(); - exercises.sort( (a, b) => a.exerciseTypeId.compareTo(b.exerciseTypeId)); + + //print("-- Start calculate --- "); exercises = this.groupByDate(exercises); - int origExerciseTypeId = 0; - int x = 0; - chartData = List(); - double maxData = 0; - double minData = 9999999999999; - exercises.forEach((exercise) { - if ( exercise.unitQuantity != null ) { - if (origExerciseTypeId != exercise.exerciseTypeId && diagramType == DiagramType.percent) { - this.basePercent = getBasePercent(exercise); - } - double diagramValue = this.getDiagramValue(exercise); + exercises = sort(exercises, true); +/* exercises.forEach((exercise) { + print ("Chart exercise " + exercise.toJsonDatePart().toString()); + });*/ + + listChartData = LinkedHashMap(); + GroupChart groupChart = GroupChart(inputList: exercises, outputList: listChartData); + groupChart.diagramType = this.diagramType; + groupChart.iteration(); + listChartData = groupChart.outputList; - if (maxData < diagramValue) { - maxData = diagramValue; - } - if (minData > diagramValue) { - minData = diagramValue; - } - //print("exercise " + exercise.exerciseTypeId.toString() + " d " + getDateFormat(exercise.dateAdd) + " mass " + sumMass.toString()); - //print("date " + exercise.dateAdd.toIso8601String() + " epoch " + exercise.dateAdd.millisecondsSinceEpoch.toString() ); - BarChartGroupData data = BarChartGroupData( - x: exercise.dateAdd.millisecondsSinceEpoch, - barRods: [ - BarChartRodData(y: diagramValue, width: 12, color: Colors.lightBlue) - ] - ); - if (origExerciseTypeId != exercise.exerciseTypeId) { - if (origExerciseTypeId == 0) { - origExerciseTypeId = exercise.exerciseTypeId; - chartData.add(data); - } else { - double dInterval = ((maxData + minData) / 8).roundToDouble(); - int gridInterval = (dInterval / 3).floor(); - ChartDataExtended extended = ChartDataExtended( - data: chartData, interval: dInterval, gridInterval: gridInterval); - listChartData[origExerciseTypeId] = extended; - maxData = 0; - minData = 9999999999999; - chartData = List(); - chartData.add(data); - origExerciseTypeId = exercise.exerciseTypeId; - } - } else { - origExerciseTypeId = exercise.exerciseTypeId; - chartData.add(data); - } - } - }); listChartData.forEach((key, value) { print ("typeid " + key.toString() + " chardata " + value.toJson().toString()); @@ -139,126 +338,38 @@ class DevelopmentByMuscleBloc extends Bloc groupByDate(List exercises) { List groupedExercises = List(); - String origDatePart; - - int countExercises = 0; - double sumQuantity = 0; - double maxQuantity = 0; - Exercise origExercise; + exercises = sort(exercises, false); exercises.forEach((exercise) { - String exerciseDatePart = getDatePart(exercise.dateAdd); - if ( origExercise != null ) { - if ( origExercise.exerciseTypeId != exercise.exerciseTypeId || origDatePart != exerciseDatePart ) { - Exercise newExercise = origExercise.copy(); - newExercise.datePart = origDatePart; - if (this.diagramType == DiagramType.oneRepMax || this.diagramType == DiagramType.percent) { - newExercise.quantity = maxQuantity; - } else { - newExercise.quantity = sumQuantity / countExercises; - } - groupedExercises.add(newExercise); - countExercises = 0; - sumQuantity = 0; - maxQuantity = 0; - } - } - origExercise = exercise; - origDatePart = exerciseDatePart; - - double newQuantity = getQuantityByDate(exercise); - sumQuantity += newQuantity; - if (maxQuantity < newQuantity) { - maxQuantity = newQuantity; - } - countExercises++; - + print ("Date exercise " + exercise.toJsonDatePart().toString()); }); - groupedExercises.forEach((element) { - print("grouped " + element.toJson().toString()); - }); + GroupDate groupDate = GroupDate(inputList: exercises, outputList: groupedExercises); + groupDate.dateRate = this.dateRate; + groupDate.diagramType = this.diagramType; + groupDate.iteration(); + groupedExercises = groupDate.outputList; + +/* groupedExercises.forEach((element) { + print("Grouped " + element.toJsonDatePart().toString()); + });*/ return groupedExercises; } - double getQuantityByDate(Exercise exercise) { - double sum = 0; - if ( this.diagramType == DiagramType.sumMass ) { - if ( exercise.unitQuantity != null ) { - sum = exercise.quantity * exercise.unitQuantity; - } else { - sum = exercise.quantity; - } - } else if ( this.diagramType == DiagramType.oneRepMax || this.diagramType == DiagramType.percent ) { - if ( exercise.unitQuantity != null ) { - sum = calculate1RM(exercise.quantity, exercise.unitQuantity); - } else { - sum = exercise.quantity; - } - } - return sum; - } + List sort(List exercises, bool asc) { + exercises.sort( (a, b) { + var aDateId = a.exerciseTypeId.toString() + "_" + a.datePart.toString(); + var bDateId = b.exerciseTypeId.toString() + "_" + b.datePart.toString(); - double getBasePercent(Exercise exercise) { - if ( exercise.unitQuantity != null ) { - this.basePercent = calculate1RM(exercise.quantity, exercise.unitQuantity); - } else { - this.basePercent = exercise.quantity; - } - //print("Base percent of " + exercise.exerciseTypeId.toString() + ": " + basePercent.toString()); - return this.basePercent; - } - - double getDiagramValue(Exercise exercise) { - double value = 0; - /*if ( diagramType == DiagramType.sumMass) { - value = exercise.quantity; - if ( exercise.unitQuantity != null ) { - value *= exercise.unitQuantity; - } - } else if ( diagramType == DiagramType.oneRepMax) { - if ( exercise.unitQuantity != null ) { - value = calculate1RM(exercise.quantity, exercise.unitQuantity); - } - } else if ( diagramType == DiagramType.percent) { - if ( exercise.unitQuantity != null ) { - double oneRepMax = calculate1RM(exercise.quantity, exercise.unitQuantity); - value = (oneRepMax / this.basePercent) * 100; - //print("Percent of " + exercise.exerciseTypeId.toString() + ": " + value.toString() + " date " + exercise.dateAdd.toIso8601String() + " 1RM " + oneRepMax.toString()); - } else { - value = (exercise.quantity / this.basePercent ) * 100; - }*/ - - if ( diagramType == DiagramType.percent) { - if ( exercise.unitQuantity != null ) { - double oneRepMax = calculate1RM(exercise.quantity, exercise.unitQuantity); - value = (oneRepMax / this.basePercent) * 100; - //print("Percent of " + exercise.exerciseTypeId.toString() + ": " + value.toString() + " date " + exercise.dateAdd.toIso8601String() + " 1RM " + oneRepMax.toString()); - } else { - value = (exercise.quantity / this.basePercent ) * 100; - } - } else { - return exercise.quantity; - } - return value; + return asc ? aDateId.compareTo(bDateId) : bDateId.compareTo(aDateId); + }); + return exercises; } String getDateFormat(DateTime datetime) { diff --git a/lib/bloc/exercise_control_form_bloc.dart b/lib/bloc/exercise_control_form_bloc.dart index 9b03e9c..dbdd0ce 100644 --- a/lib/bloc/exercise_control_form_bloc.dart +++ b/lib/bloc/exercise_control_form_bloc.dart @@ -1,5 +1,4 @@ import 'package:aitrainer_app/repository/exercise_repository.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; class ExerciseControlFormBloc extends FormBloc { diff --git a/lib/bloc/exercise_form_bloc.dart b/lib/bloc/exercise_form_bloc.dart index ebb6a3b..9253355 100644 --- a/lib/bloc/exercise_form_bloc.dart +++ b/lib/bloc/exercise_form_bloc.dart @@ -1,8 +1,11 @@ import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:flutter_form_bloc/flutter_form_bloc.dart'; +import 'menu/menu_bloc.dart'; + class ExerciseFormBloc extends FormBloc { final ExerciseRepository exerciseRepository; + final MenuBloc menuBloc; final quantityField = TextFieldBloc( validators: [ @@ -19,7 +22,7 @@ class ExerciseFormBloc extends FormBloc { final unitQuantityField = TextFieldBloc(); final unitQuantityUnitField = TextFieldBloc(); - ExerciseFormBloc({this.exerciseRepository}) { + ExerciseFormBloc({this.exerciseRepository, this.menuBloc}) { addFieldBlocs(fieldBlocs: [ quantityField, unitField, @@ -46,6 +49,7 @@ class ExerciseFormBloc extends FormBloc { emitLoading(progress: 30); // Emit either Loaded or Error await exerciseRepository.addExercise(); + menuBloc.add(MenuTreeDown(parent: 0)); emitSuccess(canSubmitAgain: false); } on Exception catch (ex) { diff --git a/lib/bloc/menu/menu_bloc.dart b/lib/bloc/menu/menu_bloc.dart index 320a52f..d6b8b1c 100644 --- a/lib/bloc/menu/menu_bloc.dart +++ b/lib/bloc/menu/menu_bloc.dart @@ -1,22 +1,72 @@ import 'dart:async'; +import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; +import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/repository/workout_tree_repository.dart'; +import 'package:aitrainer_app/util/trans.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; import 'package:meta/meta.dart'; part 'menu_event.dart'; part 'menu_state.dart'; -class MenuBloc extends Bloc { +class MenuBloc extends Bloc with Trans { final WorkoutTreeRepository menuTreeRepository; + final ExerciseRepository exerciseRepository = ExerciseRepository(); int parent; + WorkoutMenuTree workoutItem; + + String infoTitle = ""; + String infoText = ""; + String infoText2 = ""; + String infoText3 = ""; + String infoLink = ""; + int missingParent = 0; + String missingTreeName = ""; + + BuildContext context; MenuBloc({this.menuTreeRepository}) : super(MenuInitial()) { parent = 0; } + void setMenuInfo() { + double percent = Cache().getPercentExercises(); + if ( context == null ) return; + percent = percent * 100; + print("Percent " + percent.toString()); + if ( percent == -1 || percent == 0) { + infoTitle = t("Greetings!"); + infoText = t("The purpose is to measure you physical condition") + " " + t("The suggested order of the exercises: chest - biceps - triceps - back - shoulders - core - tigh - calf."); + infoText2 = t("I suggest begin your tests with a"); + infoText3 = t("Go to the menu Strength - One Rep Max - Chest, and select your favourite exercise."); + infoLink = t("Bring me there"); + } else if ( percent > 0 && percent < 20) { + infoTitle = t("Nice! This is a good start"); + } else if ( percent > 20 && percent < 40) { + infoTitle = t("Go on!") + " " + t("You are on track"); + } else if ( percent > 60 && percent < 80) { + infoTitle = t("Persistence!") + " " + t("Not so much left"); + } else if ( percent > 80 && percent < 100) { + infoTitle = t("Almost!") + " " + t("You have only 1-2 exercise left to finish!"); + } + + menuTreeRepository.sortByMuscleType(); + missingTreeName = exerciseRepository.nextMissingBaseExercise(menuTreeRepository.sortedTree); + print("Missing " + missingTreeName); + if ( percent > 0) { + infoText = t("Please continue your tests with a") + " '" + missingTreeName + "' " + t("exercise!"); + infoLink = t("Bring me there"); + } + } + + void setContext(BuildContext context) { + this.context = context; + } + @override Stream mapEventToState( MenuEvent event, @@ -25,6 +75,7 @@ class MenuBloc extends Bloc { if ( event is MenuCreate ) { yield MenuLoading(); await menuTreeRepository.createTree(); + setMenuInfo(); yield MenuReady(); } else if (event is MenuRecreateTree) { // ie. at language changes @@ -34,14 +85,28 @@ class MenuBloc extends Bloc { // get child menus or exercises yield MenuLoading(); parent = event.parent; + workoutItem = event.item; + //print("menuitem " + workoutItem.id.toString() + " parent "+workoutItem.parent.toString()); menuTreeRepository.getBranch(event.parent); yield MenuReady(); } else if (event is MenuTreeUp) { yield MenuLoading(); // get parent menus or exercises parent = event.parent; - menuTreeRepository.getBranch(parent); - + workoutItem = menuTreeRepository.getParentItem(parent); + if ( workoutItem != null) { + //print("UP menuitem " + workoutItem.id.toString() + " parent " + workoutItem.parent.toString()); + menuTreeRepository.getBranch(workoutItem.parent); + } + yield MenuReady(); + } else if (event is MenuTreeJump) { + yield MenuLoading(); + parent = event.parent; + workoutItem = menuTreeRepository.getParentItem(parent); + if ( workoutItem != null) { + //print("JUMP menuitem " + workoutItem.id.toString() + " parent " + workoutItem.parent.toString()); + menuTreeRepository.getBranch(workoutItem.parent); + } yield MenuReady(); } else if (event is MenuClickExercise) { yield MenuLoading(); diff --git a/lib/bloc/menu/menu_event.dart b/lib/bloc/menu/menu_event.dart index 515a082..c21cf9b 100644 --- a/lib/bloc/menu/menu_event.dart +++ b/lib/bloc/menu/menu_event.dart @@ -16,16 +16,26 @@ class MenuCreate extends MenuEvent { } class MenuTreeDown extends MenuEvent { + final WorkoutMenuTree item; final int parent; - const MenuTreeDown({this.parent}); + const MenuTreeDown({this.parent, this.item}); @override - List get props => [parent]; + List get props => [parent, item]; } class MenuTreeUp extends MenuEvent { final int parent; - const MenuTreeUp({this.parent}); + final WorkoutMenuTree item; + const MenuTreeUp({this.parent, this.item}); + + @override + List get props => [parent, item]; +} + +class MenuTreeJump extends MenuEvent { + final int parent; + const MenuTreeJump({this.parent}); @override List get props => [parent]; diff --git a/lib/bloc/settings/settings_bloc.dart b/lib/bloc/settings/settings_bloc.dart index 207a858..226d445 100644 --- a/lib/bloc/settings/settings_bloc.dart +++ b/lib/bloc/settings/settings_bloc.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; -import 'package:aitrainer_app/model/cache.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/library/radar_chart.dart b/lib/library/radar_chart.dart new file mode 100644 index 0000000..c4ae56a --- /dev/null +++ b/lib/library/radar_chart.dart @@ -0,0 +1,325 @@ +library flutter_radar_chart; + +import 'dart:ui'; +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'dart:math' show pi, cos, sin; + +const defaultGraphColors = [ + Colors.green, + Colors.blue, + Colors.red, + Colors.orange, +]; + +class RadarChart extends StatefulWidget { + final List ticks; + final List features; + final List> data; + final bool reverseAxis; + final TextStyle ticksTextStyle; + final TextStyle featuresTextStyle; + final Color outlineColor; + final Color axisColor; + final List graphColors; + final int sides; + + const RadarChart({ + Key key, + @required this.ticks, + @required this.features, + @required this.data, + this.reverseAxis = false, + this.ticksTextStyle = const TextStyle(color: Colors.grey, fontSize: 12), + this.featuresTextStyle = const TextStyle(color: Colors.black, fontSize: 16), + this.outlineColor = Colors.black, + this.axisColor = Colors.grey, + this.graphColors = defaultGraphColors, + this.sides = 0, + }) : super(key: key); + + factory RadarChart.light({ + @required List ticks, + @required List features, + @required List> data, + bool reverseAxis = false, + bool useSides = false, + }) { + return RadarChart( + ticks: ticks, + features: features, + data: data, + reverseAxis: reverseAxis, + sides: useSides ? features.length : 0); + } + + factory RadarChart.dark({ + @required List ticks, + @required List features, + @required List> data, + bool reverseAxis = false, + bool useSides = false, + }) { + return RadarChart( + ticks: ticks, + features: features, + data: data, + featuresTextStyle: const TextStyle(color: Colors.white, fontSize: 16), + outlineColor: Colors.white, + axisColor: Colors.grey, + reverseAxis: reverseAxis, + sides: useSides ? features.length : 0); + } + + @override + _RadarChartState createState() => _RadarChartState(); +} + +class _RadarChartState extends State + with SingleTickerProviderStateMixin { + double fraction = 0; + Animation animation; + AnimationController animationController; + + @override + void initState() { + super.initState(); + animationController = AnimationController( + duration: Duration(milliseconds: 1000), vsync: this); + + animation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( + curve: Curves.fastOutSlowIn, + parent: animationController, + )) + ..addListener(() { + setState(() { + fraction = animation.value; + }); + }); + + animationController.forward(); + } + + @override + void didUpdateWidget(RadarChart oldWidget) { + super.didUpdateWidget(oldWidget); + + animationController.reset(); + animationController.forward(); + } + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: Size(double.infinity, double.infinity), + painter: RadarChartPainter( + widget.ticks, + widget.features, + widget.data, + widget.reverseAxis, + widget.ticksTextStyle, + widget.featuresTextStyle, + widget.outlineColor, + widget.axisColor, + widget.graphColors, + widget.sides, + this.fraction), + ); + } + + @override + void dispose() { + animationController.dispose(); + super.dispose(); + } +} + +class RadarChartPainter extends CustomPainter { + final List ticks; + final List features; + final List> data; + final bool reverseAxis; + final TextStyle ticksTextStyle; + final TextStyle featuresTextStyle; + final Color outlineColor; + final Color axisColor; + final List graphColors; + final int sides; + final double fraction; + + RadarChartPainter( + this.ticks, + this.features, + this.data, + this.reverseAxis, + this.ticksTextStyle, + this.featuresTextStyle, + this.outlineColor, + this.axisColor, + this.graphColors, + this.sides, + this.fraction, + ); + + Path variablePath(Size size, double radius, int sides) { + var path = Path(); + var angle = (math.pi * 2) / sides; + + Offset center = Offset(size.width / 2, size.height / 2); + + if (sides < 3) { + // Draw a circle + path.addOval(Rect.fromCircle( + center: Offset(size.width / 2, size.height / 2), + radius: radius, + )); + } else { + // Draw a polygon + Offset startPoint = Offset(radius * cos(-pi / 2), radius * sin(-pi / 2)); + + path.moveTo(startPoint.dx + center.dx, startPoint.dy + center.dy); + + for (int i = 1; i <= sides; i++) { + double x = radius * cos(angle * i - pi / 2) + center.dx; + double y = radius * sin(angle * i - pi / 2) + center.dy; + path.lineTo(x, y); + } + path.close(); + } + return path; + } + + @override + void paint(Canvas canvas, Size size) { + final centerX = size.width / 2.0; + final centerY = size.height / 2.0; + final centerOffset = Offset(centerX, centerY); + final radius = math.min(centerX, centerY) * 0.8; + final scale = radius / ticks.last; + + // Painting the chart outline + var outlinePaint = Paint() + ..color = outlineColor + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..isAntiAlias = true; + + var ticksPaint = Paint() + ..color = axisColor + ..style = PaintingStyle.stroke + ..strokeWidth = 1.0 + ..isAntiAlias = true; + + canvas.drawPath(variablePath(size, radius, this.sides), outlinePaint); + // Painting the circles and labels for the given ticks (could be auto-generated) + // The last tick is ignored, since it overlaps with the feature label + var tickDistance = radius / (ticks.length); + var tickLabels = reverseAxis ? ticks.reversed.toList() : ticks; + + if (reverseAxis) { + TextPainter( + text: TextSpan(text: tickLabels[0].toString(), style: ticksTextStyle), + textDirection: TextDirection.ltr, + ) + ..layout(minWidth: 0, maxWidth: size.width) + ..paint(canvas, Offset(centerX, centerY - ticksTextStyle.fontSize)); + } + + tickLabels + .sublist( + reverseAxis ? 1 : 0, reverseAxis ? ticks.length : ticks.length - 1) + .asMap() + .forEach((index, tick) { + var tickRadius = tickDistance * (index + 1); + + canvas.drawPath(variablePath(size, tickRadius, this.sides), ticksPaint); + + TextPainter( + text: TextSpan(text: tick.toString(), style: ticksTextStyle), + textDirection: TextDirection.ltr, + ) + ..layout(minWidth: 0, maxWidth: size.width) + ..paint(canvas, + Offset(centerX, centerY - tickRadius - ticksTextStyle.fontSize)); + }); + + // Painting the axis for each given feature + var angle = (2 * pi) / features.length; + + features.asMap().forEach((index, feature) { + var xAngle = cos(angle * index - pi / 2); + var yAngle = sin(angle * index - pi / 2); + + var featureOffset = + Offset(centerX + radius * xAngle, centerY + radius * yAngle); + + canvas.drawLine(centerOffset, featureOffset, ticksPaint); + + var featureLabelFontHeight = featuresTextStyle.fontSize; + var featureLabelFontWidth = featuresTextStyle.fontSize - 5; + var labelYOffset = yAngle < 0 ? -featureLabelFontHeight : 0; + var labelXOffset = + xAngle < 0 ? -featureLabelFontWidth * feature.length : 0; + + TextPainter( + text: TextSpan(text: feature, style: featuresTextStyle), + textAlign: TextAlign.left, + textDirection: TextDirection.ltr, + ) + ..layout(minWidth: 0, maxWidth: size.width) + ..paint( + canvas, + Offset(featureOffset.dx + labelXOffset, + featureOffset.dy + labelYOffset)); + }); + + // Painting each graph + data.asMap().forEach((index, graph) { + var graphPaint = Paint() + ..color = graphColors[index % graphColors.length].withOpacity(0.3) + ..style = PaintingStyle.fill; + + var graphOutlinePaint = Paint() + ..color = graphColors[index % graphColors.length] + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..isAntiAlias = true; + + // Start the graph on the initial point + var scaledPoint = scale * graph[0] * fraction; + var path = Path(); + + if (reverseAxis) { + path.moveTo(centerX, centerY - (radius * fraction - scaledPoint)); + } else { + path.moveTo(centerX, centerY - scaledPoint); + } + + graph.asMap().forEach((index, point) { + if (index == 0) return; + + var xAngle = cos(angle * index - pi / 2); + var yAngle = sin(angle * index - pi / 2); + var scaledPoint = scale * point * fraction; + + if (reverseAxis) { + path.lineTo(centerX + (radius * fraction - scaledPoint) * xAngle, + centerY + (radius * fraction - scaledPoint) * yAngle); + } else { + path.lineTo( + centerX + scaledPoint * xAngle, centerY + scaledPoint * yAngle); + } + }); + + path.close(); + canvas.drawPath(path, graphPaint); + canvas.drawPath(path, graphOutlinePaint); + }); + } + + @override + bool shouldRepaint(RadarChartPainter oldDelegate) { + return oldDelegate.fraction != fraction; + } +} diff --git a/lib/treeview/tree_view.dart b/lib/library/tree_view.dart similarity index 100% rename from lib/treeview/tree_view.dart rename to lib/library/tree_view.dart diff --git a/lib/localization/app_language.dart b/lib/localization/app_language.dart index 49bf4f8..414cdcb 100644 --- a/lib/localization/app_language.dart +++ b/lib/localization/app_language.dart @@ -34,6 +34,7 @@ class AppLanguage{ String langCode = prefs.getString('language_code'); if ( langCode == null) { _appLocale = Locale('en'); + langCode = "en"; } _appLocale = Locale(langCode); print(" ---- Get lang: " + _appLocale.toString() + " lang code $langCode"); diff --git a/lib/main.dart b/lib/main.dart index 20ebbfb..62424bd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'package:aitrainer_app/view/gdpr.dart'; import 'package:aitrainer_app/view/login.dart'; import 'package:aitrainer_app/view/exercise_new_page.dart'; import 'package:aitrainer_app/view/menu_page.dart'; +import 'package:aitrainer_app/view/mydevelopment_body_page.dart'; import 'package:aitrainer_app/view/mydevelopment_muscle_page.dart'; import 'package:aitrainer_app/view/mydevelopment_page.dart'; import 'package:aitrainer_app/view/myexcercise_plan_page.dart'; @@ -34,6 +35,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:sentry/sentry.dart'; import 'bloc/account/account_bloc.dart'; +import 'bloc/body_development/body_development_bloc.dart'; import 'bloc/development_by_muscle/development_by_muscle_bloc.dart'; import 'bloc/exercise_by_plan/exercise_by_plan_bloc.dart'; import 'bloc/exercise_plan/exercise_plan_bloc.dart'; @@ -133,6 +135,10 @@ Future main() async { create: (BuildContext context) => DevelopmentByMuscleBloc( workoutTreeRepository: menuTreeRepository), ), + BlocProvider( + create: (BuildContext context) => BodyDevelopmentBloc( + workoutTreeRepository: menuTreeRepository), + ), ], child: AitrainerApp(), @@ -173,7 +179,7 @@ class AitrainerApp extends StatelessWidget { } }); if ( locale != null && realSupportedLocale.countryCode != locale.countryCode ) { - print ("Locale " + locale.countryCode + " not supported on this device :("); + //print ("Locale " + locale.countryCode + " not supported on this device :("); } return realSupportedLocale; }, @@ -202,6 +208,7 @@ class AitrainerApp extends StatelessWidget { 'exerciseByPlanPage': (context) => ExerciseByPlanPage(), 'exerciseAddByPlanPage': (context) => ExerciseAddByPlanPage(), 'mydevelopmentMusclePage': (context) => MyDevelopmentMusclePage(), + 'mydevelopmentBodyPage': (context) => MyDevelopmentBodyPage(), }, initialRoute: 'home', title: 'WorkoutTest', @@ -220,3 +227,4 @@ class AitrainerApp extends StatelessWidget { } + diff --git a/lib/model/cache.dart b/lib/model/cache.dart index 8009581..3436c0e 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -126,8 +126,8 @@ class Cache { _exercisesTrainee = null; _traineeExercisePlan = null; _exercises = List(); + _myExercisesPlanDetails = LinkedHashMap(); print("Trainees is null? " + (_trainee == null).toString() ); - //firstLoad = true; Future prefs = SharedPreferences.getInstance(); await setPreferences(prefs, SharePrefsChange.logout, 0); } diff --git a/lib/model/exercise.dart b/lib/model/exercise.dart index 8dfc562..da04d75 100644 --- a/lib/model/exercise.dart +++ b/lib/model/exercise.dart @@ -1,4 +1,3 @@ -import 'package:aitrainer_app/util/common.dart'; import 'package:intl/intl.dart'; class Exercise { @@ -12,6 +11,7 @@ class Exercise { int exercisePlanDetailId; String datePart; + double calculated; @@ -25,6 +25,7 @@ class Exercise { this.unitQuantity = json['unitQuantity']; this.dateAdd = DateTime.parse( json['dateAdd'] ); this.datePart = DateFormat('yyyy-MM-dd').format(this.dateAdd); + this.calculated = quantity; } Map toJson() => @@ -38,6 +39,18 @@ class Exercise { "exercisePlanDetailId": exercisePlanDetailId, }; + Map toJsonDatePart() => + { + "exerciseTypeId": exerciseTypeId, + "customerId": customerId, + "quantity": quantity, + 'calculated': calculated, + "unit": unit, + "unitQuantity": unitQuantity, + "datePart": this.datePart, + }; + + Exercise copy() { Exercise newExercise = Exercise(); newExercise.exerciseTypeId = this.exerciseTypeId; diff --git a/lib/repository/exercise_plan_repository.dart b/lib/repository/exercise_plan_repository.dart index 4c08c33..885b980 100644 --- a/lib/repository/exercise_plan_repository.dart +++ b/lib/repository/exercise_plan_repository.dart @@ -28,7 +28,9 @@ class ExercisePlanRepository { ExercisePlan getExercisePlan() => exercisePlan; void addDetailToPlan() { - actualPlanDetail.exercisePlanId = exercisePlan.exercisePlanId; + if ( exercisePlan != null ) { + actualPlanDetail.exercisePlanId = exercisePlan.exercisePlanId; + } exercisePlanDetails[actualPlanDetail.exerciseTypeId] = actualPlanDetail; Cache().addToMyExercisePlanDetails(actualPlanDetail); } diff --git a/lib/repository/exercise_repository.dart b/lib/repository/exercise_repository.dart index 6019875..9f4a524 100644 --- a/lib/repository/exercise_repository.dart +++ b/lib/repository/exercise_repository.dart @@ -56,17 +56,11 @@ class ExerciseRepository { this.exercise.dateAdd = datetimeExercise; } - double get unitQuantity { - return this.exercise.unitQuantity; - } + double get unitQuantity => this.exercise.unitQuantity; - double get quantity { - return this.exercise.quantity; - } + double get quantity => this.exercise.quantity; - Exercise getExercise() { - return this.exercise; - } + Exercise getExercise() => this.exercise; Future addExercise() async { final Exercise modelExercise = this.exercise; @@ -81,13 +75,9 @@ class ExerciseRepository { } - setCustomer(Customer customer) { - this.customer = customer; - } + setCustomer(Customer customer) => this.customer = customer; - setExerciseType( ExerciseType exerciseType) { - this.exerciseType = exerciseType; - } + setExerciseType( ExerciseType exerciseType) => this.exerciseType = exerciseType; Future> getExercisesByCustomer( int customerId ) async { @@ -102,17 +92,53 @@ class ExerciseRepository { } List getExerciseList() { - //if ( this.exerciseList == null || this.exerciseList.length == 0 ) { - return this.exerciseList = Cache().getExercises(); - //} - //return this.exerciseList; + this.exerciseList = Cache().getExercises(); + return this.exerciseList; } List getExerciseListTrainee() { - //if ( this.exerciseList == null || this.exerciseList.length == 0 ) { - return this.exerciseList = Cache().getExercisesTrainee(); - //} - //return this.exerciseList; + this.exerciseList = Cache().getExercisesTrainee(); + return this.exerciseList; + } + + String nextMissingBaseExercise(SplayTreeMap sortedTree) { + if ( exerciseList == null ) { + exerciseList = Cache().getExercises(); + } + + if ( exerciseList == null ) { + return ""; + } + String missingTreeName; + String foundTreeName; + bool isBreak = false; + + sortedTree.forEach((key, list) { + List listByMuscle = list as List; + String treeName = key as String; + treeName = treeName.substring(3); + foundTreeName = null; + listByMuscle.forEach((exercise) { + if ( missingTreeName == null ) { + missingTreeName = treeName; + } + if ( exercise.base ) { + exerciseList.forEach((element) { + if ( element.exerciseTypeId == exercise.exerciseTypeId ) { + foundTreeName = treeName; + //print("Found " + foundTreeName + " Missing actual: " + missingTreeName); + isBreak = true; + } + }); + } + }); + if ( foundTreeName == null &&! isBreak ) { + missingTreeName = treeName; + isBreak = true; + } + }); + + return missingTreeName; } void getBaseExerciseFinishedPercent() { @@ -153,7 +179,7 @@ class ExerciseRepository { && treeItem.exerciseType.base == true && exercise.exerciseTypeId == treeItem.exerciseType.exerciseTypeId && !checkedBaseTreeItem.contains(treeItem.parent)) { - print ("id: " + exercise.exerciseTypeId.toString()); + //print ("id: " + exercise.exerciseTypeId.toString()); checkedBaseTreeItem.add(treeItem.parent); count1RMExercises++; } @@ -197,8 +223,6 @@ class ExerciseRepository { return actualExerciseType; } - void sortByDate() { - exerciseList.sort( (b, a) => a.dateAdd.compareTo(b.dateAdd) ); - } + void sortByDate() => exerciseList.sort( (a, b) => b.dateAdd.compareTo(a.dateAdd) ); } \ No newline at end of file diff --git a/lib/repository/workout_tree_repository.dart b/lib/repository/workout_tree_repository.dart index e1cb397..5d76220 100644 --- a/lib/repository/workout_tree_repository.dart +++ b/lib/repository/workout_tree_repository.dart @@ -44,6 +44,7 @@ class WorkoutTreeRepository { Antagonist.calf: Antagonist.calfNr }; + Future createTree() async { isEnglish = AppLanguage().appLocal == Locale('en'); @@ -145,16 +146,21 @@ class WorkoutTreeRepository { return branch; } + WorkoutMenuTree getParentItem(int parent) { + WorkoutMenuTree parentItem; + tree.forEach((key, value) { + WorkoutMenuTree workoutTree = value as WorkoutMenuTree; + if ( parent == workoutTree.id) { + parentItem = workoutTree; + } + }); + return parentItem; + } + void sortByMuscleType() { sortedTree = SplayTreeMap>(); tree.forEach((key, value) { WorkoutMenuTree workoutTree = value as WorkoutMenuTree; - //print("treeitem: " + workoutTree.toJson().toString()); - /*if ( workoutTree.exerciseType != null) { - print("treeItem exerciseTye " + workoutTree.exerciseType.toJson().toString()); - } else { - print("treeItem exerciseType null " + workoutTree.toJson().toString()); - }*/ if ( workoutTree.nameEnglish != 'One Rep Max' && workoutTree.is1RM && workoutTree.exerciseTypeId == 0) { String treeName = _antagonist[workoutTree.nameEnglish].toString() + ". " + workoutTree.name; sortedTree[treeName] = this.getBranchList(workoutTree.id); @@ -163,4 +169,16 @@ class WorkoutTreeRepository { return; } + + int getMissingTreeIdByName(String name) { + int missingId = 0; + tree.forEach((key, value) { + WorkoutMenuTree item = value as WorkoutMenuTree; + if ( item.name == name || name == item.nameEnglish ) { + missingId = item.id; + } + }); + return missingId; + } + } \ No newline at end of file diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index 69ad1b4..5e2eaa0 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -48,7 +48,7 @@ class CustomerApi { customer = Customer.fromJson(jsonDecode(responseBody)); Cache().afterRegistration(customer); } - } on FormatException catch (exception) { + } on FormatException { throw new Exception(responseBody); } } @@ -63,7 +63,7 @@ class CustomerApi { try { customer = Customer.fromJson(jsonDecode(responseBody)); await Cache().afterLogin(customer); - } on FormatException catch (exception) { + } on FormatException { throw new Exception(responseBody); } } diff --git a/lib/service/exercise_service.dart b/lib/service/exercise_service.dart index 6648f99..16c810d 100644 --- a/lib/service/exercise_service.dart +++ b/lib/service/exercise_service.dart @@ -22,6 +22,18 @@ class ExerciseApi { body); } + Future> getExercisesByCustomer(int customerId ) async { + final body = await _client.get("exercises/customer/", customerId.toString() ); + final Iterable json = jsonDecode(body); + final List exercises = json.map( (exercise) { + Exercise item = Exercise.fromJson(exercise); + return item; + }).toList(); + //exercises.sort( (a, b) => b.dateAdd.compareTo(a.dateAdd) ); + + return exercises; + } + Future addExercise(Exercise exercise) async { String body = JsonEncoder().convert(exercise.toJson()); print(" ===== add new exercise: " + body ); @@ -32,12 +44,4 @@ class ExerciseApi { return savedExercise; } - Future> getExercisesByCustomer(int customerId ) async { - final body = await _client.get("exercises/customer/", customerId.toString() ); - final Iterable json = jsonDecode(body); - final List exercises = json.map( (exercise) => Exercise.fromJson(exercise) ).toList(); - - return exercises; - } - } \ No newline at end of file diff --git a/lib/util/common.dart b/lib/util/common.dart index c4778a5..be05f68 100644 --- a/lib/util/common.dart +++ b/lib/util/common.dart @@ -7,6 +7,13 @@ import 'package:aitrainer_app/repository/user_repository.dart'; import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; +class DateRate { + static String daily = "daily"; + static String weekly = "weekly"; + static String monthly = "monthly"; + static String yearly = "yearly"; +} + mixin Common { final EMAIL_ERROR = "Please type a right email address here."; @@ -24,7 +31,7 @@ mixin Common { } ExerciseType getExerciseType( int exerciseTypeId ) { - ExerciseType returnElement = null; + ExerciseType returnElement; List listExerciseType = Cache().getExerciseTypes(); if ( listExerciseType != null ) { for ( var element in listExerciseType ) { @@ -32,7 +39,7 @@ mixin Common { returnElement = element; break; } - }; + } } return returnElement; } @@ -79,4 +86,18 @@ mixin Common { return ((dayOfYear - date.weekday + 10) / 7).floor(); } + String getDatePart(DateTime date, String dateRate) { + String datePart = DateFormat('MM.dd', AppLanguage().appLocal.toString()).format(date); + if ( dateRate == DateRate.weekly ) { + datePart = weekNumber(date).toString(); + } else if ( dateRate == DateRate.monthly ) { + datePart = DateFormat('MMM', AppLanguage().appLocal.toString()).format(date); + } else if ( dateRate == DateRate.yearly ) { + datePart = DateFormat('y', AppLanguage().appLocal.toString()).format(date); + } else if ( dateRate == DateRate.daily ) { + datePart = DateFormat('MM.dd', AppLanguage().appLocal.toString()).format(date); + } + return datePart; + } + } \ No newline at end of file diff --git a/lib/util/group_data.dart b/lib/util/group_data.dart new file mode 100644 index 0000000..d2d88f3 --- /dev/null +++ b/lib/util/group_data.dart @@ -0,0 +1,15 @@ +import 'package:aitrainer_app/model/exercise.dart'; + +abstract class GroupData { + + void iteration(); + + bool checkNewType(Exercise exercise); + + void addTempData(Exercise element); + + void temp2Output(Exercise exercise); + + void resetTemp(); + +} \ No newline at end of file diff --git a/lib/view/custom_exercise_page.dart b/lib/view/custom_exercise_page.dart index 1282e9b..30055c8 100644 --- a/lib/view/custom_exercise_page.dart +++ b/lib/view/custom_exercise_page.dart @@ -122,7 +122,7 @@ class _CustomExerciseNewPageState extends State { color: Colors.lightBlue, fontWeight: FontWeight.bold), inputFormatters: [ - WhitelistingTextInputFormatter(RegExp(r"[\d.]")) + FilteringTextInputFormatter.allow(RegExp(r"[\d.]")) ], onChanged: (input) => { print("UnitQuantity value $input"), @@ -150,7 +150,6 @@ class _CustomExerciseNewPageState extends State { ), ]); } - ; return column; } @@ -164,7 +163,7 @@ class _CustomExerciseNewPageState extends State { color: Colors.deepOrange, fontWeight: FontWeight.bold), inputFormatters: [ - WhitelistingTextInputFormatter (RegExp(r"[\d.]")) + FilteringTextInputFormatter.allow(RegExp(r"[\d.]")) ], onChanged: (input) => { print("Quantity value $input"), diff --git a/lib/view/customer_goal_page.dart b/lib/view/customer_goal_page.dart index 18a8a01..9060c0f 100644 --- a/lib/view/customer_goal_page.dart +++ b/lib/view/customer_goal_page.dart @@ -23,7 +23,6 @@ class _CustomerGoalPage extends State { @override Widget build(BuildContext context) { - final double cWidth = MediaQuery.of(context).size.width * 0.75; final CustomerRepository customerRepository = ModalRoute.of(context).settings.arguments; diff --git a/lib/view/customer_modify_page.dart b/lib/view/customer_modify_page.dart index 202c477..1691778 100644 --- a/lib/view/customer_modify_page.dart +++ b/lib/view/customer_modify_page.dart @@ -1,6 +1,5 @@ import 'package:aitrainer_app/bloc/account/account_bloc.dart'; import 'package:aitrainer_app/bloc/customer_change_form_bloc.dart'; -import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/util/trans.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -169,7 +168,7 @@ class CustomerModifyPage extends StatelessWidget with Trans { child: TextFieldBlocBuilder( style: TextStyle(fontSize: 12), textFieldBloc: customerBloc.birthYearField, - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d]"))], decoration: InputDecoration( fillColor: Colors.white24, filled: true, @@ -186,7 +185,7 @@ class CustomerModifyPage extends StatelessWidget with Trans { child: TextFieldBlocBuilder( style: TextStyle(fontSize: 12), textFieldBloc: customerBloc.weightField, - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d]"))], decoration: InputDecoration( fillColor: Colors.white24, filled: true, diff --git a/lib/view/exercise_add_by_plan_page.dart b/lib/view/exercise_add_by_plan_page.dart index 3f927a9..0fdba59 100644 --- a/lib/view/exercise_add_by_plan_page.dart +++ b/lib/view/exercise_add_by_plan_page.dart @@ -148,7 +148,7 @@ class _ExerciseAddByPlanPage extends State with Trans { color: Colors.black54, fontWeight: FontWeight.bold), inputFormatters: [ - WhitelistingTextInputFormatter(RegExp(r"[\d.]")) + FilteringTextInputFormatter.allow(RegExp(r"[\d.]")) ], decoration: InputDecoration( @@ -169,7 +169,7 @@ class _ExerciseAddByPlanPage extends State with Trans { color: Colors.deepOrange, fontWeight: FontWeight.bold), inputFormatters: [ - WhitelistingTextInputFormatter(RegExp(r"[\d.]")) + FilteringTextInputFormatter.allow(RegExp(r"[\d.]")) ], decoration: InputDecoration( diff --git a/lib/view/exercise_control_page.dart b/lib/view/exercise_control_page.dart index 59b1da2..57c3c07 100644 --- a/lib/view/exercise_control_page.dart +++ b/lib/view/exercise_control_page.dart @@ -4,6 +4,8 @@ import 'package:aitrainer_app/bloc/exercise_control_form_bloc.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; +import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -48,24 +50,7 @@ class _ExerciseControlPage extends State { autovalidate: true, child: Scaffold( resizeToAvoidBottomInset: true, - appBar: AppBar( - backgroundColor: Colors.transparent, - title: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text("1RM Control"), - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, - ), - ], - ), - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => Navigator.of(context).pop(), - ), - ), + appBar: AppBarNav(depth: 1), body: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, @@ -130,7 +115,7 @@ class _ExerciseControlPage extends State { textFieldBloc: exerciseBloc.quantity1Field, textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.bold), - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d.]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], decoration: InputDecoration( fillColor: Colors.white, filled: false, @@ -173,7 +158,7 @@ class _ExerciseControlPage extends State { textFieldBloc: exerciseBloc.quantity2Field, textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.bold), - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d.]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], onChanged: (input) => { print("Quantity 2 value $input"), //exerciseBloc.exerciseRepository.setQuantity(double.parse(input)), @@ -220,7 +205,7 @@ class _ExerciseControlPage extends State { textFieldBloc: exerciseBloc.quantity3Field, textAlign: TextAlign.center, style: TextStyle(fontSize: 14, color: Colors.deepOrange, fontWeight: FontWeight.bold), - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d.]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], onChanged: (input) => { print("Quantity 3 value $input"), //exerciseBloc.exerciseRepository.setQuantity(double.parse(input)), @@ -255,7 +240,9 @@ class _ExerciseControlPage extends State { ), ]), ))), + bottomNavigationBar: BottomNavigator(bottomNavIndex: 1), ), + ); } diff --git a/lib/view/exercise_execute_by_plan_page.dart b/lib/view/exercise_execute_by_plan_page.dart index 0988bc1..a9a0725 100644 --- a/lib/view/exercise_execute_by_plan_page.dart +++ b/lib/view/exercise_execute_by_plan_page.dart @@ -1,11 +1,10 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/exercise_by_plan/exercise_by_plan_bloc.dart'; -import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/treeview/tree_view.dart'; +import 'package:aitrainer_app/library/tree_view.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar_common.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; import 'package:aitrainer_app/widgets/splash.dart'; @@ -44,7 +43,7 @@ class _ExerciseByPlanPage extends State with Trans { return Scaffold( key: _scaffoldKey, - appBar: AppBarCommonNav(), + appBar: AppBarNav(depth: 1), body: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( @@ -92,8 +91,6 @@ class _ExerciseByPlanPage extends State with Trans { List nodeExercisePlan(ExerciseByPlanBloc bloc) { List exerciseTypes = List(); - bool isEnglish = AppLanguage().appLocal == Locale("en"); - Card explanation = Card( color: Colors.white38, child: Container( diff --git a/lib/view/exercise_log_page.dart b/lib/view/exercise_log_page.dart index 9b393d1..836d50a 100644 --- a/lib/view/exercise_log_page.dart +++ b/lib/view/exercise_log_page.dart @@ -1,14 +1,15 @@ import 'dart:collection'; +import 'package:aitrainer_app/widgets/app_bar.dart'; +import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:intl/intl.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; -import 'package:aitrainer_app/treeview/tree_view.dart'; +import 'package:aitrainer_app/library/tree_view.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar_common.dart'; import 'package:flutter/material.dart'; import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; @@ -26,7 +27,7 @@ class _ExerciseLogPage extends State with Trans, Common { setContext(context); return Scaffold( - appBar: AppBarCommonNav(), + appBar: AppBarNav(depth: 1), body: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( @@ -38,7 +39,8 @@ class _ExerciseLogPage extends State with Trans, Common { ), ), child: exerciseWidget(exerciseRepository, customerId), - ) + ), + bottomNavigationBar: BottomNavigator(bottomNavIndex: 1), ); } @@ -95,12 +97,11 @@ class _ExerciseLogPage extends State with Trans, Common { List listExercises = List(); String origDate = ""; - print("start exercises"); exerciseRepository.exerciseList.forEach((exercise) { String exerciseDate = DateFormat("yyyy-MM-dd", AppLanguage().appLocal.toString()).format(exercise.dateAdd); if ( origDate != exerciseDate) { - if ( origDate.length == 0) { + if ( origDate.length == 0 ) { listExercises.add(exercise); origDate = exerciseDate; } else { @@ -109,18 +110,35 @@ class _ExerciseLogPage extends State with Trans, Common { margin: const EdgeInsets.only(left: 4.0), child: TreeViewChild( startExpanded: true, - parent: TreeviewParentWidget(text: exerciseDate), + parent: TreeviewParentWidget(text: origDate), children: _getChildList(listExercises, exerciseRepository), ) ) ); listExercises = List(); + listExercises.add(exercise); + origDate = exerciseDate; } - } - origDate = exerciseDate; + + } else { listExercises.add(exercise); + origDate = exerciseDate; + } + }); + if ( listExercises.length > 0) { + listWidget.add( + Container( + margin: const EdgeInsets.only(left: 4.0), + child: TreeViewChild( + startExpanded: true, + parent: TreeviewParentWidget(text: origDate), + children: _getChildList(listExercises, exerciseRepository), + ) + ) + ); + } return listWidget; } diff --git a/lib/view/exercise_new_page.dart b/lib/view/exercise_new_page.dart index 30c7912..547e541 100644 --- a/lib/view/exercise_new_page.dart +++ b/lib/view/exercise_new_page.dart @@ -1,11 +1,13 @@ import 'dart:collection'; import 'package:aitrainer_app/bloc/exercise_form_bloc.dart'; +import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_type.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; @@ -21,13 +23,16 @@ class _ExerciseNewPageState extends State { @override Widget build(BuildContext context) { final ExerciseType exerciseType = ModalRoute.of(context).settings.arguments; + // ignore: close_sinks + final menuBloc = BlocProvider.of(context); return BlocProvider( - create: (context) => ExerciseFormBloc(exerciseRepository: ExerciseRepository()), + create: (context) => ExerciseFormBloc(exerciseRepository: ExerciseRepository(), menuBloc: menuBloc), child: Builder(builder: (context) { // ignore: close_sinks final exerciseBloc = BlocProvider.of(context); + exerciseBloc.exerciseRepository.setExerciseType(exerciseType); String exerciseName = AppLanguage().appLocal == Locale("en") ? exerciseBloc.exerciseRepository.exerciseType.name : @@ -37,23 +42,7 @@ class _ExerciseNewPageState extends State { autovalidate: true, child: Scaffold( resizeToAvoidBottomInset: true, - appBar: AppBar( - backgroundColor: Colors.transparent, - title: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, - ), - ], - ), - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => Navigator.of(context).pop(), - ), - ), + appBar: AppBarNav(depth: 1), body: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, @@ -139,39 +128,40 @@ class _ExerciseNewPageState extends State { if ( bloc.exerciseRepository.exerciseType != null && bloc.exerciseRepository.exerciseType.unitQuantity == "1") { column = Column( - children: [ - TextFieldBlocBuilder( - textFieldBloc: bloc.unitQuantityField, - textAlign: TextAlign.center, - style: TextStyle(fontSize: 30, - color: Colors.lightBlue, - fontWeight: FontWeight.bold), - inputFormatters: [ - WhitelistingTextInputFormatter (RegExp(r"[\d.]")) - ], - onChanged: (input) => { - print ("UnitQuantity value $input"), - bloc.exerciseRepository.setUnitQuantity( - double.parse(input)) - }, - decoration: InputDecoration( - fillColor: Colors.white, - filled: false, - hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), - hintText: AppLocalizations.of(context).translate("The number of the exercise done with"), - labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), - labelText: AppLocalizations.of(context).translate( - bloc.exerciseRepository.exerciseType.unitQuantityUnit), - ), - ), - new InkWell( - child: new Text(AppLocalizations.of(context).translate( + children: [ + TextFieldBlocBuilder( + textFieldBloc: bloc.unitQuantityField, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 30, + color: Colors.lightBlue, + fontWeight: FontWeight.bold), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r"[\d.]")) + ], + onChanged: (input) => + { + print("UnitQuantity value $input"), + bloc.exerciseRepository.setUnitQuantity( + double.parse(input)) + }, + decoration: InputDecoration( + fillColor: Colors.white, + filled: false, + hintStyle: TextStyle(fontSize: 16, color: Colors.black54, fontWeight: FontWeight.w100), + hintText: AppLocalizations.of(context).translate("The number of the exercise done with"), + labelStyle: TextStyle(fontSize: 16, color: Colors.lightBlue), + labelText: AppLocalizations.of(context).translate( bloc.exerciseRepository.exerciseType.unitQuantityUnit), - style: TextStyle(fontSize: 16)), ), + ), + new InkWell( + child: new Text(AppLocalizations.of(context).translate( + bloc.exerciseRepository.exerciseType.unitQuantityUnit), + style: TextStyle(fontSize: 16)), + ), - ]); - }; + ]); + } return column; } @@ -185,7 +175,7 @@ class _ExerciseNewPageState extends State { color: Colors.deepOrange, fontWeight: FontWeight.bold), inputFormatters: [ - WhitelistingTextInputFormatter (RegExp(r"[\d.]")) + FilteringTextInputFormatter.allow (RegExp(r"[\d.]")) ], onChanged: (input) => { diff --git a/lib/view/exercise_plan_custom_detail_add_page.dart b/lib/view/exercise_plan_custom_detail_add_page.dart index 80f7104..f3e808e 100644 --- a/lib/view/exercise_plan_custom_detail_add_page.dart +++ b/lib/view/exercise_plan_custom_detail_add_page.dart @@ -5,7 +5,7 @@ import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; import 'package:aitrainer_app/repository/exercise_plan_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar_common.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/cupertino.dart'; @@ -43,7 +43,7 @@ class _ExercisePlanDetailAddPage extends State with T autovalidate: true, child: Scaffold( resizeToAvoidBottomInset: true, - appBar: AppBarCommonNav(), + appBar: AppBarNav(depth: 1), body: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, @@ -72,7 +72,7 @@ class _ExercisePlanDetailAddPage extends State with T textFieldBloc: bloc.serieField, textAlign: TextAlign.center, style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d.]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], decoration: InputDecoration( fillColor: Colors.white, filled: false, @@ -86,7 +86,7 @@ class _ExercisePlanDetailAddPage extends State with T textFieldBloc: bloc.quantityField, textAlign: TextAlign.center, style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d.]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], decoration: InputDecoration( fillColor: Colors.white, filled: false, @@ -101,7 +101,7 @@ class _ExercisePlanDetailAddPage extends State with T textFieldBloc: bloc.weightField, textAlign: TextAlign.center, style: TextStyle(fontSize: 30, color: Colors.lightBlue, fontWeight: FontWeight.bold), - inputFormatters: [WhitelistingTextInputFormatter(RegExp(r"[\d.]"))], + inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r"[\d.]"))], decoration: InputDecoration( fillColor: Colors.white, filled: false, diff --git a/lib/view/exercise_plan_custom_page.dart b/lib/view/exercise_plan_custom_page.dart index 0badde8..2a3d050 100644 --- a/lib/view/exercise_plan_custom_page.dart +++ b/lib/view/exercise_plan_custom_page.dart @@ -4,9 +4,9 @@ import 'package:aitrainer_app/bloc/exercise_plan/exercise_plan_bloc.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/treeview/tree_view.dart'; +import 'package:aitrainer_app/library/tree_view.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar_common.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; import 'package:aitrainer_app/widgets/splash.dart'; @@ -45,7 +45,7 @@ class _ExercisePlanCustomPage extends State with Trans { return Scaffold( key: _scaffoldKey, - appBar: AppBarCommonNav(), + appBar: AppBarNav(depth: 1), body: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( @@ -181,10 +181,11 @@ class _ExercisePlanCustomPage extends State with Trans { ), InkWell( child: - element.selected && bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].change != null ? + !element.selected || bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId] == null || + bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].change == null ? Text("") : Text(bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].repeats.toString() + " x " + bloc.exercisePlanRepository.exercisePlanDetails[element.exerciseTypeId].weightEquation + - " " + element.exerciseType.unitQuantityUnit, style: TextStyle(fontSize: 9, color: Colors.green),) : Text(""), + " " + element.exerciseType.unitQuantityUnit, style: TextStyle(fontSize: 9, color: Colors.green),), onTap: () => { bloc.add(ExercisePlanUpdateUI(workoutTree: element)), Navigator.of(context).pushNamed("exercisePlanDetailAdd", arguments: bloc), diff --git a/lib/view/menu_page.dart b/lib/view/menu_page.dart index 626a7d7..9ab4927 100644 --- a/lib/view/menu_page.dart +++ b/lib/view/menu_page.dart @@ -28,7 +28,7 @@ class _MenuPage extends State { Widget build(BuildContext context) { menuBloc = BlocProvider.of(context); return Scaffold( - appBar: AppBarNav(), + appBar: AppBarNav(isMenu: true,), body: Container( decoration: BoxDecoration( image: DecorationImage( diff --git a/lib/view/mydevelopment_body_page.dart b/lib/view/mydevelopment_body_page.dart new file mode 100644 index 0000000..5998705 --- /dev/null +++ b/lib/view/mydevelopment_body_page.dart @@ -0,0 +1,129 @@ +import 'dart:collection'; +import 'package:aitrainer_app/library/radar_chart.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; +import 'package:aitrainer_app/widgets/bottom_nav.dart'; +import 'package:aitrainer_app/widgets/splash.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:aitrainer_app/model/cache.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:aitrainer_app/util/trans.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:aitrainer_app/bloc/body_development/body_development_bloc.dart'; + +class MyDevelopmentBodyPage extends StatefulWidget { + @override + _MyDevelopmentBodyPage createState() => _MyDevelopmentBodyPage(); +} + +class _MyDevelopmentBodyPage extends State with Trans, Common { + // ignore: close_sinks + BodyDevelopmentBloc bloc; + + @override + void initState() { + super.initState(); + + /// We require the initializers to run after the loading screen is rendered + SchedulerBinding.instance.addPostFrameCallback((_) { + BlocProvider.of(context).add(BodyDevelopmentLoad()); + }); + } + + @override + Widget build(BuildContext context) { + bloc = BlocProvider.of(context); + LinkedHashMap arguments = ModalRoute.of(context).settings.arguments; + final int customerId = arguments['customerId']; + setContext(context); + + return Scaffold( + appBar: AppBarNav(depth: 1), + body: Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + image: DecorationImage( + image: customerId == Cache().userLoggedIn.customerId ? AssetImage('asset/image/WT_light_background.png'): + AssetImage('asset/image/WT_menu_dark.png'), + fit: BoxFit.cover, + alignment: Alignment.center, + ), + ), + child: BlocConsumer( + listener: (context, state) { + if (state is BodyDevelopmentLoading) { + LoadingDialog(); + } + }, + builder: (context, state) { + if ( state is BodyDevelopmentInitial) { + return Container(); + } else { + return developmentWidget(customerId); + } + }, + ) + + ), + bottomNavigationBar: BottomNavigator(bottomNavIndex: 1), + ); + } + + Widget developmentWidget(int customerId) { + return Column( + children: [ + explanationWidget(), + Expanded( + child: exerciseWidget(customerId), + ), + + ] + ); + } + + Widget exerciseWidget(int customerId) { + return RadarChart.light( + ticks: bloc.radarTicks, + features: bloc.radarFeatures, + data: bloc.radarData, + ); + } + + Widget explanationWidget() { + return Card( + color: Colors.white60, + child: Container( + padding: EdgeInsets.only(left: 10, right: 5, top: 12, bottom: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Row( + children: [ + Icon( + Icons.info, + color: Colors.orangeAccent, + ), + Text(" "), + Text( + t("My Body Development"), + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + ], + ), + Divider( + color: Colors.transparent, + ), + Text( + t("You see here your whole body development by muscle groups."), + style: TextStyle(fontSize: 12, fontWeight: FontWeight.normal), + ), + + ], + ) + ) + ); + } + + + +} diff --git a/lib/view/mydevelopment_muscle_page.dart b/lib/view/mydevelopment_muscle_page.dart index f154432..aa9c4ac 100644 --- a/lib/view/mydevelopment_muscle_page.dart +++ b/lib/view/mydevelopment_muscle_page.dart @@ -1,12 +1,12 @@ import 'dart:collection'; import 'package:aitrainer_app/util/trans.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/treeview_parent_widget.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:aitrainer_app/util/common.dart'; import 'package:aitrainer_app/bloc/development_by_muscle/development_by_muscle_bloc.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; -import 'package:aitrainer_app/treeview/tree_view.dart'; -import 'package:aitrainer_app/widgets/app_bar_common.dart'; +import 'package:aitrainer_app/library/tree_view.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/cupertino.dart'; @@ -43,7 +43,7 @@ class _MyDevelopmentMuscleState extends State with Comm setContext(context); return Scaffold( - appBar: AppBarCommonNav(), + appBar: AppBarNav(depth: 1), body: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( @@ -75,7 +75,6 @@ class _MyDevelopmentMuscleState extends State with Comm children: _getTreeChildren(bloc.workoutTreeRepository.sortedTree, bloc), ); } - ; }, ), ), @@ -209,11 +208,10 @@ class _MyDevelopmentMuscleState extends State with Comm exerciseTypes.add(explanation); tree.forEach((name, list) { - List listWorkoutMenuTree = list; exerciseTypes.add(Container( margin: const EdgeInsets.only(left: 4.0), child: TreeViewChild( - startExpanded: true, + startExpanded: false, parent: _getExerciseWidget(exerciseTypeName: name), children: _getChildList(list, bloc), ))); @@ -274,7 +272,7 @@ class _MyDevelopmentMuscleState extends State with Comm getTitles: (double value) { var date = new DateTime.fromMillisecondsSinceEpoch(value.toInt()); //String strDate = DateFormat('MM.dd.', AppLanguage().appLocal.toString()).format(date); - String strDate = bloc.getDatePart(date); + String strDate = getDatePart(date, bloc.dateRate); return strDate; }, ), diff --git a/lib/view/mydevelopment_page.dart b/lib/view/mydevelopment_page.dart index df425d8..b801b10 100644 --- a/lib/view/mydevelopment_page.dart +++ b/lib/view/mydevelopment_page.dart @@ -4,11 +4,11 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/customer_repository.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar_common.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; +import 'package:aitrainer_app/widgets/image_button.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; class MyDevelopmentPage extends StatefulWidget { @override @@ -24,7 +24,7 @@ class _MyDevelopmentPage extends State with Trans { setContext(context); return Scaffold( - appBar: AppBarCommonNav(), + appBar: AppBarNav(depth: 0), body: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( @@ -41,7 +41,16 @@ class _MyDevelopmentPage extends State with Trans { SliverGrid( delegate: SliverChildListDelegate( [ - FlatButton( + ImageButton( + textAlignment: Alignment.topCenter, + text: t("My Exercise Logs"), + style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, backgroundColor: Colors.black54.withOpacity(0.4)), + image: "asset/image/exercise_log.jpg", + top: 40, + onTap:() => this.callBackExerciseLog(exerciseRepository, customerRepository), + isLocked: false, + ), + /*FlatButton( padding: EdgeInsets.all(0), textColor: Colors.white, color: Colors.black12, @@ -56,8 +65,8 @@ class _MyDevelopmentPage extends State with Trans { }, child: Text(t("My Exercise Logs"), style: TextStyle(fontSize: 18),) - ), - Stack( + ),*/ + /*Stack( fit: StackFit.passthrough, overflow: Overflow.clip, alignment: Alignment.topLeft, @@ -73,22 +82,51 @@ class _MyDevelopmentPage extends State with Trans { focusColor: Colors.blueAccent, onPressed: () => { + args['customerId'] = Cache().userLoggedIn.customerId, + Navigator.of(context).pushNamed('mydevelopmentBodyPage', + arguments: args) }, child: Text(t("My Whole Body Development"), style: TextStyle(fontSize: 18),) ), ], + ),*/ + ImageButton( + textAlignment: Alignment.topLeft, + text: t("My Whole Body Development"), + style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4)), + image: "asset/menu/3.1.BMI.png", + top: 50, + onTap:() => { + args['customerId'] = Cache().userLoggedIn.customerId, + Navigator.of(context).pushNamed('mydevelopmentBodyPage', + arguments: args) + }, + isLocked: true, ), - - Stack( + ImageButton( + textAlignment: Alignment.topLeft, + text: t("Development Of Muscles"), + style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4)), + image: "asset/image/development_muscles.jpg", + top: 50, + onTap:() => { + Navigator.of(context).pushNamed('mydevelopmentMusclePage', + arguments: args) + }, + isLocked: true, + ), + /*Stack( fit: StackFit.passthrough, overflow: Overflow.clip, children: [ - /*Image.asset('asset/image/lock.png', + *//*Image.asset('asset/image/lock.png', height: 40, width: 40, - ),*/ + ),*//* FlatButton( padding: EdgeInsets.all(20), textColor: Colors.white, @@ -104,8 +142,20 @@ class _MyDevelopmentPage extends State with Trans { ), ] + ),*/ + ImageButton( + textAlignment: Alignment.topLeft, + text: t("Predictions"), + style: TextStyle(fontSize: 20, color: Colors.orange, fontWeight: FontWeight.bold, + backgroundColor: Colors.black54.withOpacity(0.4)), + image: "asset/menu/2.2.1.1RM.png", + top: 50, + onTap:() => { + + }, + isLocked: true, ), - Stack( + /*Stack( fit: StackFit.passthrough, overflow: Overflow.clip, @@ -126,7 +176,7 @@ class _MyDevelopmentPage extends State with Trans { style: TextStyle(fontSize: 18),) ), ] - ), + ),*/ hiddenWidget(customerRepository, exerciseRepository), ] ), @@ -167,6 +217,15 @@ class _MyDevelopmentPage extends State with Trans { } } + void callBackExerciseLog(ExerciseRepository exerciseRepository, CustomerRepository customerRepository) { + final LinkedHashMap args = LinkedHashMap(); + args['exerciseRepository'] = exerciseRepository; + args['customerRepository'] = customerRepository; + args['customerId'] = Cache().userLoggedIn.customerId; + Navigator.of(context).pushNamed('exerciseLogPage', + arguments: args); + } + } diff --git a/lib/view/myexcercise_plan_page.dart b/lib/view/myexcercise_plan_page.dart index f96c94a..e527193 100644 --- a/lib/view/myexcercise_plan_page.dart +++ b/lib/view/myexcercise_plan_page.dart @@ -2,7 +2,7 @@ import 'dart:collection'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; import 'package:aitrainer_app/util/trans.dart'; -import 'package:aitrainer_app/widgets/app_bar_common.dart'; +import 'package:aitrainer_app/widgets/app_bar.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -20,7 +20,7 @@ class _MyExercisePlanPage extends State with Trans { setContext(context); return Scaffold( - appBar: AppBarCommonNav(), + appBar: AppBarNav(depth: 0), body: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( diff --git a/lib/view/settings.dart b/lib/view/settings.dart index 3112dcd..61d5170 100644 --- a/lib/view/settings.dart +++ b/lib/view/settings.dart @@ -2,7 +2,6 @@ import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/bloc/settings/settings_bloc.dart'; import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; -import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/widgets/bottom_nav.dart'; import 'package:aitrainer_app/widgets/splash.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/app_bar.dart b/lib/widgets/app_bar.dart index d4edec4..8af89e7 100644 --- a/lib/widgets/app_bar.dart +++ b/lib/widgets/app_bar.dart @@ -4,6 +4,7 @@ import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/repository/exercise_repository.dart'; +import 'package:aitrainer_app/util/common.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -13,20 +14,20 @@ import 'package:rainbow_color/rainbow_color.dart'; class AppBarNav extends StatefulWidget implements PreferredSizeWidget { final MenuBloc menuBloc; - const AppBarNav({this.menuBloc}); + final bool isMenu; + final int depth; + const AppBarNav({this.menuBloc, this.isMenu, this.depth}); @override _AppBarNav createState() => _AppBarNav(); @override - Size get preferredSize => const Size.fromHeight(60); + Size get preferredSize => const Size.fromHeight(50); } -class _AppBarNav extends State with SingleTickerProviderStateMixin { +class _AppBarNav extends State with SingleTickerProviderStateMixin, Common { Animation colorAnim; - //Animation sizeAnim; AnimationController colorController; - //AnimationController sizeController; MenuBloc menuBloc; @override @@ -34,26 +35,7 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { colorController = AnimationController(duration: Duration(seconds: 4), vsync:this); - //sizeController = - // AnimationController(duration: Duration(seconds: 3), vsync: this); - /* final curvedAnimation = CurvedAnimation( - parent: sizeController, - curve: Curves.easeIn, - reverseCurve: Curves.easeInOutBack, - ); - - sizeAnim = - Tween(begin: 0, end: 1.5).animate(curvedAnimation) - ..addStatusListener((status) { - if (status == AnimationStatus.completed) { - sizeController.reverse(); - } else if (status == AnimationStatus.dismissed) { - Timer(Duration(seconds: 5), () { - sizeController.forward(); - }); - } - }); */ colorAnim = RainbowColorTween([Colors.white70, Colors.blueGrey, Colors.blueAccent, @@ -69,7 +51,6 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { ..addStatusListener((status) { if (status == AnimationStatus.completed) { Timer(Duration(seconds: 10), () { - //colorController.reset(); if ( mounted ) { colorController.forward(); } @@ -85,7 +66,6 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { menuBloc = BlocProvider.of(context); - //setContext(context); return AppBar( backgroundColor: Colors.black, @@ -96,8 +76,8 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { getAnimatedWidget(), Image.asset( 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, + //fit: BoxFit.cover, + height: 45.0, ), ], ), @@ -105,8 +85,16 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { icon: Icon(Icons.arrow_back, color: Colors.white), onPressed: () => { - if ( menuBloc != null ) { - menuBloc.add(MenuTreeUp(parent: 0)), + if ( widget.isMenu != null ) { + if ( menuBloc.workoutItem != null ) { + menuBloc.add(MenuTreeUp(parent: menuBloc.workoutItem.parent)), + } + } else if ( widget.depth != null ) { + if ( widget.depth == 0 ) { + Navigator.of(context).pushNamed('home') + } else { + Navigator.of(context).pop() + } } }, ) @@ -115,12 +103,12 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { @override void dispose() { - //sizeController.dispose(); colorController.dispose(); super.dispose(); } Widget getAnimatedWidget() { + double cWidth = mediaSizeWidth(context); double percent = Cache().getPercentExercises(); if ( percent == -1) { ExerciseRepository exerciseRepository = ExerciseRepository(); @@ -130,9 +118,10 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { int sizeExerciseList = Cache().getExercises() == null? 0 : Cache().getExercises().length; if ( sizeExerciseList == 0 ) { String text = AppLocalizations.of(context).translate("Make your first test"); - double fontSize = text.length > 15 ? 10 : 16; + double fontSize = text.length > 24 ? 10 : 16; return Stack( alignment: Alignment.topLeft, + overflow: Overflow.clip, children: [ Text(text, style: TextStyle(fontSize: fontSize, color: colorAnim.value, shadows: [Shadow(color: Colors.purple , blurRadius: 15)]), @@ -147,12 +136,12 @@ class _AppBarNav extends State with SingleTickerProviderStateMixin { alignment: Alignment.topLeft, children: [ LinearPercentIndicator( - width: 120.0, + width: cWidth / 4, lineHeight: 14.0, percent: percent, center: Text( (percent * 100).toStringAsFixed(0) + "% " + AppLocalizations.of(context).translate("finished"), - style: new TextStyle(fontSize: 12.0), + style: new TextStyle(fontSize: 10.0), ), trailing: Icon(percent > 0.6 ? Icons.mood : Icons.mood_bad, color: colorAnim.value,), linearStrokeCap: LinearStrokeCap.roundAll, diff --git a/lib/widgets/app_bar_common.dart b/lib/widgets/app_bar_common.dart deleted file mode 100644 index aeb921f..0000000 --- a/lib/widgets/app_bar_common.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'dart:async'; -import 'package:aitrainer_app/localization/app_localization.dart'; -import 'package:aitrainer_app/model/cache.dart'; -import 'package:aitrainer_app/repository/exercise_repository.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:percent_indicator/linear_percent_indicator.dart'; -import 'package:rainbow_color/rainbow_color.dart'; - - -class AppBarCommonNav extends StatefulWidget implements PreferredSizeWidget { - - @override - _AppBarCommonNav createState() => _AppBarCommonNav(); - - @override - Size get preferredSize => const Size.fromHeight(60); -} - -class _AppBarCommonNav extends State with SingleTickerProviderStateMixin { - Animation colorAnim; - AnimationController colorController; - - @override - void initState() { - - colorController = - AnimationController(duration: Duration(seconds: 4),vsync: this); - - colorAnim = RainbowColorTween([Colors.white70, - Colors.blueGrey, - Colors.blueAccent, - Colors.lightBlue, - Colors.lightBlueAccent, - Colors.yellowAccent, - Colors.orange, - Colors.orangeAccent, - Colors.yellowAccent, - Color(0xffcce6ff)]) - .animate(colorController) - ..addListener(() { setState(() {}); }) - ..addStatusListener((status) { - if (status == AnimationStatus.completed) { - Timer(Duration(seconds: 10), () { - //colorController.reset(); - if ( mounted ) { - colorController.forward(); - } - }); - } else if (status == AnimationStatus.dismissed) { - colorController.forward(); - } - }); - colorController.forward(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return AppBar( - backgroundColor: Colors.black, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - - getAnimatedWidget(), - Image.asset( - 'asset/image/WT_long_logo.png', - fit: BoxFit.cover, - height: 65.0, - ), - ], - ), - leading: IconButton( - icon: Icon(Icons.arrow_back, color: Colors.white), - onPressed: () => - { - Navigator.of(context).pushNamed('home') - }, - ) - ); - } - - @override - void dispose() { - //sizeController.dispose(); - colorController.dispose(); - super.dispose(); - } - - Widget getAnimatedWidget() { - double percent = Cache().getPercentExercises(); - if ( percent == -1) { - ExerciseRepository exerciseRepository = ExerciseRepository(); - exerciseRepository.getBaseExerciseFinishedPercent(); - percent = Cache().getPercentExercises(); - } - int sizeExerciseList = Cache().getExercises() == null? 0 : Cache().getExercises().length; - if ( sizeExerciseList == 0 ) { - String text = AppLocalizations.of(context).translate("Make your first test"); - double fontSize = text.length > 17 ? 10 : 16; - return Stack( - alignment: Alignment.topLeft, - children: [ - Text(text, - style: TextStyle(fontSize: fontSize, color: colorAnim.value, shadows: [Shadow(color: Colors.purple , blurRadius: 15)]), - - ), - //TestProgress(animation: sizeAnim), - ] - ); - } else { - - return Stack( - alignment: Alignment.topLeft, - children: [ - LinearPercentIndicator( - width: 120.0, - lineHeight: 14.0, - percent: percent, - center: Text( - (percent * 100).toStringAsFixed(0) + "% " + AppLocalizations.of(context).translate("finished"), - style: new TextStyle(fontSize: 12.0), - ), - trailing: Icon(percent > 0.6 ? Icons.mood : Icons.mood_bad, color: colorAnim.value,), - linearStrokeCap: LinearStrokeCap.roundAll, - backgroundColor: colorAnim.value, - progressColor: Color(0xff73e600), - animation: true, - ), - ], - ); - } - } -} diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index 5504093..db29605 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -1,6 +1,5 @@ import 'package:aitrainer_app/bloc/session/session_bloc.dart'; import 'package:aitrainer_app/bloc/settings/settings_bloc.dart'; -import 'package:aitrainer_app/localization/app_language.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/view/login.dart'; import 'package:aitrainer_app/view/menu_page.dart'; @@ -13,6 +12,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'loading.dart'; +// ignore: must_be_immutable class AitrainerHome extends StatefulWidget { _HomePageState _state; @override diff --git a/lib/widgets/image_button.dart b/lib/widgets/image_button.dart new file mode 100644 index 0000000..219e691 --- /dev/null +++ b/lib/widgets/image_button.dart @@ -0,0 +1,119 @@ +import 'package:aitrainer_app/model/cache.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:bloc/bloc.dart'; + +class ImageButton extends StatelessWidget { + final String text; + final TextStyle style; + final String image; + final double top; + final double height; + final double width; + final bool isShape; + final Bloc bloc; + final Alignment textAlignment; + final VoidCallback onTap; + bool isLocked; + + ImageButton({ + this.text, + this.style, + this.image, + this.top, + this.height, + this.width, + this.bloc, + this.isShape, + this.textAlignment, + this.onTap, + @required this.isLocked + }); + + @override + Widget build(BuildContext context) { + return Stack( + //alignment: textAlignment, + fit: StackFit.passthrough, + overflow: Overflow.clip, + children: [ + FlatButton( + child: image == null ? + _getButtonImage("asset/image/WT_menu_dark.png") : + _getButtonImage(image), + padding: EdgeInsets.only(left: 0.0, bottom: 0), + shape: getShape(isShape), + onPressed: onTap ?? onTap, + ), + Stack( + alignment: Alignment.topLeft, + children: [ + this.isLocked? + Image.asset( + 'asset/image/lock.png', + height: 40, + width: 40, + ) + : Container(), + ]), + Positioned( + top: top, + left: 10, + child: Container( + height: 100, + width: 150, + child: InkWell( + onTap: onTap ?? onTap, + child: Text( + text, + maxLines: 2, + style: style, + ), + ), + color: Colors.transparent, + ), + ), + ] + //) + // ) + ); + } + + dynamic getShape(bool isShape) { + dynamic returnCode = (isShape == true) + ? RoundedRectangleBorder( + side: BorderSide(width: 4, color: Colors.orangeAccent), + ) + : null; + return returnCode; + } + + dynamic _getButtonImage(String imageName) { + dynamic image; + try { + image = Image.asset( + imageName, + fit: BoxFit.fitWidth, + alignment: Alignment.center, + errorBuilder: (context, error, stackTrace) { + String url = Cache.mediaUrl + 'images/' + imageName.substring(11); + Widget image = FadeInImage.assetNetwork( + placeholder: 'asset/image/dots.gif', + image: url, + height: 180, + ); + return image; + }, + ); + } on Exception catch (_) { + String url = Cache.mediaUrl + '/images/' + imageName; + image = FadeInImage.assetNetwork( + placeholder: 'asset/image/dots.gif', + image: url, + height: 180, + ); + } + + return image; + } +} diff --git a/lib/widgets/menu_info_widget.dart b/lib/widgets/menu_info_widget.dart new file mode 100644 index 0000000..81664a4 --- /dev/null +++ b/lib/widgets/menu_info_widget.dart @@ -0,0 +1,113 @@ +import 'dart:ui'; + +import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; +import 'package:aitrainer_app/util/common.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +// ignore: must_be_immutable +class MenuInfoWidget extends StatelessWidget with Common { + final String title; + final double titleSize; + final Color titleColor; + final FontWeight titleWeight; + final String text; + final double textSize; + final Color textColor; + final FontWeight textWeight; + final Icon leadingIcon; + final Color leadingIconColor; + final String text2; + final String text3; + final String link; + final int parentMenu; + final MenuBloc bloc; + + MenuInfoWidget( + {this.title, + this.titleSize, + this.titleColor, + this.titleWeight, + @required this.text, + this.textSize, + this.textColor, + this.textWeight, + this.leadingIcon, + this.leadingIconColor, + this.text2, + this.text3, + this.link, + this.parentMenu, + this.bloc}); + + @override + Widget build(BuildContext context) { + int length = title.length + text2.length + text3.length + link.length; + return Container( + height: length > 100? 350 : 250, + width: 180, + padding: EdgeInsets.all(25.0), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + "asset/menu/3.bcs1.png", + ), + fit: BoxFit.contain), + ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 9, sigmaY: 9), + child: Container( + padding: EdgeInsets.only(top: 10.0, left: 15, right: 10, bottom: 5), + + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + border: Border.all(color: Colors.white60), + borderRadius: BorderRadius.all(Radius.circular(10.0)), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + title, + maxLines: 1, + style: + TextStyle(color: titleColor, fontSize: titleSize, fontFamily: 'Arial', fontWeight: titleWeight), + ), + Divider(), + Text( + text, + maxLines: 6, + style: TextStyle(color: textColor, fontSize: textSize, fontFamily: 'Arial', fontWeight: textWeight), + ), + Divider(), + Text( + text2, + maxLines: 6, + style: TextStyle(color: textColor, fontSize: textSize, fontFamily: 'Arial', fontWeight: textWeight), + ), + Divider(), + Text( + text3, + maxLines: 6, + style: TextStyle(color: textColor, fontSize: textSize, fontFamily: 'Arial', fontWeight: textWeight), + ), + getLink(), + ], + ), + ), + )); + } + + Widget getLink() { + int missingId; + return InkWell( + child: new Text(link, style: TextStyle(color: Colors.lightBlueAccent, fontSize: textSize, fontFamily: 'Arial', fontWeight: textWeight),), + onTap: () => + { + missingId = bloc.menuTreeRepository.getMissingTreeIdByName(bloc.missingTreeName), + print("menu " + missingId.toString()), + bloc.add(MenuTreeJump(parent: missingId)) + } + ); + } +} diff --git a/lib/widgets/menu_page_widget.dart b/lib/widgets/menu_page_widget.dart index b5319a2..60157af 100644 --- a/lib/widgets/menu_page_widget.dart +++ b/lib/widgets/menu_page_widget.dart @@ -4,13 +4,16 @@ import 'package:aitrainer_app/bloc/menu/menu_bloc.dart'; import 'package:aitrainer_app/localization/app_localization.dart'; import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/workout_menu_tree.dart'; +import 'package:aitrainer_app/util/trans.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'menu_info_widget.dart'; + // ignore: must_be_immutable -class MenuPageWidget extends StatelessWidget { +class MenuPageWidget extends StatelessWidget with Trans { int parent; MenuPageWidget({this.parent}); @@ -18,16 +21,37 @@ class MenuPageWidget extends StatelessWidget { @override Widget build(BuildContext context) { MenuBloc menuBloc = BlocProvider.of(context); + setContext(context); return CustomScrollView( scrollDirection: Axis.vertical, slivers: [buildMenuColumn(parent, context, menuBloc)]); } - SliverList buildMenuColumn( - int parent, BuildContext context, MenuBloc menuBloc) { + SliverList buildMenuColumn(int parent, BuildContext context, MenuBloc menuBloc) { final List _columnChildren = List(); + if ( context != null ) { + menuBloc.setContext(context); + menuBloc.setMenuInfo(); + + Widget info = MenuInfoWidget( + title: menuBloc.infoTitle, + titleSize: 18, + titleWeight: FontWeight.bold, + titleColor: Colors.orangeAccent, + text: menuBloc.infoText, + textSize: 13, + textColor: Colors.yellowAccent, + text2: menuBloc.infoText2, + text3: menuBloc.infoText3, + link: menuBloc.infoLink, + bloc: menuBloc, + ); + _columnChildren.add(info); + } + + menuBloc.menuTreeRepository .getBranch(menuBloc.parent) .forEach((treeName, value) { @@ -86,7 +110,7 @@ class MenuPageWidget extends StatelessWidget { WorkoutMenuTree workoutTree, MenuBloc menuBloc, BuildContext context) { print("Hi!, Menu clicked " + workoutTree.id.toString()); if (workoutTree.child == false) { - menuBloc.add(MenuTreeDown(parent: workoutTree.id)); + menuBloc.add(MenuTreeDown(item: workoutTree, parent: workoutTree.id)); } else { menuBloc.add(MenuClickExercise(exerciseTypeId: workoutTree.id)); if (Cache().userLoggedIn == null) { diff --git a/pubspec.lock b/pubspec.lock index e3c3ff3..6adb048 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -35,7 +35,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety" + version: "2.4.2" bloc: dependency: transitive description: @@ -56,7 +56,7 @@ packages: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety" + version: "2.0.0" build: dependency: transitive description: @@ -91,14 +91,14 @@ packages: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.10.2" + version: "1.10.0" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.0.1" + version: "5.2.0" built_collection: dependency: transitive description: @@ -119,14 +119,14 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.2" + version: "1.0.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety" + version: "1.1.3" checked_yaml: dependency: transitive description: @@ -147,7 +147,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.0.1" code_builder: dependency: transitive description: @@ -161,7 +161,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.2" + version: "1.14.13" convert: dependency: transitive description: @@ -224,7 +224,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.1.0" ffi: dependency: transitive description: @@ -238,7 +238,7 @@ packages: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.0.0-nullsafety.1" + version: "5.2.1" fixnum: dependency: transitive description: @@ -415,7 +415,7 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3-nullsafety.1" + version: "0.6.2" json_annotation: dependency: transitive description: @@ -443,14 +443,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety" + version: "0.12.8" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.2" + version: "1.1.8" mime: dependency: transitive description: @@ -506,7 +506,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety" + version: "1.7.0" path_drawing: dependency: transitive description: @@ -548,7 +548,7 @@ packages: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.9.0" percent_indicator: dependency: "direct main" description: @@ -562,14 +562,14 @@ packages: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.0.4" platform: dependency: transitive description: name: platform url: "https://pub.dartlang.org" source: hosted - version: "3.0.0-nullsafety.1" + version: "2.2.1" plugin_platform_interface: dependency: transitive description: @@ -583,14 +583,14 @@ packages: name: pool url: "https://pub.dartlang.org" source: hosted - version: "1.5.0-nullsafety.1" + version: "1.4.0" process: dependency: transitive description: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.0.0-nullsafety.1" + version: "3.0.13" provider: dependency: transitive description: @@ -735,21 +735,21 @@ packages: name: source_map_stack_trace url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.2" + version: "2.0.0" source_maps: dependency: transitive description: name: source_maps url: "https://pub.dartlang.org" source: hosted - version: "0.10.10-nullsafety.1" + version: "0.10.9" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety" + version: "1.7.0" spider_chart: dependency: "direct main" description: @@ -763,14 +763,14 @@ packages: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety" + version: "1.9.5" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety" + version: "2.0.0" stream_transform: dependency: transitive description: @@ -784,7 +784,7 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety" + version: "1.0.5" sync_http: dependency: transitive description: @@ -798,28 +798,28 @@ packages: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety" + version: "1.1.0" test: dependency: "direct dev" description: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.16.0-nullsafety.4" + version: "1.15.2" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety" + version: "0.2.17" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.3.12-nullsafety.4" + version: "0.3.10" timing: dependency: transitive description: @@ -833,7 +833,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.2" + version: "1.2.0" usage: dependency: transitive description: @@ -854,14 +854,14 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.2" + version: "2.0.8" vm_service: dependency: transitive description: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "5.2.0" + version: "4.2.0" vm_service_client: dependency: transitive description: @@ -910,7 +910,7 @@ packages: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.1.2" + version: "0.1.0" xml: dependency: transitive description: @@ -926,5 +926,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.10.0-4.0.dev <2.10.0" + dart: ">=2.9.0-14.0.dev <3.0.0" flutter: ">=1.16.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 210c4b6..7981f2a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.0+23 +version: 1.1.2+2 environment: sdk: ">=2.7.0 <3.1.0" @@ -24,8 +24,6 @@ dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 devicelocale: ^0.3.2 sentry: ^3.0.1 @@ -94,6 +92,9 @@ flutter: - asset/image/WT_welcome.png - asset/image/login_fb.png - asset/image/lock.png + - asset/image/Congrats_N1.jpg + - asset/image/development_muscles.jpg + - asset/image/exercise_log.jpg - asset/menu/1.cardio.png - asset/menu/1.1.aerob.png - asset/menu/1.2.anaerob.png @@ -118,6 +119,14 @@ flutter: - asset/menu/3.1.BMI.png - asset/menu/3.2.BMR.png - asset/menu/3.3.sizes.png + - asset/menu/cable triceps.png + - asset/menu/Back_pullup.png + - asset/menu/biceps.jpg + - asset/menu/calf.png + - asset/menu/legpress.jpg + - asset/menu/shoulder press.png + - asset/menu/squat.jpg + - asset/menu/tricdip.jpg - i18n/en.json - i18n/hu.json diff --git a/test/customer_service_test.dart b/test/customer_service_test.dart index d2d7018..d18ac68 100644 --- a/test/customer_service_test.dart +++ b/test/customer_service_test.dart @@ -1,9 +1,8 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/customer.dart'; import 'package:aitrainer_app/service/customer_service.dart'; -import 'package:aitrainer_app/util/env.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; + //import 'package:mockito/mockito.dart'; // Create a MockClient using the Mock class provided by the Mockito package. diff --git a/test/exercise_plan_bloc.dart b/test/exercise_plan_bloc.dart index 6ce2be1..1c942f0 100644 --- a/test/exercise_plan_bloc.dart +++ b/test/exercise_plan_bloc.dart @@ -11,8 +11,6 @@ main() { SimExercisePlanRepository _exercisePlanRepository; ExercisePlanBloc bloc; - int _customerId; - Future setUpPlan() async { final String planName2 = "Test Plan2"; ExercisePlan plan2 = ExercisePlan(planName2, 101); @@ -42,7 +40,6 @@ main() { WorkoutTreeRepository menuTreeRepository = WorkoutTreeRepository(); bloc = ExercisePlanBloc(menuTreeRepository: menuTreeRepository); bloc.setExercisePlanRepository(_exercisePlanRepository); - _customerId = 62; await setUpPlan(); }); diff --git a/test/exercise_plan_repository_test.dart b/test/exercise_plan_repository_test.dart index 617c37e..df4ef29 100644 --- a/test/exercise_plan_repository_test.dart +++ b/test/exercise_plan_repository_test.dart @@ -1,7 +1,6 @@ import 'package:aitrainer_app/model/cache.dart'; import 'package:aitrainer_app/model/exercise_plan.dart'; import 'package:aitrainer_app/model/exercise_plan_detail.dart'; -import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'mocks.dart'; diff --git a/test/mocks.dart b/test/mocks.dart index c462f97..ccbe1eb 100644 --- a/test/mocks.dart +++ b/test/mocks.dart @@ -13,7 +13,7 @@ class MockExercisePlanApi extends Mock implements ExercisePlanApi { return _singleton; } - MockExercisePlanApi._internal() {} + MockExercisePlanApi._internal(); final List memoryExercisePlan = List(); final List memoryExercisePlanDetail = List(); diff --git a/test/widget_test.login.dart b/test/widget_test.login.dart index 59449be..d5490db 100644 --- a/test/widget_test.login.dart +++ b/test/widget_test.login.dart @@ -52,13 +52,11 @@ class MockCommon with Common { void main() { group('LoginScreen', () { MockLoginBloc loginBloc; - MockUserRepository userRepository; Widget loginWidget; MockCommon common; setUp(() { loginBloc = MockLoginBloc(); - userRepository = MockUserRepository(); common = MockCommon(); loginWidget = diff --git a/test_driver/app_test.dart b/test_driver/app_test.dart index eab3403..d2b8990 100644 --- a/test_driver/app_test.dart +++ b/test_driver/app_test.dart @@ -1,5 +1,4 @@ // Imports the Flutter Driver API. -import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; void main() {