From 52d595bbac2a45f1998cce9d4e39379888241252 Mon Sep 17 00:00:00 2001 From: Jacques Doeleman Date: Wed, 21 Sep 2022 14:08:36 +0200 Subject: [PATCH] Made some changes according to feedback --- README.md | 53 +++++++---- .../lib/utils/example_profile_service.dart | 12 +-- lib/src/models/user.dart | 54 ++---------- lib/src/services/profile_service.dart | 20 ++--- .../widgets/item_builder/item_builder.dart | 6 +- lib/src/widgets/item_builder/item_list.dart | 80 +++++++++++++++++ lib/src/widgets/profile/profile_page.dart | 10 +-- lib/src/widgets/profile/proifle_wrapper.dart | 87 +++++++++++++------ test/test_classes/test_profile_service.dart | 16 ++-- 9 files changed, 206 insertions(+), 132 deletions(-) create mode 100644 lib/src/widgets/item_builder/item_list.dart diff --git a/README.md b/README.md index 7c6ea38..8afd88d 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,52 @@ -[![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart) +# Flutter Profile +Flutter Profile is a package you can use to display any user data and let them alter it . -Short description of what your package is, why you created it. What issues it fixes and how it works. Also mention the available platforms +![Gif](example/gif/ImagePickerGif.gif) + +## Features + +With the Flutter Image Picker you can select an existing picture from the gallery of your device or make a picture with the camera to use in your app. This package is made for Android, iOS and Windows. ## Setup -What setup steps are neccesarry and why> +To use this package, add `flutter_image_picker` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). -
-PLATFORM - -specific platform steps +## How To Use -
+See the [Example Code](example/lib/main.dart) for an example on how to use this package. -## How to use +You can add an optional `ImagePickerTheme()` to the `ImagePicker()` to change the layout of the Image Picker Dialog. You can add the following parameters to the `ImagePickerTheme`: +An example on how to add a parameter to the `ImagePickerTheme()` is: `ImagePickerTheme(imagePickerTheme: const ImagePickerTheme(title: "Image Picker"))`. +As a whole you get `ImagePicker(ImagePickerTheme(imagePickerTheme: const ImagePickerTheme(title: "Image Picker")))` -How can we use the package descibe the most common ways with examples in +| Parameter | Explaination | +|-------------------|----------------| +| font | The font that is being used in the Image Picker Dialog. | +| title | The title displayed at the top of the Image Picker Dialog. | +| titleTextSize | The font size of the title mentioned above. | +| titleColor | The color of the title text. | +| titleBackgroundColor | The color of the title background. | +| titleAlignment | The alignment of the title text. | +| textColor | The color of the text that is displayed in the Image Picker Dialog. | +| iconColor | The color of the icons that are displayed in the Image Picker Dialog. | +| iconSize | The size of the icons that are visible in the Image Picker Dialog. | +| iconTextSize | The font size of the text underneath the icon buttons. | +| spaceBetweenIcons | The size of the space between the two icons in the Image Picker Dialog. | +| makePhotoIcon | The icon that is displayed for the 'Make Photo' functionality of the Image Picker Dialog. | +| makePhotoText | The text that is displayed underneath the 'Make Photo' icon. | +| selectImageIcon | The icon that is displayed for the 'Select Image From Gallery' functionality of the Image Picker Dialog. | +| selectImageText | The text that is displayed underneath the 'Select Image From Gallery' icon. | +| closeButtonText | The text that is shown on the 'Close Dialog' button at the bottom of the Image Picker Dialog. | +| closeButtonTextSize | The size of the text that is being displayed on the 'Close Dialog' button at the bottom of the Image Picker Dialog. | +| closeButtonTextColor | The color of the text that is being displayed on the 'Close Dialog' button at the bottom of the Image Picker Dialog. | +| closeButtonWidth | The width of the 'Close Dialog' button at the bottom of the Image Picker Dialog. | +| closeButtonHeight | The height of the 'Close Dialog' button at the bottom of the Image Picker Dialog. | +| closeButtonBackgroundColor | The background color of the 'Close Dialog' button at the bottom of the Image Picker Dialog. | -```dart - codeblocks -``` ## Issues -Please file any issues, bugs or feature request as an issue on our [GitHub](REPO URL) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl). +Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/flutter_image_picker/pulls) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl). ## Want to contribute @@ -31,4 +54,4 @@ If you would like to contribute to the plugin (e.g. by improving the documentati ## Author -This flutter_profile package for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at +This `flutter-image-picker` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at \ No newline at end of file diff --git a/example/lib/utils/example_profile_service.dart b/example/lib/utils/example_profile_service.dart index 8fd7873..ddb42ee 100644 --- a/example/lib/utils/example_profile_service.dart +++ b/example/lib/utils/example_profile_service.dart @@ -5,20 +5,16 @@ class ExampleProfileService extends ProfileService { ExampleProfileService(); @override - void deleteProfile() { - super.deleteProfile(); - - debugPrint('Deleting profile'); + void pageBottomAction() { + debugPrint('Bottom page action'); } @override - void editProfile( - User user, + void editProfile( + User user, String key, String value, ) { - super.editProfile(user, key, value); - debugPrint('Editing key: $key with $value'); } diff --git a/lib/src/models/user.dart b/lib/src/models/user.dart index a4ee6e0..ef7c943 100644 --- a/lib/src/models/user.dart +++ b/lib/src/models/user.dart @@ -1,16 +1,15 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:profile/profile.dart'; /// User is used to contain all user data. It consists of three standard fields: firstName, lastName and image. -/// -/// For additional data profileData can be used. -class User { +/// +/// For additional data profileData can be used. +class User { String? firstName; String? lastName; Uint8List? image; - T? profileData; + ProfileData? profileData; User( this.firstName, @@ -39,9 +38,9 @@ class User { } /// ProfileData is used to store custom/addintional data for a user. -/// +/// /// The MapWidget method is used to bind a [Widget] to one of the keys. This will override the standard textfield. -/// +/// /// The Builditems method is used to make the list of field to house the user data. abstract class ProfileData { const ProfileData(); @@ -53,45 +52,4 @@ abstract class ProfileData { Map mapWidget(VoidCallback update, BuildContext context); ProfileData create(); - - List buildItems( - Map items, - Map typeMap, - double spacing, - Function(String, String) updateProfile, { - ItemBuilder? itemBuilder, - ItemBuilderOptions? itemBuilderOptions, - }) { - var widgets = []; - ItemBuilder builder = ItemBuilder( - options: itemBuilderOptions ?? ItemBuilderOptions(), - ); - for (var item in items.entries) { - itemBuilder == null - ? widgets.add( - builder.build( - item.key, - item.value, - typeMap[item.key], - (value) { - updateProfile(item.key, value); - }, - ), - ) - : widgets.add( - itemBuilder.build( - item.key, - item.value, - typeMap[item.key], - (value) { - updateProfile(item.key, value); - }, - ), - ); - widgets.add(SizedBox( - height: spacing, - )); - } - return widgets; - } } diff --git a/lib/src/services/profile_service.dart b/lib/src/services/profile_service.dart index 78efe1c..a3b62e0 100644 --- a/lib/src/services/profile_service.dart +++ b/lib/src/services/profile_service.dart @@ -1,9 +1,11 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:profile/profile.dart'; /// ProfileService can be extended and set for the profilePage. The following method can be overriden. /// -/// DeleteProfile is called when the user want to delete their profile. +/// BottompageAction is called when the [InkWell] at the bottom of the page is tapped. /// /// EditProfile is called when a user changes and submits a standard textfields. /// @@ -11,19 +13,9 @@ import 'package:profile/profile.dart'; abstract class ProfileService { const ProfileService(); - deleteProfile() async {} + FutureOr pageBottomAction(); - editProfile( - User user, String key, String value) async { - if (user.profileData != null) { - var map = user.profileData!.toMap(); - if (map.containsKey(key)) { - map[key] = value; - var profile = user.profileData!.create(); - user.profileData = profile.fromMap(map); - } - } - } + FutureOr editProfile(User user, String key, String value); - uploadImage(BuildContext context) async {} + FutureOr uploadImage(BuildContext context); } diff --git a/lib/src/widgets/item_builder/item_builder.dart b/lib/src/widgets/item_builder/item_builder.dart index 95ada51..4321595 100644 --- a/lib/src/widgets/item_builder/item_builder.dart +++ b/lib/src/widgets/item_builder/item_builder.dart @@ -11,8 +11,8 @@ class ItemBuilder { final ItemBuilderOptions options; - Widget build( - String key, dynamic value, Widget? widget, Function(String) updateItem) { + Widget build(String key, GlobalKey formKey, dynamic value, + Widget? widget, Function(String) updateItem) { if (widget == null) { var controller = TextEditingController( text: '${value ?? ''}', @@ -26,8 +26,6 @@ class ItemBuilder { inputDecoration = options.inputDecoration; } - final formKey = GlobalKey(); - return Form( key: formKey, child: TextFormField( diff --git a/lib/src/widgets/item_builder/item_list.dart b/lib/src/widgets/item_builder/item_list.dart new file mode 100644 index 0000000..21c7af2 --- /dev/null +++ b/lib/src/widgets/item_builder/item_list.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:profile/src/widgets/item_builder/item_builder.dart'; +import 'package:profile/src/widgets/item_builder/item_builder_options.dart'; + +class ItemList extends StatefulWidget { + const ItemList( + this.items, + this.typeMap, + this.spacing, + this.updateProfile, { + this.itemBuilder, + this.itemBuilderOptions, + super.key, + }); + + final Map items; + final Map typeMap; + final double spacing; + final Function(String, String) updateProfile; + final ItemBuilder? itemBuilder; + final ItemBuilderOptions? itemBuilderOptions; + + @override + State createState() => _ItemListState(); +} + +class _ItemListState extends State { + Map> formKeys = {}; + + @override + void initState() { + super.initState(); + + for (var item in widget.items.entries) { + formKeys.addAll( + {item.key: GlobalKey()}, + ); + } + } + + @override + Widget build(BuildContext context) { + var widgets = []; + ItemBuilder builder = ItemBuilder( + options: widget.itemBuilderOptions ?? ItemBuilderOptions(), + ); + for (var item in widget.items.entries) { + widget.itemBuilder == null + ? widgets.add( + builder.build( + item.key, + formKeys['item.key'] ?? GlobalKey(), + item.value, + widget.typeMap[item.key], + (value) { + widget.updateProfile(item.key, value); + }, + ), + ) + : widgets.add( + widget.itemBuilder!.build( + item.key, + formKeys['item.key'] ?? GlobalKey(), + item.value, + widget.typeMap[item.key], + (value) { + widget.updateProfile(item.key, value); + }, + ), + ); + widgets.add(SizedBox( + height: widget.spacing, + )); + } + + return Column( + children: widgets, + ); + } +} diff --git a/lib/src/widgets/profile/profile_page.dart b/lib/src/widgets/profile/profile_page.dart index 75b3564..3d76986 100644 --- a/lib/src/widgets/profile/profile_page.dart +++ b/lib/src/widgets/profile/profile_page.dart @@ -14,7 +14,7 @@ import 'package:profile/src/widgets/profile/proifle_wrapper.dart'; /// /// ShowAvatar can be set using a [bool] to determine whether the avatar should be shown and be able to be set by the user. Default set to true. /// -/// DeleteProfileText sets the text for the inkwell at the bottom of the page. If this is set the null then the deletion of the profile is disabled. +/// BottomActionText sets the text for the inkwell at the bottom of the page. If this is set the null then the [InkWell] is disabled. /// /// ItemBuilder is used to determine how the user data is represented. /// @@ -29,7 +29,7 @@ class ProfilePage extends StatefulWidget { this.showAvatar = true, this.itemBuilder, this.itemBuilderOptions, - this.deleteProfileText = 'Delete profile', + this.bottomActionText, }) : super(key: key); /// User containing all the user data. @@ -47,8 +47,8 @@ class ProfilePage extends StatefulWidget { /// Whether to show the users avatar. final bool showAvatar; - /// Sets the text for the [InkWell] at the bottom of the profile page. If null deleting the profile is disabled. - final String? deleteProfileText; + /// Sets the text for the [InkWell] at the bottom of the profile page. The [InkWell] is disabled when null. + final String? bottomActionText; /// Itembuilder is used the build each field in the user. final ItemBuilder? itemBuilder; @@ -72,7 +72,7 @@ class _ProfilePageState extends State { style: widget.style, customAvatar: widget.customAvatar, showAvatar: widget.showAvatar, - deleteProfileText: widget.deleteProfileText, + bottomActionText: widget.bottomActionText, itemBuilder: widget.itemBuilder, itemBuilderOptions: widget.itemBuilderOptions, key: UniqueKey(), diff --git a/lib/src/widgets/profile/proifle_wrapper.dart b/lib/src/widgets/profile/proifle_wrapper.dart index 27f428f..580ea0a 100644 --- a/lib/src/widgets/profile/proifle_wrapper.dart +++ b/lib/src/widgets/profile/proifle_wrapper.dart @@ -4,6 +4,7 @@ import 'package:profile/src/services/profile_service.dart'; import 'package:profile/src/widgets/avatar/avatar.dart'; import 'package:profile/src/widgets/item_builder/item_builder.dart'; import 'package:profile/src/widgets/item_builder/item_builder_options.dart'; +import 'package:profile/src/widgets/item_builder/item_list.dart'; import 'package:profile/src/widgets/profile/profile_style.dart'; class ProfileWrapper extends StatefulWidget { @@ -17,7 +18,7 @@ class ProfileWrapper extends StatefulWidget { this.showAvatar = true, this.itemBuilder, this.itemBuilderOptions, - this.deleteProfileText = 'Delete profile', + this.bottomActionText, }) : super(key: key); final User user; @@ -25,7 +26,7 @@ class ProfileWrapper extends StatefulWidget { final ProfileStyle style; final Widget? customAvatar; final bool showAvatar; - final String? deleteProfileText; + final String? bottomActionText; final ItemBuilder? itemBuilder; final Function rebuild; final ItemBuilderOptions? itemBuilderOptions; @@ -37,6 +38,10 @@ class ProfileWrapper extends StatefulWidget { class _ProfileWrapperState extends State { List defaultItems = []; + GlobalKey firstNameKey = GlobalKey(); + + GlobalKey lastNameKey = GlobalKey(); + @override void initState() { super.initState(); @@ -45,41 +50,69 @@ class _ProfileWrapperState extends State { ItemBuilder builder = ItemBuilder( options: widget.itemBuilderOptions ?? ItemBuilderOptions(), ); - defaultItems - .add(builder.build('firstName', widget.user.firstName, null, (v) { - widget.user.firstName = v; + defaultItems.add( + builder.build( + 'firstName', + firstNameKey, + widget.user.firstName, + null, + (v) { + widget.user.firstName = v; - widget.service.editProfile(widget.user, 'firstName', v); - })); + widget.service.editProfile(widget.user, 'firstName', v); + }, + ), + ); defaultItems.add( SizedBox( height: widget.style.betweenDefaultItemPadding, ), ); - defaultItems - .add(builder.build('lastName', widget.user.lastName, null, (v) { - widget.user.lastName = v; + defaultItems.add( + builder.build( + 'lastName', + lastNameKey, + widget.user.lastName, + null, + (v) { + widget.user.lastName = v; - widget.service.editProfile(widget.user, 'lastName', v); - })); + widget.service.editProfile(widget.user, 'lastName', v); + }, + ), + ); } else { - defaultItems.add(widget.itemBuilder! - .build('firstName', widget.user.firstName, null, (v) { - widget.user.firstName = v; + defaultItems.add( + widget.itemBuilder!.build( + 'firstName', + firstNameKey, + widget.user.firstName, + null, + (v) { + widget.user.firstName = v; - widget.service.editProfile(widget.user, 'firstname', v); - })); + widget.service.editProfile(widget.user, 'firstname', v); + }, + ), + ); defaultItems.add( SizedBox( height: widget.style.betweenDefaultItemPadding, ), ); - defaultItems.add(widget.itemBuilder! - .build('lastName', widget.user.lastName, null, (v) { - widget.user.lastName = v; + defaultItems.add( + widget.itemBuilder!.build( + 'lastName', + lastNameKey, + widget.user.lastName, + null, + (v) { + widget.user.lastName = v; - widget.service.editProfile(widget.user, 'lastName', v); - })); + widget.service.editProfile(widget.user, 'lastName', v); + }, + ), + ); } } @@ -109,7 +142,7 @@ class _ProfileWrapperState extends State { height: widget.style.betweenDefaultItemPadding, ), ...defaultItems, - ...widget.user.profileData!.buildItems( + ItemList( widget.user.profileData!.toMap(), widget.user.profileData!.mapWidget( () { @@ -124,19 +157,19 @@ class _ProfileWrapperState extends State { itemBuilder: widget.itemBuilder, itemBuilderOptions: widget.itemBuilderOptions, ), - if (widget.deleteProfileText != null) + if (widget.bottomActionText != null) SizedBox( height: widget.style.betweenDefaultItemPadding, ), const Spacer(), - if (widget.deleteProfileText != null) + if (widget.bottomActionText != null) InkWell( onTap: () { - widget.service.deleteProfile(); + widget.service.pageBottomAction(); }, child: Padding( padding: const EdgeInsets.all(8.0), - child: Text(widget.deleteProfileText!), + child: Text(widget.bottomActionText!), ), ), ], diff --git a/test/test_classes/test_profile_service.dart b/test/test_classes/test_profile_service.dart index e0b53e7..185df60 100644 --- a/test/test_classes/test_profile_service.dart +++ b/test/test_classes/test_profile_service.dart @@ -5,21 +5,15 @@ class TestProfileService extends ProfileService { TestProfileService(); @override - void deleteProfile() { - super.deleteProfile(); - } + void pageBottomAction() {} @override - void editProfile( - User user, + void editProfile( + User user, String key, String value, - ) { - super.editProfile(user, key, value); - } + ) {} @override - Future uploadImage(BuildContext context) async { - super.uploadImage(context); - } + Future uploadImage(BuildContext context) async {} }