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
|
||||
|
||||
- Fixed avatar image zooming when constrained beyond it's size
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:example/utils/example_profile_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
@ -44,9 +42,6 @@ class _ProfileExampleState extends State<ProfileExample> {
|
|||
_user = User(
|
||||
firstName: 'Firstname',
|
||||
lastName: 'Lastname',
|
||||
image: Uint8List.fromList(
|
||||
[],
|
||||
),
|
||||
profileData: profileData,
|
||||
);
|
||||
}
|
||||
|
@ -58,57 +53,62 @@ class _ProfileExampleState extends State<ProfileExample> {
|
|||
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 400,
|
||||
width: 800,
|
||||
child: ProfilePage(
|
||||
showItems: false,
|
||||
prioritizedItems: const ['remarks', 'about'],
|
||||
wrapViewOptions: WrapViewOptions(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 16,
|
||||
),
|
||||
bottomActionText: 'Log out',
|
||||
itemBuilderOptions: ItemBuilderOptions(
|
||||
//no label for email
|
||||
validators: {
|
||||
'first_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'last_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'email': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
),
|
||||
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,
|
||||
),
|
||||
child: ProfilePage(
|
||||
changePasswordConfig:
|
||||
const ChangePasswordConfig(enablePasswordChange: true),
|
||||
wrapViewOptions: WrapViewOptions(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 16,
|
||||
),
|
||||
bottomActionText: 'Log out',
|
||||
itemBuilderOptions: ItemBuilderOptions(
|
||||
//no label for email
|
||||
validators: {
|
||||
'first_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'last_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'email': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
inputDecorationField: {
|
||||
'password_1': const InputDecoration(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: 60,
|
||||
maxWidth: 200,
|
||||
),
|
||||
),
|
||||
'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}) {
|
||||
debugPrint('Updating avatar');
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> changePassword(String password) {
|
||||
debugPrint(password);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,26 +21,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image
|
||||
sha256: be69fd8b429d48807102cee6ab4106a55e1f2f3e79a1b83abb8572bc06c64f26
|
||||
sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
version: "3.3.1"
|
||||
cached_network_image_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_platform_interface
|
||||
sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7
|
||||
sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
version: "4.0.0"
|
||||
cached_network_image_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_web
|
||||
sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0
|
||||
sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "1.1.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -61,10 +61,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.2"
|
||||
version: "1.18.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -110,22 +110,23 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3"
|
||||
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
|
||||
url: "https://pub.dev"
|
||||
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:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -140,7 +141,7 @@ packages:
|
|||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.5"
|
||||
version: "1.1.6"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -162,6 +163,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -190,18 +199,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.10.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: octo_image
|
||||
sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143"
|
||||
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "2.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -266,14 +275,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -339,18 +340,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.0"
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -379,10 +380,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
version: "0.6.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -411,10 +412,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.4-beta"
|
||||
version: "0.3.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -432,5 +433,5 @@ packages:
|
|||
source: hosted
|
||||
version: "0.2.0+2"
|
||||
sdks:
|
||||
dart: ">=3.1.0-185.0.dev <4.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
dart: ">=3.2.0-194.0.dev <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
|
|
@ -11,4 +11,5 @@ export 'src/widgets/avatar/avatar.dart';
|
|||
export 'src/services/profile_service.dart';
|
||||
export 'src/widgets/item_builder/item_builder.dart';
|
||||
export 'src/models/user.dart';
|
||||
export 'src/models/change_password_config.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, {
|
||||
required Function(bool isUploading) onUploadStateChanged,
|
||||
});
|
||||
|
||||
FutureOr<void> changePassword(String password);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
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';
|
||||
|
||||
/// ItemBuilder is used to set the standard textfield for each undefined users data item.
|
||||
|
@ -22,10 +23,9 @@ class ItemBuilder {
|
|||
text: '${value ?? ''}',
|
||||
);
|
||||
|
||||
late InputDecoration inputDecoration;
|
||||
|
||||
inputDecoration =
|
||||
var inputDecoration =
|
||||
options.inputDecorationField?[key] ?? options.inputDecoration;
|
||||
|
||||
var formFieldKey = GlobalKey<FormFieldState>();
|
||||
return TextFormField(
|
||||
style: options.inputTextStyle,
|
||||
|
@ -47,4 +47,22 @@ class ItemBuilder {
|
|||
}
|
||||
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
|
||||
|
||||
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/services/profile_service.dart';
|
||||
import 'package:flutter_profile/src/widgets/item_builder/item_builder.dart';
|
||||
|
@ -46,6 +47,8 @@ class ProfilePage extends StatefulWidget {
|
|||
this.wrapViewOptions,
|
||||
this.extraWidgets,
|
||||
this.formKey,
|
||||
this.changePasswordConfig =
|
||||
const ChangePasswordConfig(enablePasswordChange: false),
|
||||
}) : super(key: key);
|
||||
|
||||
/// User containing all the user data.
|
||||
|
@ -96,6 +99,9 @@ class ProfilePage extends StatefulWidget {
|
|||
/// Use the form key to save on any custom callback
|
||||
final GlobalKey<FormState>? formKey;
|
||||
|
||||
/// Configuration to give the user the option to change his/her password.
|
||||
final ChangePasswordConfig changePasswordConfig;
|
||||
|
||||
@override
|
||||
State<ProfilePage> createState() => _ProfilePageState();
|
||||
}
|
||||
|
@ -123,6 +129,7 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||
extraWidgets: widget.extraWidgets,
|
||||
formKey: widget.formKey,
|
||||
avatarBackgroundColor: widget.avatarBackgroundColor,
|
||||
changePasswordConfig: widget.changePasswordConfig,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
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/services/profile_service.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_options.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_style.dart';
|
||||
|
||||
|
@ -31,6 +33,8 @@ class ProfileWrapper extends StatefulWidget {
|
|||
this.wrapItemsBuilder,
|
||||
this.formKey,
|
||||
this.extraWidgets,
|
||||
this.changePasswordConfig =
|
||||
const ChangePasswordConfig(enablePasswordChange: false),
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -50,6 +54,7 @@ class ProfileWrapper extends StatefulWidget {
|
|||
final Widget Function(BuildContext context, Widget child)? wrapItemsBuilder;
|
||||
final Map<String, Widget>? extraWidgets;
|
||||
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.
|
||||
final List<String> prioritizedItems;
|
||||
|
@ -193,63 +198,85 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
|||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: widget.style.pagePadding,
|
||||
child: Column(
|
||||
children: [
|
||||
if (widget.showAvatar) ...[
|
||||
InkWell(
|
||||
onTap: () => widget.service.uploadImage(
|
||||
context,
|
||||
onUploadStateChanged: (isUploading) => setState(
|
||||
() {
|
||||
_isUploadingImage = isUploading;
|
||||
child: SingleChildScrollView(
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Padding(
|
||||
padding: widget.style.pagePadding,
|
||||
child: Column(
|
||||
children: [
|
||||
if (widget.showAvatar) ...[
|
||||
InkWell(
|
||||
onTap: () => widget.service.uploadImage(
|
||||
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(
|
||||
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.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(),
|
||||
],
|
||||
],
|
||||
],
|
||||
if (widget.bottomActionText == null &&
|
||||
!widget.changePasswordConfig.enablePasswordChange) ...[
|
||||
const Spacer(),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
14
pubspec.yaml
14
pubspec.yaml
|
@ -1,14 +1,22 @@
|
|||
name: flutter_profile
|
||||
description: Flutter profile package
|
||||
version: 1.1.6
|
||||
version: 1.2.0
|
||||
repository: https://github.com/Iconica-Development/flutter_profile
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.6 <3.0.0"
|
||||
sdk: ^3.0.0
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
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:
|
||||
sdk: flutter
|
||||
|
||||
|
|
|
@ -25,4 +25,7 @@ class TestProfileService extends ProfileService {
|
|||
BuildContext context, {
|
||||
required Function(bool isUploading) onUploadStateChanged,
|
||||
}) {}
|
||||
|
||||
@override
|
||||
FutureOr<void> changePassword(String password) {}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue