From 6085297b1c00e46f7771bbdbfafab45bdc03109d Mon Sep 17 00:00:00 2001 From: bogdan <1926167@stud.hs-mannheim.de> Date: Sat, 3 Jun 2023 00:40:40 +0200 Subject: [PATCH] settings page --- .dart_tool/package_config.json | 8 +- .dart_tool/package_config_subset | 4 + .flutter-plugins-dependencies | 2 +- lib/android/components/form/form_builder.dart | 148 ++++++++++++++ .../form_builder_text_field_component.dart | 96 +++++++++ lib/android/models/form_builder.dart | 26 +++ lib/android/pages/nav_pages/main_page.dart | 31 ++- lib/android/pages/settings.dart | 25 +++ lib/android/pages/welcome.dart | 187 +----------------- pubspec.lock | 8 + pubspec.yaml | 1 + 11 files changed, 345 insertions(+), 191 deletions(-) create mode 100644 lib/android/components/form/form_builder.dart create mode 100644 lib/android/components/form/form_builder_text_field_component.dart create mode 100644 lib/android/models/form_builder.dart create mode 100644 lib/android/pages/settings.dart diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index efef7a0..9cfbe85 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json @@ -229,6 +229,12 @@ "packageUri": "lib/", "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", "rootUri": "file:///usr/local/Caskroom/flutter/3.7.7/flutter/packages/flutter_test", @@ -608,7 +614,7 @@ "languageVersion": "2.19" } ], - "generated": "2023-06-01T16:44:17.796384Z", + "generated": "2023-06-02T11:38:29.193689Z", "generator": "pub", "generatorVersion": "2.19.6" } diff --git a/.dart_tool/package_config_subset b/.dart_tool/package_config_subset index fe09e4c..76af3b9 100644 --- a/.dart_tool/package_config_subset +++ b/.dart_tool/package_config_subset @@ -142,6 +142,10 @@ flutter_lints 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/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/ form_builder_validators 2.19 file:///Users/bogdan/.pub-cache/hosted/pub.dev/form_builder_validators-8.6.1/ diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 1311292..3457623 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_foundation","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_foundation-2.2.2/","native_build":true,"dependencies":[]}],"android":[{"name":"path_provider_android","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_android-2.0.27/","native_build":true,"dependencies":[]}],"macos":[{"name":"path_provider_foundation","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_foundation-2.2.2/","native_build":true,"dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.10/","native_build":false,"dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.6/","native_build":false,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-06-01 21:59:58.395996","version":"3.7.9"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_foundation","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_foundation-2.2.2/","native_build":true,"dependencies":[]}],"android":[{"name":"path_provider_android","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_android-2.0.27/","native_build":true,"dependencies":[]}],"macos":[{"name":"path_provider_foundation","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_foundation-2.2.2/","native_build":true,"dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_linux-2.1.10/","native_build":false,"dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/bogdan/.pub-cache/hosted/pub.dev/path_provider_windows-2.1.6/","native_build":false,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-06-02 13:45:10.283479","version":"3.7.9"} \ No newline at end of file diff --git a/lib/android/components/form/form_builder.dart b/lib/android/components/form/form_builder.dart new file mode 100644 index 0000000..9874c44 --- /dev/null +++ b/lib/android/components/form/form_builder.dart @@ -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 createState() => _FormBuilderComponentState(); +} + +class _FormBuilderComponentState extends State { + final formKey = GlobalKey(); + + final List 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_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"), + )), + ], + )), + )); + } +} diff --git a/lib/android/components/form/form_builder_text_field_component.dart b/lib/android/components/form/form_builder_text_field_component.dart new file mode 100644 index 0000000..1c18a35 --- /dev/null +++ b/lib/android/components/form/form_builder_text_field_component.dart @@ -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 createState() => _FormBuilderTextFieldComponentState(); +} + +class _FormBuilderTextFieldComponentState extends State { + 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) + ]), + ); + } +} diff --git a/lib/android/models/form_builder.dart b/lib/android/models/form_builder.dart new file mode 100644 index 0000000..ce4502f --- /dev/null +++ b/lib/android/models/form_builder.dart @@ -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); +} diff --git a/lib/android/pages/nav_pages/main_page.dart b/lib/android/pages/nav_pages/main_page.dart index d33c526..faa05c0 100644 --- a/lib/android/pages/nav_pages/main_page.dart +++ b/lib/android/pages/nav_pages/main_page.dart @@ -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/pages/nav_pages/progress_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_profile_picture/flutter_profile_picture.dart'; +import 'package:hive/hive.dart'; +import '../../models/user.dart'; import 'meal_plan_page.dart'; class MainPage extends StatefulWidget { @@ -24,9 +28,10 @@ class MainPageState extends State { void onTap(int index) { setState(() { currentIndex = index; - if(currentIndex == 1){ - StatisticsService.instance.updateStatisticsTodayBoxByTimespan(TimeSpan.day); - }else if(currentIndex == 2){ + if (currentIndex == 1) { + StatisticsService.instance + .updateStatisticsTodayBoxByTimespan(TimeSpan.day); + } else if (currentIndex == 2) { StatisticsService.instance.updateProgressBoxValues(); } pages[currentIndex]; @@ -35,10 +40,28 @@ class MainPageState extends State { @override Widget build(BuildContext context) { + final Box box = Hive.box("USER_BOX"); return Scaffold( extendBodyBehindAppBar: false, appBar: AppBar( - title: Text(pages[currentIndex].title), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(pages[currentIndex].title), + 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, foregroundColor: Colors.grey.shade400, elevation: 0, diff --git a/lib/android/pages/settings.dart b/lib/android/pages/settings.dart new file mode 100644 index 0000000..0e86108 --- /dev/null +++ b/lib/android/pages/settings.dart @@ -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 createState() => _SettingsPageState(); +} + +class _SettingsPageState extends State { + @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), + ); + } +} \ No newline at end of file diff --git a/lib/android/pages/welcome.dart b/lib/android/pages/welcome.dart index b70d208..0669548 100644 --- a/lib/android/pages/welcome.dart +++ b/lib/android/pages/welcome.dart @@ -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_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 { const OnboardingPage({Key? key}) : super(key: key); @@ -14,193 +9,15 @@ class OnboardingPage extends StatefulWidget { } class _OnboardingPageState extends State { - 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 Widget build(BuildContext context) { - final formKey = GlobalKey(); - return Scaffold( appBar: AppBar( title: const Text("Welcome"), backgroundColor: Colors.grey.shade100, ), - body: FormBuilder( - 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_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"), - )), - ], - ), - ), - )), + body: const FormBuilderComponent(lockTextFields: false,) ); } } diff --git a/pubspec.lock b/pubspec.lock index a0ea314..82401db 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -299,6 +299,14 @@ packages: description: flutter source: sdk 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: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 79a821b..4e068ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: basic_utils: ^5.5.4 flutter_form_builder: ^8.0.0 form_builder_validators: ^8.0.0 + flutter_profile_picture: ^2.0.0 dev_dependencies: