mirror of
https://github.com/Iconica-Development/flutter_profile.git
synced 2025-05-19 01:03:45 +02:00
feat(password): Add the abiltity for the user to change its password
This commit is contained in:
parent
661a3fc331
commit
e98fb1bdd8
13 changed files with 410 additions and 155 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
- Added the posibilty to enable the user to change it's password.
|
||||||
|
|
||||||
## 1.1.6
|
## 1.1.6
|
||||||
|
|
||||||
- Fixed avatar image zooming when constrained beyond it's size
|
- Fixed avatar image zooming when constrained beyond it's size
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:example/utils/example_profile_service.dart';
|
import 'package:example/utils/example_profile_service.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
@ -44,9 +42,6 @@ class _ProfileExampleState extends State<ProfileExample> {
|
||||||
_user = User(
|
_user = User(
|
||||||
firstName: 'Firstname',
|
firstName: 'Firstname',
|
||||||
lastName: 'Lastname',
|
lastName: 'Lastname',
|
||||||
image: Uint8List.fromList(
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
profileData: profileData,
|
profileData: profileData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -58,57 +53,62 @@ class _ProfileExampleState extends State<ProfileExample> {
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: ProfilePage(
|
||||||
children: [
|
changePasswordConfig:
|
||||||
SizedBox(
|
const ChangePasswordConfig(enablePasswordChange: true),
|
||||||
height: 400,
|
wrapViewOptions: WrapViewOptions(
|
||||||
width: 800,
|
direction: Axis.horizontal,
|
||||||
child: ProfilePage(
|
spacing: 16,
|
||||||
showItems: false,
|
),
|
||||||
prioritizedItems: const ['remarks', 'about'],
|
bottomActionText: 'Log out',
|
||||||
wrapViewOptions: WrapViewOptions(
|
itemBuilderOptions: ItemBuilderOptions(
|
||||||
direction: Axis.horizontal,
|
//no label for email
|
||||||
spacing: 16,
|
validators: {
|
||||||
),
|
'first_name': (String? value) {
|
||||||
bottomActionText: 'Log out',
|
if (value == null || value.isEmpty) {
|
||||||
itemBuilderOptions: ItemBuilderOptions(
|
return 'Field empty';
|
||||||
//no label for email
|
}
|
||||||
validators: {
|
return null;
|
||||||
'first_name': (String? value) {
|
},
|
||||||
if (value == null || value.isEmpty) {
|
'last_name': (String? value) {
|
||||||
return 'Field empty';
|
if (value == null || value.isEmpty) {
|
||||||
}
|
return 'Field empty';
|
||||||
return null;
|
}
|
||||||
},
|
return null;
|
||||||
'last_name': (String? value) {
|
},
|
||||||
if (value == null || value.isEmpty) {
|
'email': (String? value) {
|
||||||
return 'Field empty';
|
if (value == null || value.isEmpty) {
|
||||||
}
|
return 'Field empty';
|
||||||
return null;
|
}
|
||||||
},
|
return null;
|
||||||
'email': (String? value) {
|
},
|
||||||
if (value == null || value.isEmpty) {
|
},
|
||||||
return 'Field empty';
|
inputDecorationField: {
|
||||||
}
|
'password_1': const InputDecoration(
|
||||||
return null;
|
constraints: BoxConstraints(
|
||||||
},
|
maxHeight: 60,
|
||||||
},
|
maxWidth: 200,
|
||||||
),
|
|
||||||
user: _user,
|
|
||||||
service: ExampleProfileService(),
|
|
||||||
style: ProfileStyle(
|
|
||||||
avatarTextStyle: const TextStyle(fontSize: 20),
|
|
||||||
pagePadding: EdgeInsets.only(
|
|
||||||
top: 50,
|
|
||||||
bottom: 50,
|
|
||||||
left: width * 0.1,
|
|
||||||
right: width * 0.1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
'password_2': const InputDecoration(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxHeight: 60,
|
||||||
|
maxWidth: 200,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
user: _user,
|
||||||
|
service: ExampleProfileService(),
|
||||||
|
style: ProfileStyle(
|
||||||
|
avatarTextStyle: const TextStyle(fontSize: 20),
|
||||||
|
pagePadding: EdgeInsets.only(
|
||||||
|
top: 50,
|
||||||
|
bottom: 50,
|
||||||
|
left: width * 0.1,
|
||||||
|
right: width * 0.1,
|
||||||
),
|
),
|
||||||
const Text('test')
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,4 +28,9 @@ class ExampleProfileService extends ProfileService {
|
||||||
{required Function(bool isUploading) onUploadStateChanged}) {
|
{required Function(bool isUploading) onUploadStateChanged}) {
|
||||||
debugPrint('Updating avatar');
|
debugPrint('Updating avatar');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<void> changePassword(String password) {
|
||||||
|
debugPrint(password);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,26 +21,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cached_network_image
|
name: cached_network_image
|
||||||
sha256: be69fd8b429d48807102cee6ab4106a55e1f2f3e79a1b83abb8572bc06c64f26
|
sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.2"
|
version: "3.3.1"
|
||||||
cached_network_image_platform_interface:
|
cached_network_image_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cached_network_image_platform_interface
|
name: cached_network_image_platform_interface
|
||||||
sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7
|
sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "4.0.0"
|
||||||
cached_network_image_web:
|
cached_network_image_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cached_network_image_web
|
name: cached_network_image_web
|
||||||
sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0
|
sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.1.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -61,10 +61,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.2"
|
version: "1.18.0"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -110,22 +110,23 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_blurhash:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: flutter_blurhash
|
|
||||||
sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.7.0"
|
|
||||||
flutter_cache_manager:
|
flutter_cache_manager:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_cache_manager
|
name: flutter_cache_manager
|
||||||
sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3"
|
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "3.3.1"
|
||||||
|
flutter_input_library:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: "2.7.0"
|
||||||
|
resolved-ref: "8eb1d80a9f08be0b7fe70078104d1a8851083edd"
|
||||||
|
url: "https://github.com/Iconica-Development/flutter_input_library"
|
||||||
|
source: git
|
||||||
|
version: "2.7.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -140,7 +141,7 @@ packages:
|
||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "1.0.5"
|
version: "1.1.6"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -162,6 +163,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
intl:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.18.1"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -190,18 +199,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.10.0"
|
||||||
octo_image:
|
octo_image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: octo_image
|
name: octo_image
|
||||||
sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143"
|
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "2.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -266,14 +275,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
pedantic:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pedantic
|
|
||||||
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.11.1"
|
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -339,18 +340,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -379,10 +380,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.1"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -411,10 +412,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4-beta"
|
version: "0.3.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -432,5 +433,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+2"
|
version: "0.2.0+2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.1.0-185.0.dev <4.0.0"
|
dart: ">=3.2.0-194.0.dev <4.0.0"
|
||||||
flutter: ">=3.3.0"
|
flutter: ">=3.10.0"
|
||||||
|
|
|
@ -11,4 +11,5 @@ export 'src/widgets/avatar/avatar.dart';
|
||||||
export 'src/services/profile_service.dart';
|
export 'src/services/profile_service.dart';
|
||||||
export 'src/widgets/item_builder/item_builder.dart';
|
export 'src/widgets/item_builder/item_builder.dart';
|
||||||
export 'src/models/user.dart';
|
export 'src/models/user.dart';
|
||||||
|
export 'src/models/change_password_config.dart';
|
||||||
export 'src/widgets/item_builder/item_builder_options.dart';
|
export 'src/widgets/item_builder/item_builder_options.dart';
|
||||||
|
|
44
lib/src/models/change_password_config.dart
Normal file
44
lib/src/models/change_password_config.dart
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Configuration to enable to user to change his password in the profilescreen.
|
||||||
|
class ChangePasswordConfig {
|
||||||
|
const ChangePasswordConfig({
|
||||||
|
required this.enablePasswordChange,
|
||||||
|
this.title = 'Change password',
|
||||||
|
this.titleStyle,
|
||||||
|
this.underTitle =
|
||||||
|
'You van make the password more secure using upper and lower case '
|
||||||
|
'letter, numbers and special characters.',
|
||||||
|
this.underTitleStyle,
|
||||||
|
this.saveButtonBuilder,
|
||||||
|
this.fieldRequiredErrorText = 'Field required',
|
||||||
|
this.notEqualErrorText = 'Password have to be equal',
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Enables the textfields for the user to provide a new password.
|
||||||
|
final bool enablePasswordChange;
|
||||||
|
|
||||||
|
/// Text for the title above the textfields.
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
/// Textstyle of the title.
|
||||||
|
final TextStyle? titleStyle;
|
||||||
|
|
||||||
|
/// Text for the undertitle just above the textfields.
|
||||||
|
final String underTitle;
|
||||||
|
|
||||||
|
/// Textstyle for the undertitle
|
||||||
|
final TextStyle? underTitleStyle;
|
||||||
|
|
||||||
|
/// Ability to override the standard 'save password' button.
|
||||||
|
final Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
void Function() onTap,
|
||||||
|
)? saveButtonBuilder;
|
||||||
|
|
||||||
|
/// Error text to be shown when either of the textfields is empty.
|
||||||
|
final String fieldRequiredErrorText;
|
||||||
|
|
||||||
|
/// Error text to be shown when the second password isn't equal to the first password.
|
||||||
|
final String notEqualErrorText;
|
||||||
|
}
|
|
@ -24,4 +24,6 @@ abstract class ProfileService {
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required Function(bool isUploading) onUploadStateChanged,
|
required Function(bool isUploading) onUploadStateChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
FutureOr<void> changePassword(String password);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_input_library/flutter_input_library.dart';
|
||||||
import 'package:flutter_profile/src/widgets/item_builder/item_builder_options.dart';
|
import 'package:flutter_profile/src/widgets/item_builder/item_builder_options.dart';
|
||||||
|
|
||||||
/// ItemBuilder is used to set the standard textfield for each undefined users data item.
|
/// ItemBuilder is used to set the standard textfield for each undefined users data item.
|
||||||
|
@ -22,10 +23,9 @@ class ItemBuilder {
|
||||||
text: '${value ?? ''}',
|
text: '${value ?? ''}',
|
||||||
);
|
);
|
||||||
|
|
||||||
late InputDecoration inputDecoration;
|
var inputDecoration =
|
||||||
|
|
||||||
inputDecoration =
|
|
||||||
options.inputDecorationField?[key] ?? options.inputDecoration;
|
options.inputDecorationField?[key] ?? options.inputDecoration;
|
||||||
|
|
||||||
var formFieldKey = GlobalKey<FormFieldState>();
|
var formFieldKey = GlobalKey<FormFieldState>();
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
style: options.inputTextStyle,
|
style: options.inputTextStyle,
|
||||||
|
@ -47,4 +47,22 @@ class ItemBuilder {
|
||||||
}
|
}
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildPassword(
|
||||||
|
String key,
|
||||||
|
Function(String?) onChanged,
|
||||||
|
String? Function(String?) validator,
|
||||||
|
) {
|
||||||
|
var inputDecoration =
|
||||||
|
options.inputDecorationField?[key] ?? options.inputDecoration;
|
||||||
|
|
||||||
|
return FlutterFormInputPassword(
|
||||||
|
style: options.inputTextStyle,
|
||||||
|
decoration: inputDecoration,
|
||||||
|
onChanged: onChanged,
|
||||||
|
enabled: !options.readOnly,
|
||||||
|
validator: (value) =>
|
||||||
|
validator(value) ?? options.validators?[key]?.call(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
135
lib/src/widgets/profile/change_password_widget.dart
Normal file
135
lib/src/widgets/profile/change_password_widget.dart
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_profile/flutter_profile.dart';
|
||||||
|
|
||||||
|
class ChangePassword extends StatefulWidget {
|
||||||
|
const ChangePassword({
|
||||||
|
required this.config,
|
||||||
|
required this.service,
|
||||||
|
this.wrapViewOptions,
|
||||||
|
this.wrapItemsBuilder,
|
||||||
|
this.itemBuilder,
|
||||||
|
this.itemBuilderOptions,
|
||||||
|
this.style = const ProfileStyle(),
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ChangePasswordConfig config;
|
||||||
|
final WrapViewOptions? wrapViewOptions;
|
||||||
|
final ItemBuilder? itemBuilder;
|
||||||
|
final ItemBuilderOptions? itemBuilderOptions;
|
||||||
|
final Widget Function(BuildContext context, Widget child)? wrapItemsBuilder;
|
||||||
|
final ProfileStyle style;
|
||||||
|
final ProfileService service;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ChangePassword> createState() => _ChangePasswordState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ChangePasswordState extends State<ChangePassword> {
|
||||||
|
late var config = widget.config;
|
||||||
|
|
||||||
|
late final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
late final Widget? changePasswordChild;
|
||||||
|
|
||||||
|
String? password1;
|
||||||
|
String? password2;
|
||||||
|
|
||||||
|
late var builder = widget.itemBuilder ??
|
||||||
|
ItemBuilder(
|
||||||
|
options: widget.itemBuilderOptions ?? ItemBuilderOptions(),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
var changePasswordItems = Wrap(
|
||||||
|
alignment: widget.wrapViewOptions?.wrapAlignment ?? WrapAlignment.start,
|
||||||
|
direction: widget.wrapViewOptions?.direction ?? Axis.vertical,
|
||||||
|
spacing: widget.wrapViewOptions?.spacing ?? 0,
|
||||||
|
runSpacing: widget.wrapViewOptions?.runSpacing ?? 0,
|
||||||
|
clipBehavior: widget.wrapViewOptions?.clipBehavior ?? Clip.none,
|
||||||
|
children: [
|
||||||
|
builder.buildPassword(
|
||||||
|
'password_1',
|
||||||
|
(value) => password1 = value,
|
||||||
|
(value) {
|
||||||
|
if (password1?.isEmpty ?? true) {
|
||||||
|
return config.fieldRequiredErrorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
builder.buildPassword(
|
||||||
|
'password_2',
|
||||||
|
(value) => password2 = value,
|
||||||
|
(value) {
|
||||||
|
if (password2?.isEmpty ?? true) {
|
||||||
|
return config.fieldRequiredErrorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password2 != password1) {
|
||||||
|
return config.notEqualErrorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
changePasswordChild =
|
||||||
|
widget.wrapItemsBuilder?.call(context, changePasswordItems) ??
|
||||||
|
changePasswordItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
|
void onTapSave() {
|
||||||
|
if ((_formKey.currentState?.validate() ?? false) && password2 != null) {
|
||||||
|
widget.service.changePassword(password2!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: widget.style.betweenDefaultItemPadding * 2.5,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
config.title,
|
||||||
|
style: config.titleStyle ?? theme.textTheme.titleMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
config.underTitle,
|
||||||
|
style: config.underTitleStyle ?? theme.textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: widget.style.betweenDefaultItemPadding,
|
||||||
|
),
|
||||||
|
changePasswordChild!,
|
||||||
|
SizedBox(
|
||||||
|
height: widget.style.betweenDefaultItemPadding * 2,
|
||||||
|
),
|
||||||
|
config.saveButtonBuilder?.call(
|
||||||
|
context,
|
||||||
|
() => onTapSave(),
|
||||||
|
) ??
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () => onTapSave(),
|
||||||
|
child: const Text('Save password'),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_profile/src/models/change_password_config.dart';
|
||||||
import 'package:flutter_profile/src/models/user.dart';
|
import 'package:flutter_profile/src/models/user.dart';
|
||||||
import 'package:flutter_profile/src/services/profile_service.dart';
|
import 'package:flutter_profile/src/services/profile_service.dart';
|
||||||
import 'package:flutter_profile/src/widgets/item_builder/item_builder.dart';
|
import 'package:flutter_profile/src/widgets/item_builder/item_builder.dart';
|
||||||
|
@ -46,6 +47,8 @@ class ProfilePage extends StatefulWidget {
|
||||||
this.wrapViewOptions,
|
this.wrapViewOptions,
|
||||||
this.extraWidgets,
|
this.extraWidgets,
|
||||||
this.formKey,
|
this.formKey,
|
||||||
|
this.changePasswordConfig =
|
||||||
|
const ChangePasswordConfig(enablePasswordChange: false),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// User containing all the user data.
|
/// User containing all the user data.
|
||||||
|
@ -96,6 +99,9 @@ class ProfilePage extends StatefulWidget {
|
||||||
/// Use the form key to save on any custom callback
|
/// Use the form key to save on any custom callback
|
||||||
final GlobalKey<FormState>? formKey;
|
final GlobalKey<FormState>? formKey;
|
||||||
|
|
||||||
|
/// Configuration to give the user the option to change his/her password.
|
||||||
|
final ChangePasswordConfig changePasswordConfig;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ProfilePage> createState() => _ProfilePageState();
|
State<ProfilePage> createState() => _ProfilePageState();
|
||||||
}
|
}
|
||||||
|
@ -123,6 +129,7 @@ class _ProfilePageState extends State<ProfilePage> {
|
||||||
extraWidgets: widget.extraWidgets,
|
extraWidgets: widget.extraWidgets,
|
||||||
formKey: widget.formKey,
|
formKey: widget.formKey,
|
||||||
avatarBackgroundColor: widget.avatarBackgroundColor,
|
avatarBackgroundColor: widget.avatarBackgroundColor,
|
||||||
|
changePasswordConfig: widget.changePasswordConfig,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_profile/src/models/change_password_config.dart';
|
||||||
import 'package:flutter_profile/src/models/user.dart';
|
import 'package:flutter_profile/src/models/user.dart';
|
||||||
import 'package:flutter_profile/src/services/profile_service.dart';
|
import 'package:flutter_profile/src/services/profile_service.dart';
|
||||||
import 'package:flutter_profile/src/widgets/avatar/avatar_wrapper.dart';
|
import 'package:flutter_profile/src/widgets/avatar/avatar_wrapper.dart';
|
||||||
import 'package:flutter_profile/src/widgets/item_builder/item_builder.dart';
|
import 'package:flutter_profile/src/widgets/item_builder/item_builder.dart';
|
||||||
import 'package:flutter_profile/src/widgets/item_builder/item_builder_options.dart';
|
import 'package:flutter_profile/src/widgets/item_builder/item_builder_options.dart';
|
||||||
import 'package:flutter_profile/src/widgets/item_builder/item_list.dart';
|
import 'package:flutter_profile/src/widgets/item_builder/item_list.dart';
|
||||||
|
import 'package:flutter_profile/src/widgets/profile/change_password_widget.dart';
|
||||||
import 'package:flutter_profile/src/widgets/profile/profile_page.dart';
|
import 'package:flutter_profile/src/widgets/profile/profile_page.dart';
|
||||||
import 'package:flutter_profile/src/widgets/profile/profile_style.dart';
|
import 'package:flutter_profile/src/widgets/profile/profile_style.dart';
|
||||||
|
|
||||||
|
@ -31,6 +33,8 @@ class ProfileWrapper extends StatefulWidget {
|
||||||
this.wrapItemsBuilder,
|
this.wrapItemsBuilder,
|
||||||
this.formKey,
|
this.formKey,
|
||||||
this.extraWidgets,
|
this.extraWidgets,
|
||||||
|
this.changePasswordConfig =
|
||||||
|
const ChangePasswordConfig(enablePasswordChange: false),
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -50,6 +54,7 @@ class ProfileWrapper extends StatefulWidget {
|
||||||
final Widget Function(BuildContext context, Widget child)? wrapItemsBuilder;
|
final Widget Function(BuildContext context, Widget child)? wrapItemsBuilder;
|
||||||
final Map<String, Widget>? extraWidgets;
|
final Map<String, Widget>? extraWidgets;
|
||||||
final GlobalKey<FormState>? formKey;
|
final GlobalKey<FormState>? formKey;
|
||||||
|
final ChangePasswordConfig changePasswordConfig;
|
||||||
|
|
||||||
/// Map keys of items that should be shown first before the default items and the rest of the items.
|
/// Map keys of items that should be shown first before the default items and the rest of the items.
|
||||||
final List<String> prioritizedItems;
|
final List<String> prioritizedItems;
|
||||||
|
@ -193,63 +198,85 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: Padding(
|
child: SingleChildScrollView(
|
||||||
padding: widget.style.pagePadding,
|
child: SizedBox(
|
||||||
child: Column(
|
height: MediaQuery.of(context).size.height,
|
||||||
children: [
|
child: Padding(
|
||||||
if (widget.showAvatar) ...[
|
padding: widget.style.pagePadding,
|
||||||
InkWell(
|
child: Column(
|
||||||
onTap: () => widget.service.uploadImage(
|
children: [
|
||||||
context,
|
if (widget.showAvatar) ...[
|
||||||
onUploadStateChanged: (isUploading) => setState(
|
InkWell(
|
||||||
() {
|
onTap: () => widget.service.uploadImage(
|
||||||
_isUploadingImage = isUploading;
|
context,
|
||||||
|
onUploadStateChanged: (isUploading) => setState(
|
||||||
|
() {
|
||||||
|
_isUploadingImage = isUploading;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: AvatarWrapper(
|
||||||
|
avatarBackgroundColor: widget.avatarBackgroundColor,
|
||||||
|
user: widget.user,
|
||||||
|
textStyle: widget.style.avatarTextStyle,
|
||||||
|
customAvatar: _isUploadingImage
|
||||||
|
? Container(
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.black,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: const CircularProgressIndicator(),
|
||||||
|
)
|
||||||
|
: widget.customAvatar,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: widget.style.betweenDefaultItemPadding,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (widget.showItems) Form(key: _formKey, child: child),
|
||||||
|
if (widget.changePasswordConfig.enablePasswordChange) ...[
|
||||||
|
Expanded(
|
||||||
|
child: ChangePassword(
|
||||||
|
config: widget.changePasswordConfig,
|
||||||
|
service: widget.service,
|
||||||
|
wrapViewOptions: widget.wrapViewOptions,
|
||||||
|
wrapItemsBuilder: widget.wrapItemsBuilder,
|
||||||
|
itemBuilder: widget.itemBuilder,
|
||||||
|
itemBuilderOptions: widget.itemBuilderOptions,
|
||||||
|
style: widget.style,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (widget.bottomActionText != null) ...[
|
||||||
|
SizedBox(
|
||||||
|
height: widget.style.betweenDefaultItemPadding,
|
||||||
|
),
|
||||||
|
if (!widget.changePasswordConfig.enablePasswordChange) ...[
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
widget.service.pageBottomAction();
|
||||||
},
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Text(
|
||||||
|
widget.bottomActionText!,
|
||||||
|
style: widget.style.bottomActionTextStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
child: AvatarWrapper(
|
if (widget.bottomActionText == null &&
|
||||||
avatarBackgroundColor: widget.avatarBackgroundColor,
|
!widget.changePasswordConfig.enablePasswordChange) ...[
|
||||||
user: widget.user,
|
const Spacer(),
|
||||||
textStyle: widget.style.avatarTextStyle,
|
]
|
||||||
customAvatar: _isUploadingImage
|
],
|
||||||
? Container(
|
),
|
||||||
width: 100,
|
),
|
||||||
height: 100,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.black,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const CircularProgressIndicator(),
|
|
||||||
)
|
|
||||||
: widget.customAvatar,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: widget.style.betweenDefaultItemPadding,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
if (widget.showItems) Form(key: _formKey, child: child),
|
|
||||||
if (widget.bottomActionText != null) ...[
|
|
||||||
SizedBox(
|
|
||||||
height: widget.style.betweenDefaultItemPadding,
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
widget.service.pageBottomAction();
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Text(
|
|
||||||
widget.bottomActionText!,
|
|
||||||
style: widget.style.bottomActionTextStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
] else ...[
|
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
14
pubspec.yaml
14
pubspec.yaml
|
@ -1,14 +1,22 @@
|
||||||
name: flutter_profile
|
name: flutter_profile
|
||||||
description: Flutter profile package
|
description: Flutter profile package
|
||||||
version: 1.1.6
|
version: 1.2.0
|
||||||
repository: https://github.com/Iconica-Development/flutter_profile
|
repository: https://github.com/Iconica-Development/flutter_profile
|
||||||
|
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.6 <3.0.0"
|
sdk: ^3.0.0
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
cached_network_image: ^3.2.2
|
cached_network_image: ^3.3.0
|
||||||
|
|
||||||
|
flutter_input_library:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_input_library
|
||||||
|
ref: 2.7.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
|
|
@ -25,4 +25,7 @@ class TestProfileService extends ProfileService {
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required Function(bool isUploading) onUploadStateChanged,
|
required Function(bool isUploading) onUploadStateChanged,
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<void> changePassword(String password) {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue