Merge remote-tracking branch 'origin/main' into feature/optimize-search-page

# Conflicts:
#	.dart_tool/package_config.json
#	.dart_tool/package_config_subset
#	.flutter-plugins-dependencies
#	lib/android/pages/nav_pages/main_page.dart
#	pubspec.yaml
pull/5/head
Bogdan Kotikov 2023-06-03 00:48:17 +02:00
commit 741199d84d
10 changed files with 344 additions and 189 deletions

View File

@ -229,6 +229,12 @@
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "2.17" "languageVersion": "2.17"
}, },
{
"name": "flutter_profile_picture",
"rootUri": "file:///Users/bogdan/.pub-cache/hosted/pub.dev/flutter_profile_picture-2.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{ {
"name": "flutter_test", "name": "flutter_test",
"rootUri": "file:///Users/bogdan/fvm/versions/3.7.9/packages/flutter_test", "rootUri": "file:///Users/bogdan/fvm/versions/3.7.9/packages/flutter_test",

View File

@ -142,6 +142,10 @@ flutter_lints
2.17 2.17
file:///Users/bogdan/.pub-cache/hosted/pub.dev/flutter_lints-2.0.1/ file:///Users/bogdan/.pub-cache/hosted/pub.dev/flutter_lints-2.0.1/
file:///Users/bogdan/.pub-cache/hosted/pub.dev/flutter_lints-2.0.1/lib/ file:///Users/bogdan/.pub-cache/hosted/pub.dev/flutter_lints-2.0.1/lib/
flutter_profile_picture
2.12
file:///Users/bogdan/.pub-cache/hosted/pub.dev/flutter_profile_picture-2.0.0/
file:///Users/bogdan/.pub-cache/hosted/pub.dev/flutter_profile_picture-2.0.0/lib/
fluttertoast fluttertoast
2.12 2.12
file:///Users/bogdan/.pub-cache/hosted/pub.dev/fluttertoast-8.2.2/ file:///Users/bogdan/.pub-cache/hosted/pub.dev/fluttertoast-8.2.2/

View File

@ -0,0 +1,148 @@
import 'package:ernaehrung/android/components/form/form_builder_text_field_component.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:hive/hive.dart';
import '../../models/form_builder.dart';
import '../../models/user.dart';
import '../../pages/nav_pages/main_page.dart';
class FormBuilderComponent extends StatefulWidget {
final bool lockTextFields;
const FormBuilderComponent({Key? key, required this.lockTextFields})
: super(key: key);
@override
State<FormBuilderComponent> createState() => _FormBuilderComponentState();
}
class _FormBuilderComponentState extends State<FormBuilderComponent> {
final formKey = GlobalKey<FormBuilderState>();
final List<FormTextField> listOfTextField = [
FormTextField(
"vorname",
TextInputType.text,
30,
"Der Vorname sollte maximal 30 Zeichen lang sein",
"Der Vorname sollte mindestens 2 Zeichen lang sein",
2,
null,
null,
null,
null),
FormTextField(
"nachname",
TextInputType.text,
30,
"Der Nachname sollte maximal 30 Zeichen lang sein",
"Der Nachname sollte mindestens 2 Zeichen lang sein",
2,
null,
null,
null,
null),
FormTextField(
"gewicht",
TextInputType.number,
null,
null,
null,
null,
200,
10,
"Der Gewicht sollte maximal 200 kg sein",
"Der Gewicht sollte mindestens 10 kg sein"),
FormTextField(
"groesse",
TextInputType.number,
null,
null,
null,
null,
230,
60,
"Die Größe sollte maximal 230cm sein",
"Die Größe sollte mindestens 60cm sein"),
FormTextField(
"alter",
TextInputType.number,
null,
null,
null,
null,
99,
6,
"Das Alter sollte maximal 99 Jahre alt sein",
"Das Alter sollte mindestens 6 Jahre alt sein"),
FormTextField(
"kalorien",
TextInputType.number,
null,
null,
null,
null,
30000,
1000,
"Die Kalorienanzahl sollte mindestens 30000 Kcal sein",
"Die Kalorienanzahl sollte mindestens 1000 Kcal sein")
];
@override
Widget build(BuildContext context) {
print("widget enable ${widget.lockTextFields}");
return FormBuilder(
key: formKey,
child: Padding(
padding: const EdgeInsets.all(8),
child: SingleChildScrollView(
child: Column(
children: [
ListView.builder(
primary: false,
itemCount: listOfTextField.length,
shrinkWrap: true,
itemBuilder: (context, index) {
print(listOfTextField[index]);
return FormBuilderTextFieldComponent(
true, listOfTextField[index]);
}),
Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(40), //
backgroundColor: const Color(0xFF6E7BFB),
foregroundColor: const Color(0xFFffffff),
shape: const StadiumBorder(),
),
onPressed: () {
final Box box = Hive.box<User>("USER_BOX");
box.put(
"USER",
User(
formKey.currentState?.fields['vorname']?.value,
formKey.currentState?.fields['nachname']?.value,
int.parse(formKey
.currentState?.fields['gewicht']?.value),
int.parse(formKey
.currentState?.fields['groesse']?.value),
int.parse(
formKey.currentState?.fields['alter']?.value),
int.parse(formKey
.currentState?.fields['kalorien']?.value)));
print(box.get("USER"));
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => const MainPage()));
},
child: const Text("Eingaben bestätigen"),
)),
],
)),
));
}
}

