mirror of
https://github.com/Iconica-Development/flutter_profile.git
synced 2025-05-19 09:13:44 +02:00
Merge pull request #4 from Iconica-Development/feat/image-url-support
Feat/image url support
This commit is contained in:
commit
a910c4d381
10 changed files with 130 additions and 109 deletions
|
@ -5,3 +5,7 @@
|
||||||
## 0.0.2
|
## 0.0.2
|
||||||
|
|
||||||
* Add prioritizedItems option to display items at the top of the page.
|
* Add prioritizedItems option to display items at the top of the page.
|
||||||
|
|
||||||
|
## 0.0.3
|
||||||
|
|
||||||
|
* Add support for image URL (instead of in-memory image using image parameter from User model)
|
|
@ -37,12 +37,12 @@ class _ProfileExampleState extends State<ProfileExample> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_user = User(
|
_user = User(
|
||||||
'Firstname',
|
firstName: 'Firstname',
|
||||||
'Lastname',
|
lastName: 'Lastname',
|
||||||
Uint8List.fromList(
|
image: Uint8List.fromList(
|
||||||
[],
|
[],
|
||||||
),
|
),
|
||||||
profileData,
|
profileData: profileData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +53,10 @@ class _ProfileExampleState extends State<ProfileExample> {
|
||||||
bottomActionText: 'Log out',
|
bottomActionText: 'Log out',
|
||||||
itemBuilderOptions: ItemBuilderOptions(
|
itemBuilderOptions: ItemBuilderOptions(
|
||||||
inputDecorationField: {
|
inputDecorationField: {
|
||||||
'firstName': const InputDecoration(
|
'first_name': const InputDecoration(
|
||||||
label: Text('First name'),
|
label: Text('First name'),
|
||||||
),
|
),
|
||||||
'lastName': const InputDecoration(
|
'last_name': const InputDecoration(
|
||||||
label: Text('Last name'),
|
label: Text('Last name'),
|
||||||
),
|
),
|
||||||
'email': const InputDecoration(
|
'email': const InputDecoration(
|
||||||
|
@ -64,13 +64,13 @@ class _ProfileExampleState extends State<ProfileExample> {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
validators: {
|
validators: {
|
||||||
'firstName': (String? value) {
|
'first_name': (String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Field empty';
|
return 'Field empty';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
'lastName': (String? value) {
|
'last_name': (String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Field empty';
|
return 'Field empty';
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ library flutter_profile;
|
||||||
|
|
||||||
export 'src/widgets/profile/profile_page.dart';
|
export 'src/widgets/profile/profile_page.dart';
|
||||||
export 'src/widgets/profile/profile_style.dart';
|
export 'src/widgets/profile/profile_style.dart';
|
||||||
|
export 'src/widgets/avatar/avatar_wrapper.dart';
|
||||||
export 'src/widgets/avatar/avatar.dart';
|
export 'src/widgets/avatar/avatar.dart';
|
||||||
export 'src/widgets/avatar/avatar_style.dart';
|
export 'src/widgets/avatar/avatar_style.dart';
|
||||||
export 'src/services/profile_service.dart';
|
export 'src/services/profile_service.dart';
|
||||||
|
|
|
@ -2,39 +2,42 @@ import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// User is used to contain all user data. It consists of three standard fields: firstName, lastName and image.
|
/// User is used to contain all user data. It consists of three standard fields: firstName, lastName and image/imageUrl.
|
||||||
///
|
///
|
||||||
/// For additional data profileData can be used.
|
/// For additional data profileData can be used.
|
||||||
class User {
|
class User {
|
||||||
String? firstName;
|
String? firstName;
|
||||||
String? lastName;
|
String? lastName;
|
||||||
Uint8List? image;
|
Uint8List? image;
|
||||||
|
String? imageUrl;
|
||||||
ProfileData? profileData;
|
ProfileData? profileData;
|
||||||
|
|
||||||
User(
|
User({
|
||||||
this.firstName,
|
this.firstName,
|
||||||
this.lastName,
|
this.lastName,
|
||||||
this.image,
|
this.image,
|
||||||
|
this.imageUrl,
|
||||||
this.profileData,
|
this.profileData,
|
||||||
|
});
|
||||||
|
|
||||||
|
String get displayName => '${firstName ?? ''} ${lastName ?? ''}';
|
||||||
|
String get initials => '${firstName?[0] ?? ''}${lastName?[0] ?? ''}';
|
||||||
|
|
||||||
|
factory User.fromMap(Map<String, dynamic> data) => User(
|
||||||
|
firstName: data['first_name'],
|
||||||
|
lastName: data['last_name'],
|
||||||
|
image: data['image'],
|
||||||
|
imageUrl: data['image_url'],
|
||||||
|
profileData: data['profile_data'],
|
||||||
);
|
);
|
||||||
|
|
||||||
factory User.fromMap(Map<String, dynamic> data) {
|
Map<String, dynamic> toMap() => {
|
||||||
return User(
|
'first_name': firstName,
|
||||||
data['firstName'],
|
'last_name': lastName,
|
||||||
data['lastName'],
|
|
||||||
data['image'],
|
|
||||||
data['profileData'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return {
|
|
||||||
'firstName': firstName,
|
|
||||||
'lastName': lastName,
|
|
||||||
'image': image,
|
'image': image,
|
||||||
'profileData': profileData,
|
'image_url': image,
|
||||||
|
'profile_data': profileData,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ProfileData is used to store custom/addintional data for a user.
|
/// ProfileData is used to store custom/addintional data for a user.
|
||||||
|
|
|
@ -1,79 +1,60 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_profile/src/models/user.dart';
|
||||||
import 'package:flutter_profile/src/widgets/avatar/avatar_style.dart';
|
import 'package:flutter_profile/src/widgets/avatar/avatar_style.dart';
|
||||||
|
|
||||||
class Avatar extends StatelessWidget {
|
class Avatar extends StatelessWidget {
|
||||||
const Avatar({
|
const Avatar({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.image,
|
required this.user,
|
||||||
this.firstName,
|
|
||||||
this.lastName,
|
|
||||||
this.avatar,
|
|
||||||
this.style = const AvatarStyle(),
|
this.style = const AvatarStyle(),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Uint8List? image;
|
final User user;
|
||||||
final String? firstName;
|
|
||||||
final String? lastName;
|
|
||||||
final Widget? avatar;
|
|
||||||
final AvatarStyle style;
|
final AvatarStyle style;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
var imageProvider = _getImageProvider();
|
||||||
children: [
|
|
||||||
_avatar(),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
if (firstName != null || lastName != null)
|
|
||||||
Text(
|
|
||||||
'${firstName ?? ''} ${lastName ?? ''}',
|
|
||||||
style: style.displayNameStyle,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _avatar() {
|
if (imageProvider != null) {
|
||||||
if (avatar != null) {
|
|
||||||
return avatar!;
|
|
||||||
}
|
|
||||||
if (image != null && image!.isNotEmpty) {
|
|
||||||
return Container(
|
return Container(
|
||||||
width: style.width,
|
width: style.width,
|
||||||
height: style.height,
|
height: style.height,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: MemoryImage(image!),
|
image: imageProvider,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (firstName != null || lastName != null) {
|
} else if (user.firstName != null || user.lastName != null) {
|
||||||
return Container(
|
return Container(
|
||||||
width: style.width,
|
width: style.width,
|
||||||
height: style.height,
|
height: style.height,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _generateColorWithIntials(firstName, lastName),
|
color: _generateColorWithIntials(user.firstName, user.lastName),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
style: const TextStyle(fontSize: 40),
|
style: const TextStyle(fontSize: 40),
|
||||||
_getInitials(firstName, lastName),
|
user.initials,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String _getInitials(String? firstName, String? lastName) {
|
return Container();
|
||||||
return (firstName?[0] ?? '') + (lastName?[0] ?? '');
|
}
|
||||||
|
|
||||||
|
ImageProvider? _getImageProvider() {
|
||||||
|
if (user.image != null) {
|
||||||
|
return MemoryImage(user.image!);
|
||||||
|
} else if (user.imageUrl != null) {
|
||||||
|
return NetworkImage(user.imageUrl!);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _generateColorWithIntials(String? firstName, String? lastName) {
|
Color _generateColorWithIntials(String? firstName, String? lastName) {
|
||||||
|
|
|
@ -13,12 +13,16 @@ class AvatarStyle {
|
||||||
const AvatarStyle({
|
const AvatarStyle({
|
||||||
this.width = 100,
|
this.width = 100,
|
||||||
this.height = 100,
|
this.height = 100,
|
||||||
|
this.displayName = true,
|
||||||
this.initialStyle = const TextStyle(),
|
this.initialStyle = const TextStyle(),
|
||||||
|
this.displayNamePadding = const EdgeInsets.only(top: 16),
|
||||||
this.displayNameStyle = const TextStyle(),
|
this.displayNameStyle = const TextStyle(),
|
||||||
});
|
});
|
||||||
|
|
||||||
final double width;
|
final double width;
|
||||||
final double height;
|
final double height;
|
||||||
final TextStyle initialStyle;
|
final TextStyle initialStyle;
|
||||||
|
final bool displayName;
|
||||||
|
final EdgeInsets displayNamePadding;
|
||||||
final TextStyle displayNameStyle;
|
final TextStyle displayNameStyle;
|
||||||
}
|
}
|
||||||
|
|
44
lib/src/widgets/avatar/avatar_wrapper.dart
Normal file
44
lib/src/widgets/avatar/avatar_wrapper.dart
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_profile/src/models/user.dart';
|
||||||
|
import 'package:flutter_profile/src/widgets/avatar/avatar.dart';
|
||||||
|
import 'package:flutter_profile/src/widgets/avatar/avatar_style.dart';
|
||||||
|
|
||||||
|
class AvaterWrapper extends StatelessWidget {
|
||||||
|
const AvaterWrapper({
|
||||||
|
Key? key,
|
||||||
|
required this.user,
|
||||||
|
this.avatar,
|
||||||
|
this.style = const AvatarStyle(),
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final User user;
|
||||||
|
final Widget? avatar;
|
||||||
|
final AvatarStyle style;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var avatar = this.avatar ??
|
||||||
|
Avatar(
|
||||||
|
user: user,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!style.displayName) {
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
avatar,
|
||||||
|
if (user.firstName != null || user.firstName != null)
|
||||||
|
Padding(
|
||||||
|
padding: style.displayNamePadding,
|
||||||
|
child: Text(
|
||||||
|
user.displayName,
|
||||||
|
style: style.displayNameStyle,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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.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';
|
||||||
|
@ -56,14 +56,14 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
||||||
);
|
);
|
||||||
defaultItems.add(
|
defaultItems.add(
|
||||||
builder.build(
|
builder.build(
|
||||||
'firstName',
|
'first_name',
|
||||||
firstNameKey,
|
firstNameKey,
|
||||||
widget.user.firstName,
|
widget.user.firstName,
|
||||||
null,
|
null,
|
||||||
(v) {
|
(v) {
|
||||||
widget.user.firstName = v;
|
widget.user.firstName = v;
|
||||||
|
|
||||||
widget.service.editProfile(widget.user, 'firstName', v);
|
widget.service.editProfile(widget.user, 'first_name', v);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -74,14 +74,14 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
||||||
);
|
);
|
||||||
defaultItems.add(
|
defaultItems.add(
|
||||||
builder.build(
|
builder.build(
|
||||||
'lastName',
|
'last_name',
|
||||||
lastNameKey,
|
lastNameKey,
|
||||||
widget.user.lastName,
|
widget.user.lastName,
|
||||||
null,
|
null,
|
||||||
(v) {
|
(v) {
|
||||||
widget.user.lastName = v;
|
widget.user.lastName = v;
|
||||||
|
|
||||||
widget.service.editProfile(widget.user, 'lastName', v);
|
widget.service.editProfile(widget.user, 'last_name', v);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -93,14 +93,14 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
||||||
} else {
|
} else {
|
||||||
defaultItems.add(
|
defaultItems.add(
|
||||||
widget.itemBuilder!.build(
|
widget.itemBuilder!.build(
|
||||||
'firstName',
|
'first_name',
|
||||||
firstNameKey,
|
firstNameKey,
|
||||||
widget.user.firstName,
|
widget.user.firstName,
|
||||||
null,
|
null,
|
||||||
(v) {
|
(v) {
|
||||||
widget.user.firstName = v;
|
widget.user.firstName = v;
|
||||||
|
|
||||||
widget.service.editProfile(widget.user, 'firstname', v);
|
widget.service.editProfile(widget.user, 'first_name', v);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -111,14 +111,14 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
||||||
);
|
);
|
||||||
defaultItems.add(
|
defaultItems.add(
|
||||||
widget.itemBuilder!.build(
|
widget.itemBuilder!.build(
|
||||||
'lastName',
|
'last_name',
|
||||||
lastNameKey,
|
lastNameKey,
|
||||||
widget.user.lastName,
|
widget.user.lastName,
|
||||||
null,
|
null,
|
||||||
(v) {
|
(v) {
|
||||||
widget.user.lastName = v;
|
widget.user.lastName = v;
|
||||||
|
|
||||||
widget.service.editProfile(widget.user, 'lastName', v);
|
widget.service.editProfile(widget.user, 'last_name', v);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -143,12 +143,10 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await widget.service.uploadImage(context);
|
await widget.service.uploadImage(context);
|
||||||
},
|
},
|
||||||
child: Avatar(
|
child: AvaterWrapper(
|
||||||
firstName: widget.user.firstName,
|
user: widget.user,
|
||||||
lastName: widget.user.lastName,
|
|
||||||
style: widget.style.avatarStyle,
|
style: widget.style.avatarStyle,
|
||||||
avatar: widget.customAvatar,
|
avatar: widget.customAvatar,
|
||||||
image: widget.user.image,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_profile/flutter_profile.dart';
|
import 'package:flutter_profile/flutter_profile.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'test_classes/test_profile_data.dart';
|
import 'test_classes/test_profile_data.dart';
|
||||||
import 'test_classes/test_profile_service.dart';
|
import 'test_classes/test_profile_service.dart';
|
||||||
|
|
||||||
|
@ -13,10 +11,9 @@ void main() {
|
||||||
home: Material(
|
home: Material(
|
||||||
child: ProfilePage(
|
child: ProfilePage(
|
||||||
user: User(
|
user: User(
|
||||||
'Firstname',
|
firstName: 'Firstname',
|
||||||
'Lastname',
|
lastName: 'Lastname',
|
||||||
Uint8List.fromList([]),
|
profileData: TestProfileData(email: 'test@email.com'),
|
||||||
TestProfileData(email: 'test@email.com'),
|
|
||||||
),
|
),
|
||||||
service: TestProfileService(),
|
service: TestProfileService(),
|
||||||
),
|
),
|
||||||
|
@ -39,10 +36,7 @@ void main() {
|
||||||
home: Material(
|
home: Material(
|
||||||
child: ProfilePage(
|
child: ProfilePage(
|
||||||
user: User(
|
user: User(
|
||||||
null,
|
profileData: TestProfileData(email: null),
|
||||||
null,
|
|
||||||
null,
|
|
||||||
TestProfileData(email: null),
|
|
||||||
),
|
),
|
||||||
service: TestProfileService(),
|
service: TestProfileService(),
|
||||||
),
|
),
|
||||||
|
@ -58,10 +52,9 @@ void main() {
|
||||||
home: Material(
|
home: Material(
|
||||||
child: ProfilePage(
|
child: ProfilePage(
|
||||||
user: User(
|
user: User(
|
||||||
'Firstname',
|
firstName: 'Firstname',
|
||||||
'Lastname',
|
lastName: 'Lastname',
|
||||||
null,
|
profileData: TestProfileData(email: 'test@email.com'),
|
||||||
TestProfileData(email: 'test@email.com'),
|
|
||||||
),
|
),
|
||||||
service: TestProfileService(),
|
service: TestProfileService(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -12,28 +12,21 @@ class TestProfileData extends ProfileData {
|
||||||
Map<String, dynamic> mapWidget(
|
Map<String, dynamic> mapWidget(
|
||||||
VoidCallback update,
|
VoidCallback update,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) =>
|
||||||
return {
|
{
|
||||||
'email': null,
|
'email': null,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ProfileData fromMap(Map<String, dynamic> data) {
|
ProfileData fromMap(Map<String, dynamic> data) => TestProfileData(
|
||||||
return TestProfileData(
|
|
||||||
email: data['email'],
|
email: data['email'],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() => {
|
||||||
return {
|
|
||||||
'email': email,
|
'email': email,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ProfileData create() {
|
ProfileData create() => TestProfileData();
|
||||||
return TestProfileData();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue