Aitrainer_app 1.1.1

test menu, customer modification, exercise save images, localization
This commit is contained in:
Bossanyi Tibor
2020-07-07 16:53:03 +02:00
parent 79142b92f2
commit 2177db10ea
80 changed files with 2751 additions and 553 deletions
+29 -2
View File
@@ -1,5 +1,7 @@
import 'dart:collection';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/model/auth.dart';
import 'package:aitrainer_app/model/exercise_type.dart';
import 'package:intl/intl.dart';
class Common {
@@ -13,4 +15,29 @@ class Common {
return rc;
}
static ExerciseType getExerciseType( int exerciseTypeId ) {
ExerciseType returnElement = null;
List<ExerciseType> listExerciseType = Auth().getExerciseTypes();
if ( listExerciseType != null ) {
for ( var element in listExerciseType ) {
if (exerciseTypeId == element.exerciseTypeId) {
returnElement = element;
break;
}
};
}
return returnElement;
}
static String getDateLocale( DateTime datetime, bool timeDisplay ) {
AppLanguage appLanguage = AppLanguage();
var date = datetime;
String dateName = DateFormat(DateFormat.YEAR_MONTH_DAY, appLanguage.appLocal.toString()).format(date.toUtc());
if ( timeDisplay ) {
dateName += " " +DateFormat(DateFormat.HOUR_MINUTE, appLanguage.appLocal.toString()).format(date.toUtc());
}
return dateName;
}
}
+61
View File
@@ -0,0 +1,61 @@
import 'package:aitrainer_app/util/loading_screen_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
/// Loading Screen Widget that updates the screen once all inistializer methods
/// are called
class LoadingScreen extends StatefulWidget {
/// List of methods that are called once the Loading Screen is rendered
/// for the first time. These are the methods that can update the messages
/// that are shown under the loading symbol
final List<dynamic> initializers;
/// The name of the application that is shown at the top of the loading screen
RichText title = RichText(text: TextSpan(text: 'AI Trainer'));
//final Text title;
/// The background colour which is used as a filler when the image doesn't
/// occupy the full screen
final Color backgroundColor;
/// The styling that is used with the text (messages) that are displayed under
/// the loader symbol
final TextStyle styleTextUnderTheLoader;
/// The Layout/Scaffold Widget that is loaded once all the initializer methods
/// have been executed
final dynamic navigateToWidget;
/// The colour that is used for the loader symbol
final Color loaderColor;
/// The image widget that is used as a background cover to the loading screen
final Image image;
/// The message that is displayed on the first load of the widget
final String initialMessage;
/// Constructor for the LoadingScreen widget with all the required
/// initializers
LoadingScreen(
{this.initializers,
this.navigateToWidget,
this.loaderColor,
this.image,
//this.title = Text("Welcome"),
this.backgroundColor = Colors.white,
this.styleTextUnderTheLoader = const TextStyle(
fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.black),
this.initialMessage})
// The Widget depends on the initializers and navigateToWidget to have a
// valid value. Thus we assert that the values passed are valid and
// not null
: assert(initializers != null && initializers.length > 0),
assert(navigateToWidget != null);
/// Bind the Widget to the custom State object
@override
LoadingScreenState createState() => LoadingScreenState();
}
+131
View File
@@ -0,0 +1,131 @@
import 'dart:core';
import 'dart:async';
import 'file:///D:/projects/aitrainer/src/aitrainer_app/lib/util/loading_screen.dart';
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/widgets/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:aitrainer_app/util/message_state.dart';
/// The custom state that is used by the Loading Screen widget to handle the
/// messages that are provided by the initializer methods.
///
/// Note: Although the class is not exported from the package as not required by
/// the implementers using the package, the protected metatag is added to make
/// the code clearer.
@protected
class LoadingScreenState extends MessageState<LoadingScreen> {
/// Initialise the state
@override
void initState() {
super.initState();
/// If the LoadingScreen widget has an initial message set, then the default
/// message in the MessageState class needs to be updated
if (widget.initialMessage != null) {
initialMessage = widget.initialMessage;
}
/// We require the initializers to run after the loading screen is rendered
SchedulerBinding.instance.addPostFrameCallback((_) {
runInitTasks();
});
}
/// This method calls the initializers and once they complete redirects to
/// the widget provided in navigateAfterInit
@protected
Future runInitTasks() async {
print(" ----- runInitTasks");
/// Run each initializer method sequentially
Future.forEach(widget.initializers, (init) => init(this, callbackFunction)).whenComplete(() {
// When all the initializers has been called and terminated their
// execution. The screen is navigated to the next scaffolding widget
if (widget.navigateToWidget is String) {
// It's fairly safe to assume this is using the in-built material
// named route component
print(" ----- navigate to " + widget.navigateToWidget);
Navigator.of(context).pushReplacementNamed(widget.navigateToWidget);
} else if (widget.navigateToWidget is Widget) {
Navigator.of(context).pushReplacement(new MaterialPageRoute(
builder: (BuildContext context) => widget.navigateToWidget));
print(" ----- navigate to main ");
} else {
throw new ArgumentError(
'widget.navigateAfterSeconds must either be a String or Widget');
}
});
}
void callbackFunction() {
print("Call Home callback if widget");
if (widget.navigateToWidget is Widget) {
AitrainerHome home = widget.navigateToWidget as AitrainerHome;
home.callback();
}
}
/// Render the LoadingScreen widget
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: widget.backgroundColor,
body: new InkWell(
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
/// Paint the area where the inner widgets are loaded with the
/// background to keep consistency with the screen background
new Container(
decoration: BoxDecoration(color: widget.backgroundColor),
),
/// Render the background image
new Container(
child: widget.image,
),
/// Render the Title widget, loader and messages below each other
new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Expanded(
flex: 3,
child: new Container(
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(top: 30.0),
),
widget.title,
],
)),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
/// Loader Animation Widget
CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(
widget.loaderColor),
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
),
Text(getMessage, style: widget.styleTextUnderTheLoader),
],
),
),
],
),
],
),
),
);
}
}
+93
View File
@@ -0,0 +1,93 @@
import 'dart:collection';
import 'package:aitrainer_app/localization/app_localization.dart';
import 'package:aitrainer_app/model/workout_tree.dart';
import 'package:flutter/material.dart';
class MenuTests {
LinkedHashMap tree = LinkedHashMap();
MenuTests(BuildContext context) {
this.tree['Cardio']= WorkoutTree(1, 0, AppLocalizations.of(context).translate("Cardio"),
'asset/menu/1.cardio.png',
Colors.white, 48, false,0);
this.tree['Aerobic']= WorkoutTree(2, 1, AppLocalizations.of(context).translate("Aerobic"),
'asset/menu/1.1.aerob.png',
Colors.white, 48, false,0);
this.tree['Cooper']= WorkoutTree(21, 2, AppLocalizations.of(context).translate("Cooper"),
'asset/menu/1.1.1.cooper.png',
Colors.white, 48, true,30);
this.tree['Anaerobic']= WorkoutTree(3, 1, AppLocalizations.of(context).translate("Anaerobic"),
'asset/menu/1.2.anaerob.png',
Colors.white, 48, false,0);
this.tree['300m']= WorkoutTree(22, 3, "300m",
'asset/menu/1.2.1.300m.png',
Colors.white, 48, true,31);
this.tree['400m']= WorkoutTree(24, 3, "400m",
'asset/menu/1.2.2.400m.png',
Colors.white, 48, true,32);
this.tree['Strength']= WorkoutTree(4, 0, AppLocalizations.of(context).translate("Strength"),
'asset/menu/2.strength.png',
Colors.white, 48, false,0);
this.tree['Endurance']= WorkoutTree(5, 4, AppLocalizations.of(context).translate("Endurance"),
'asset/menu/2.1.endurance.png',
Colors.white, 36, false,0);
this.tree['Pullups']= WorkoutTree(6, 5, AppLocalizations.of(context).translate("Pull Ups"),
'asset/menu/2.1.1.pull-ups.png',
Colors.white, 48, true,38);
this.tree['Pushups']= WorkoutTree(7, 5, AppLocalizations.of(context).translate("Pushups"),
'asset/menu/2.1.2.pushup.png',
Colors.white, 48, true,33);
this.tree['Situps']= WorkoutTree(10, 5, AppLocalizations.of(context).translate("Sit-ups"),
'asset/menu/2.1.3.sit-ups.png',
Colors.white, 48, true,36);
this.tree['Squats']= WorkoutTree(11, 5, AppLocalizations.of(context).translate("Squats"),
'asset/menu/2.1.4.squats.png',
Colors.white, 48, true,35);
this.tree['TimedPushups']= WorkoutTree(12, 5, AppLocalizations.of(context).translate("Timed Pushups"),
'asset/menu/2.1.5.timedpushup.png',
Colors.white, 32, true,34);
this.tree['Core']= WorkoutTree(43, 5, AppLocalizations.of(context).translate("Core"),
'asset/menu/2.1.6.core.png',
Colors.white, 48, true,45);
this.tree['1RM']= WorkoutTree(8, 4, AppLocalizations.of(context).translate("1RM"),
'asset/menu/2.2.1.1RM.png',
Colors.white, 48, false,0);
this.tree['Chestpress']= WorkoutTree(13, 8, AppLocalizations.of(context).translate("Chest Press"),
'asset/menu/2.2.1.1.chestpress.png',
Colors.white, 48, true,37);
this.tree['PullUps1rm']= WorkoutTree(14, 8, AppLocalizations.of(context).translate("Pull Ups"),
'asset/menu/2.2.1.2.pullups.png',
Colors.white, 48, true, 38);
this.tree['Biceps']= WorkoutTree(15, 8, AppLocalizations.of(context).translate("Biceps"),
'asset/menu/2.2.1.3.biceps.png',
Colors.white, 48, true, 39);
this.tree['Triceps']= WorkoutTree(16, 8, AppLocalizations.of(context).translate("Triceps"),
'asset/menu/2.2.1.4.triceps.png',
Colors.white, 48, true, 40);
this.tree['Shoulders']= WorkoutTree(17, 8, AppLocalizations.of(context).translate("Shoulders"),
'asset/menu/2.2.1.5.shoulders.png',
Colors.white, 48, true, 41);
this.tree['BodyCompositions']= WorkoutTree(9, 0, AppLocalizations.of(context).translate("Body Compositions"),
'asset/menu/3.bcs1.png',
Colors.white, 40, false,0);
this.tree['BMI']= WorkoutTree(18, 9, AppLocalizations.of(context).translate("BMI"),
'asset/menu/3.1.BMI.png',
Colors.white, 32, true,42);
this.tree['BMR']= WorkoutTree(19, 9, AppLocalizations.of(context).translate("BMR"),
'asset/menu/3.2.BMR.png',
Colors.white, 32, true,0);
this.tree['Sizes']= WorkoutTree(20, 9, AppLocalizations.of(context).translate("Sizes"),
'asset/menu/3.3.sizes.png',
Colors.black, 48, true,0);
}
LinkedHashMap getMenuItems() {
return this.tree;
}
}
+32
View File
@@ -0,0 +1,32 @@
import 'package:flutter/widgets.dart';
/// An extension class to the Flutter standard State. The class provides getter
/// and setters for updating the message section of the loading screen
///
/// Note: The class is marked as abstract to avoid IDE issues that expects
/// protected methods to be overloaded
abstract class MessageState<T extends StatefulWidget> extends State<T> {
/// The state variable that will hold the latest message that needs to be
/// displayed.
///
/// Note: Although Flutter standard allow member variables to be used from
/// instance object reference, this is not a best practice with OOP. OOP
/// design proposes that member variables should be accessed through getter
/// and setter methods.
@protected
String _message = 'Loading . . .';
/// The member variable is set as protected this it is not exposed to the
/// widget state class. As a workaround a protected setter is set so it is
/// not used outside the package
@protected
set initialMessage(String message) => _message = message;
/// Setter for the message variable
set setMessage(String message) => setState(() {
_message = message;
});
/// Getter for the message variable
String get getMessage => _message;
}
+74
View File
@@ -0,0 +1,74 @@
import 'package:aitrainer_app/localization/app_language.dart';
import 'package:aitrainer_app/service/api.dart';
import 'package:aitrainer_app/service/customer_service.dart';
import 'package:aitrainer_app/service/exercisetype_service.dart';
import 'package:aitrainer_app/viewmodel/exercise_type_changing_view_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:aitrainer_app/model/auth.dart';
class Session {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Auth _auth = Auth();
SharedPreferences _sharedPreferences;
final AppLanguage appLanguage = AppLanguage();
fetchSessionAndNavigate(Function callback ) async {
_sharedPreferences = await _prefs;
if ( _auth.firstLoad ) {
_fetchToken(_sharedPreferences, callback);
appLanguage.fetchLocale();
}
}
/*
Auth flow of the user, see auth.dart
*/
_fetchToken(SharedPreferences prefs, Function callback) async {
var responseJson = await APIClient.authenticateUser(
Auth.username,
Auth.password
);
print("--- Lang: " + appLanguage.appLocal.toString());
if(responseJson['error'] != null) {
print("************** Here big error - no authentication");
} else if (responseJson['token'] != null) {
prefs.setString(Auth.authTokenKey, responseJson['token']);
Auth auth = Auth();
auth.authToken = responseJson['token'];
if (prefs.get(Auth.customerIdKey) == null) {
print("************** Registration");
// registration
//Navigator.of(context).pushNamed('registration');
prefs.setBool(Auth.isRegisteredKey, true);
} else {
DateTime now = DateTime.now();
DateTime lastStoreDate = DateTime.parse(
prefs.get(Auth.lastStoreDateKey));
DateTime minStoreDate = now.add(Duration(days: -10));
if (lastStoreDate == null ||
lastStoreDate.difference(minStoreDate) > Duration(days: 10) ||
prefs.get(Auth.isLoggedInKey) == null ||
prefs.get(Auth.isLoggedInKey) == false) {
print("************* Login");
//Navigator.of(context).pushNamed('login');
} else {
print("************** Store SharedPreferences");
// get API customer
await CustomerApi().getCustomer(prefs.getInt(Auth.customerIdKey));
}
await ExerciseTypeApi().getExerciseTypes("");
print("--- Session finished, call callback ");
callback();
}
}
}
}