View File

@ -0,0 +1,96 @@
import 'package:basic_utils/basic_utils.dart';
import 'package:ernaehrung/android/models/form_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
class FormBuilderTextFieldComponent extends StatefulWidget {
final bool isSettingsPage;
final FormTextField formTextField;
const FormBuilderTextFieldComponent(this.isSettingsPage, this.formTextField,
{super.key});
@override
State<FormBuilderTextFieldComponent> createState() => _FormBuilderTextFieldComponentState();
}
class _FormBuilderTextFieldComponentState extends State<FormBuilderTextFieldComponent> {
bool enable = false;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
child: widget.isSettingsPage
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child:
TextFieldChild(widget.isSettingsPage, widget.formTextField, enable: enable)),
enable
? IconButton(
onPressed: () async {
setState(() {
enable = !enable;
});
},
icon: const Icon(Icons.lock_open))
: IconButton(
onPressed: () async {
setState(() {
enable = !enable;
});
},
icon: const Icon(Icons.lock))
],
)
: TextFieldChild(widget.isSettingsPage, widget.formTextField),
);
}
}
class TextFieldChild extends StatelessWidget {
final bool isSettingsPage;
final FormTextField formTextField;
final bool? enable;
const TextFieldChild(this.isSettingsPage, this.formTextField,
{super.key, this.enable = true});
@override
Widget build(BuildContext context) {
return FormBuilderTextField(
name: formTextField.title,
enabled: enable!,
decoration: InputDecoration(
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0))),
filled: true,
hintStyle: TextStyle(color: Colors.grey.shade400),
hintText: StringUtils.capitalize(formTextField.title),
labelText: StringUtils.capitalize(formTextField.title),
fillColor: Colors.white70),
keyboardType: TextInputType.number,
maxLength: TextInputType.number == formTextField.textInputType ? 7 : 30,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
TextInputType.number == formTextField.textInputType
? FormBuilderValidators.max(formTextField.numericMax!,
errorText: formTextField.numericMaxErrorMessage)
: FormBuilderValidators.maxLength(30,
errorText: formTextField.maxLengthErrorMessage),
TextInputType.number == formTextField.textInputType
? FormBuilderValidators.min(formTextField.numericMin!,
errorText: formTextField.numericMinErrorMessage)
: FormBuilderValidators.minLength(2,
errorText: formTextField.minLengthErrorMessage)
]),
);
}
}

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class FormTextField{
final String title;
final TextInputType textInputType;
final num? maxLength;
final String? maxLengthErrorMessage;
final String? minLengthErrorMessage;
final num? minLength;
final num? numericMax;
final num? numericMin;
final String? numericMaxErrorMessage;
final String? numericMinErrorMessage;
FormTextField(
this.title,
this.textInputType,
this.maxLength,
this.maxLengthErrorMessage,
this.minLengthErrorMessage,
this.minLength,
this.numericMax,
this.numericMin,
this.numericMaxErrorMessage,
this.numericMinErrorMessage);
}

