diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c75b98..f8589a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,3 +5,7 @@ ## 0.0.2 * 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) \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index d1b6562..a2feed1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -37,12 +37,12 @@ class _ProfileExampleState extends State { void initState() { super.initState(); _user = User( - 'Firstname', - 'Lastname', - Uint8List.fromList( + firstName: 'Firstname', + lastName: 'Lastname', + image: Uint8List.fromList( [], ), - profileData, + profileData: profileData, ); } @@ -53,10 +53,10 @@ class _ProfileExampleState extends State { bottomActionText: 'Log out', itemBuilderOptions: ItemBuilderOptions( inputDecorationField: { - 'firstName': const InputDecoration( + 'first_name': const InputDecoration( label: Text('First name'), ), - 'lastName': const InputDecoration( + 'last_name': const InputDecoration( label: Text('Last name'), ), 'email': const InputDecoration( @@ -64,13 +64,13 @@ class _ProfileExampleState extends State { ), }, validators: { - 'firstName': (String? value) { + 'first_name': (String? value) { if (value == null || value.isEmpty) { return 'Field empty'; } return null; }, - 'lastName': (String? value) { + 'last_name': (String? value) { if (value == null || value.isEmpty) { return 'Field empty'; } diff --git a/lib/flutter_profile.dart b/lib/flutter_profile.dart index 19a1dac..66018b3 100644 --- a/lib/flutter_profile.dart +++ b/lib/flutter_profile.dart @@ -2,6 +2,7 @@ library flutter_profile; export 'src/widgets/profile/profile_page.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_style.dart'; export 'src/services/profile_service.dart'; diff --git a/lib/src/models/user.dart b/lib/src/models/user.dart index ef7c943..550bc9e 100644 --- a/lib/src/models/user.dart +++ b/lib/src/models/user.dart @@ -2,39 +2,42 @@ import 'dart:typed_data'; 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. class User { String? firstName; String? lastName; Uint8List? image; + String? imageUrl; ProfileData? profileData; - User( + User({ this.firstName, this.lastName, this.image, + this.imageUrl, this.profileData, - ); + }); - factory User.fromMap(Map data) { - return User( - data['firstName'], - data['lastName'], - data['image'], - data['profileData'], - ); - } + String get displayName => '${firstName ?? ''} ${lastName ?? ''}'; + String get initials => '${firstName?[0] ?? ''}${lastName?[0] ?? ''}'; - Map toMap() { - return { - 'firstName': firstName, - 'lastName': lastName, - 'image': image, - 'profileData': profileData, - }; - } + factory User.fromMap(Map data) => User( + firstName: data['first_name'], + lastName: data['last_name'], + image: data['image'], + imageUrl: data['image_url'], + profileData: data['profile_data'], + ); + + Map 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. diff --git a/lib/src/widgets/avatar/avatar.dart b/lib/src/widgets/avatar/avatar.dart index 268fcaa..f4ae9b9 100644 --- a/lib/src/widgets/avatar/avatar.dart +++ b/lib/src/widgets/avatar/avatar.dart @@ -1,79 +1,60 @@ -import 'dart:typed_data'; - import 'package:flutter/material.dart'; +import 'package:flutter_profile/src/models/user.dart'; import 'package:flutter_profile/src/widgets/avatar/avatar_style.dart'; class Avatar extends StatelessWidget { const Avatar({ Key? key, - this.image, - this.firstName, - this.lastName, - this.avatar, + required this.user, this.style = const AvatarStyle(), }) : super(key: key); - final Uint8List? image; - final String? firstName; - final String? lastName; - final Widget? avatar; + final User user; final AvatarStyle style; @override Widget build(BuildContext context) { - return Column( - children: [ - _avatar(), - const SizedBox( - height: 16, - ), - if (firstName != null || lastName != null) - Text( - '${firstName ?? ''} ${lastName ?? ''}', - style: style.displayNameStyle, - ) - ], - ); - } + var imageProvider = _getImageProvider(); - Widget _avatar() { - if (avatar != null) { - return avatar!; - } - if (image != null && image!.isNotEmpty) { + if (imageProvider != null) { return Container( width: style.width, height: style.height, decoration: BoxDecoration( shape: BoxShape.circle, image: DecorationImage( - image: MemoryImage(image!), + image: imageProvider, fit: BoxFit.cover, ), ), ); - } else if (firstName != null || lastName != null) { + } else if (user.firstName != null || user.lastName != null) { return Container( width: style.width, height: style.height, decoration: BoxDecoration( - color: _generateColorWithIntials(firstName, lastName), + color: _generateColorWithIntials(user.firstName, user.lastName), shape: BoxShape.circle, ), child: Center( child: Text( style: const TextStyle(fontSize: 40), - _getInitials(firstName, lastName), + user.initials, ), ), ); - } else { - return Container(); } + + return Container(); } - String _getInitials(String? firstName, String? lastName) { - 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) { diff --git a/lib/src/widgets/avatar/avatar_style.dart b/lib/src/widgets/avatar/avatar_style.dart index 3107a90..df77df0 100644 --- a/lib/src/widgets/avatar/avatar_style.dart +++ b/lib/src/widgets/avatar/avatar_style.dart @@ -13,12 +13,16 @@ class AvatarStyle { const AvatarStyle({ this.width = 100, this.height = 100, + this.displayName = true, this.initialStyle = const TextStyle(), + this.displayNamePadding = const EdgeInsets.only(top: 16), this.displayNameStyle = const TextStyle(), }); final double width; final double height; final TextStyle initialStyle; + final bool displayName; + final EdgeInsets displayNamePadding; final TextStyle displayNameStyle; } diff --git a/lib/src/widgets/avatar/avatar_wrapper.dart b/lib/src/widgets/avatar/avatar_wrapper.dart new file mode 100644 index 0000000..aa17b8a --- /dev/null +++ b/lib/src/widgets/avatar/avatar_wrapper.dart @@ -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, + ), + ) + ], + ); + } +} diff --git a/lib/src/widgets/profile/proifle_wrapper.dart b/lib/src/widgets/profile/proifle_wrapper.dart index bd6221b..da722a4 100644 --- a/lib/src/widgets/profile/proifle_wrapper.dart +++ b/lib/src/widgets/profile/proifle_wrapper.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.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.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'; @@ -56,14 +56,14 @@ class _ProfileWrapperState extends State { ); defaultItems.add( builder.build( - 'firstName', + 'first_name', firstNameKey, widget.user.firstName, null, (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 { ); defaultItems.add( builder.build( - 'lastName', + 'last_name', lastNameKey, widget.user.lastName, null, (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 { } else { defaultItems.add( widget.itemBuilder!.build( - 'firstName', + 'first_name', firstNameKey, widget.user.firstName, null, (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 { ); defaultItems.add( widget.itemBuilder!.build( - 'lastName', + 'last_name', lastNameKey, widget.user.lastName, null, (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 { onTap: () async { await widget.service.uploadImage(context); }, - child: Avatar( - firstName: widget.user.firstName, - lastName: widget.user.lastName, + child: AvaterWrapper( + user: widget.user, style: widget.style.avatarStyle, avatar: widget.customAvatar, - image: widget.user.image, ), ), SizedBox( diff --git a/test/profile_test.dart b/test/profile_test.dart index 674b073..3cd6f9c 100644 --- a/test/profile_test.dart +++ b/test/profile_test.dart @@ -1,8 +1,6 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_profile/flutter_profile.dart'; import 'package:flutter_test/flutter_test.dart'; - import 'test_classes/test_profile_data.dart'; import 'test_classes/test_profile_service.dart'; @@ -13,10 +11,9 @@ void main() { home: Material( child: ProfilePage( user: User( - 'Firstname', - 'Lastname', - Uint8List.fromList([]), - TestProfileData(email: 'test@email.com'), + firstName: 'Firstname', + lastName: 'Lastname', + profileData: TestProfileData(email: 'test@email.com'), ), service: TestProfileService(), ), @@ -39,10 +36,7 @@ void main() { home: Material( child: ProfilePage( user: User( - null, - null, - null, - TestProfileData(email: null), + profileData: TestProfileData(email: null), ), service: TestProfileService(), ), @@ -58,10 +52,9 @@ void main() { home: Material( child: ProfilePage( user: User( - 'Firstname', - 'Lastname', - null, - TestProfileData(email: 'test@email.com'), + firstName: 'Firstname', + lastName: 'Lastname', + profileData: TestProfileData(email: 'test@email.com'), ), service: TestProfileService(), ), diff --git a/test/test_classes/test_profile_data.dart b/test/test_classes/test_profile_data.dart index 1207ab6..3c7d93b 100644 --- a/test/test_classes/test_profile_data.dart +++ b/test/test_classes/test_profile_data.dart @@ -12,28 +12,21 @@ class TestProfileData extends ProfileData { Map mapWidget( VoidCallback update, BuildContext context, - ) { - return { - 'email': null, - }; - } + ) => + { + 'email': null, + }; @override - ProfileData fromMap(Map data) { - return TestProfileData( - email: data['email'], - ); - } + ProfileData fromMap(Map data) => TestProfileData( + email: data['email'], + ); @override - Map toMap() { - return { - 'email': email, - }; - } + Map toMap() => { + 'email': email, + }; @override - ProfileData create() { - return TestProfileData(); - } + ProfileData create() => TestProfileData(); }