Merge pull request #4 from Iconica-Development/feat/image-url-support

Feat/image url support
This commit is contained in:
Stein Milder 2022-10-21 13:15:25 +02:00 committed by GitHub
commit a910c4d381
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 130 additions and 109 deletions

View file

@ -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)

View file

@ -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';
} }

View file

@ -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';

View file

@ -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,
); });
factory User.fromMap(Map<String, dynamic> data) { String get displayName => '${firstName ?? ''} ${lastName ?? ''}';
return User( String get initials => '${firstName?[0] ?? ''}${lastName?[0] ?? ''}';
data['firstName'],
data['lastName'],
data['image'],
data['profileData'],
);
}
Map<String, dynamic> toMap() { factory User.fromMap(Map<String, dynamic> data) => User(
return { firstName: data['first_name'],
'firstName': firstName, lastName: data['last_name'],
'lastName': lastName, image: data['image'],
'image': image, imageUrl: data['image_url'],
'profileData': profileData, profileData: data['profile_data'],
}; );
}
Map<String, dynamic> toMap() => {
'first_name': firstName,
'last_name': lastName,
'image': image,
'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.

View file

@ -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();
} }
return Container();
} }
String _getInitials(String? firstName, String? lastName) { ImageProvider? _getImageProvider() {
return (firstName?[0] ?? '') + (lastName?[0] ?? ''); 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) {

View file

@ -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;
} }

View 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,
),
)
],
);
}
}

View file

@ -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(

View file

@ -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(),
), ),

View file

@ -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();
}
} }