View File

@ -2,7 +2,11 @@ import 'package:ernaehrung/android/components/meal_page_text/days_component.dart
import 'package:ernaehrung/android/config/statistics.dart'; import 'package:ernaehrung/android/config/statistics.dart';
import 'package:ernaehrung/android/pages/nav_pages/progress_page.dart'; import 'package:ernaehrung/android/pages/nav_pages/progress_page.dart';
import 'package:ernaehrung/android/pages/nav_pages/today_page.dart'; import 'package:ernaehrung/android/pages/nav_pages/today_page.dart';
import 'package:ernaehrung/android/pages/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_profile_picture/flutter_profile_picture.dart';
import 'package:hive/hive.dart';
import '../../models/user.dart';
import 'meal_plan_page.dart'; import 'meal_plan_page.dart';
class MainPage extends StatefulWidget { class MainPage extends StatefulWidget {
@ -24,9 +28,10 @@ class MainPageState extends State<MainPage> {
void onTap(int index) { void onTap(int index) {
setState(() { setState(() {
currentIndex = index; currentIndex = index;
if(currentIndex == 1){ if (currentIndex == 1) {
StatisticsService.instance.updateStatisticsTodayBoxByTimespan(TimeSpan.day); StatisticsService.instance
}else if(currentIndex == 2){ .updateStatisticsTodayBoxByTimespan(TimeSpan.day);
} else if (currentIndex == 2) {
StatisticsService.instance.updateProgressBoxValues(); StatisticsService.instance.updateProgressBoxValues();
} }
pages[currentIndex]; pages[currentIndex];
@ -35,14 +40,32 @@ class MainPageState extends State<MainPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Box box = Hive.box<User>("USER_BOX");
return Scaffold( return Scaffold(
extendBodyBehindAppBar: false, extendBodyBehindAppBar: false,
appBar: AppBar( appBar: AppBar(
title: Text( title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
pages[currentIndex].title, pages[currentIndex].title,
style: const TextStyle( style: const TextStyle(
color: Colors.black color: Colors.black
), ),
),
ElevatedButton(
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SettingsPage()),
);
},
child: ProfilePicture(
name: "${box.get("FIRST_NAME_FIELD")}",
radius: 16,
fontsize: 14,
))
],
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
foregroundColor: Colors.grey.shade400, foregroundColor: Colors.grey.shade400,

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import '../components/form/form_builder.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({Key? key}) : super(key: key);
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: BackButton(
onPressed: () async => Navigator.of(context).pop(),
),
title: const Text('Einstellungen'),
),
body: const FormBuilderComponent(lockTextFields: true),
);
}
}

View File

@ -1,10 +1,5 @@
import 'package:ernaehrung/android/pages/nav_pages/main_page.dart'; import 'package:ernaehrung/android/components/form/form_builder.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:hive_flutter/hive_flutter.dart';
import '../models/user.dart';
class OnboardingPage extends StatefulWidget { class OnboardingPage extends StatefulWidget {
const OnboardingPage({Key? key}) : super(key: key); const OnboardingPage({Key? key}) : super(key: key);
@ -14,193 +9,15 @@ class OnboardingPage extends StatefulWidget {
} }
class _OnboardingPageState extends State<OnboardingPage> { class _OnboardingPageState extends State<OnboardingPage> {
InputDecoration decoration(String hintText) {
return InputDecoration(
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0))),
filled: true,
hintStyle: TextStyle(color: Colors.grey.shade400),
hintText: hintText,
labelText: hintText,
fillColor: Colors.white70);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final formKey = GlobalKey<FormBuilderState>();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text("Welcome"), title: const Text("Welcome"),
backgroundColor: Colors.grey.shade100, backgroundColor: Colors.grey.shade100,
), ),
body: FormBuilder( body: const FormBuilderComponent(lockTextFields: false,)
key: formKey,
child: Padding(
padding: const EdgeInsets.all(8),
child: SingleChildScrollView(
child: Column(
children: [
Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
child: FormBuilderTextField(
name: 'vorname',
decoration: decoration("Vorname"),
keyboardType: TextInputType.text,
maxLength: 30,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.minLength(2,
errorText:
"Die Name sollte mindestens 2 Zeichen lang sein"),
FormBuilderValidators.maxLength(30,
errorText:
"Die Name sollte maximal 30 Zeichen lang sein")
]),
),
),
Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
child: FormBuilderTextField(
name: 'nachname',
decoration: decoration("Nachname"),
keyboardType: TextInputType.text,
maxLength: 30,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.minLength(2,
errorText:
"Die Nachname sollte mindestens 2 Zeichen lang sein"),
FormBuilderValidators.maxLength(30,
errorText:
"Die Nachname sollte maximal 30 Zeichen lang sein")
]),
),
),
Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
child: FormBuilderTextField(
name: 'gewicht',
decoration: decoration("Gewicht"),
keyboardType: TextInputType.number,
maxLength: 7,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.numeric(
errorText:
"Der Gewicht sollte mindestens 10 kg sein"),
FormBuilderValidators.max(200,
errorText:
"Der Gewicht sollte maximal 200 kg sein"),
FormBuilderValidators.min(10,
errorText:
"Der Gewicht sollte mindestens 10 kg sein")
]),
),
),
Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
child: FormBuilderTextField(
name: 'groesse',
decoration: decoration("Größe"),
keyboardType: TextInputType.number,
maxLength: 7,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.numeric(
errorText: "Die Größe sollte mindestens 60cm sein"),
FormBuilderValidators.max(230,
errorText: "Die Größe sollte maximal 230cm sein"),
FormBuilderValidators.min(60,
errorText: "Die Größe sollte mindestens 60cm sein")
]),
),
),
Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
child: FormBuilderTextField(
name: 'alter',
decoration: decoration("Alter"),
keyboardType: TextInputType.number,
maxLength: 7,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.numeric(
errorText:
"Das Alter sollte mindestens 6 Jahre alt sein"),
FormBuilderValidators.max(99,
errorText:
"Das Alter sollte maximal 99 Jahre alt sein"),
FormBuilderValidators.min(6,
errorText:
"Das Alter sollte mindestens 6 Jahre alt sein")
]),
),
),
Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
child: FormBuilderTextField(
name: 'kalorien',
decoration:
decoration("gewünschte Kalorienzufuhr: min. 1000"),
keyboardType: TextInputType.number,
maxLength: 7,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.numeric(
errorText:
"Die Kalorienanzahl sollte mindestens 1000 Kcal sein"),
FormBuilderValidators.max(30000),
FormBuilderValidators.min(1000,
errorText:
"Die Kalorienanzahl sollte mindestens 1000 Kcal sein")
]),
),
),
Container(
margin: const EdgeInsets.symmetric(
vertical: 8, horizontal: 0),
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(40), //
backgroundColor: const Color(0xFF6E7BFB),
foregroundColor: const Color(0xFFffffff),
shape: const StadiumBorder(),
),
onPressed: () {
final Box box = Hive.box<User>("USER_BOX");
box.put("USER", User(
formKey.currentState?.fields['vorname']?.value,
formKey.currentState?.fields['nachname']?.value,
int.parse(formKey.currentState?.fields['gewicht']?.value),
int.parse(formKey.currentState?.fields['groesse']?.value),
int.parse(formKey.currentState?.fields['alter']?.value),
int.parse(formKey.currentState?.fields['kalorien']?.value)
));
Navigator
.of(context)
.pushReplacement(MaterialPageRoute(builder: (BuildContext context) => const MainPage()));
},
child: const Text("Eingaben bestätigen"),
)),
],
),
),
)),
); );
} }
} }

View File

@ -299,6 +299,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_profile_picture:
dependency: "direct main"
description:
name: flutter_profile_picture
sha256: "067caecef0d7162d3858eaca3c5859cbbb21b3e5704c2bc5b96b50ffe267f45d"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter

View File

@ -50,6 +50,8 @@ dependencies:
flutter_form_builder: ^8.0.0 flutter_form_builder: ^8.0.0
form_builder_validators: ^8.0.0 form_builder_validators: ^8.0.0
fluttertoast: ^8.0.7 fluttertoast: ^8.0.7
flutter_profile_picture: ^2.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: