Made some changes according to feedback

This commit is contained in:
Jacques Doeleman 2022-09-21 14:08:36 +02:00
parent 57e0b7a1fc
commit 52d595bbac
9 changed files with 206 additions and 132 deletions

View file

@ -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 ## 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).
<details> ## How To Use
<summary>PLATFORM</summary>
specific platform steps
</details> 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 ## 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 ## Want to contribute
@ -31,4 +54,4 @@ If you would like to contribute to the plugin (e.g. by improving the documentati
## Author ## Author
This flutter_profile package for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl> This `flutter-image-picker` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>

View file

@ -5,20 +5,16 @@ class ExampleProfileService extends ProfileService {
ExampleProfileService(); ExampleProfileService();
@override @override
void deleteProfile() { void pageBottomAction() {
super.deleteProfile(); debugPrint('Bottom page action');
debugPrint('Deleting profile');
} }
@override @override
void editProfile<T extends ProfileData>( void editProfile(
User<ProfileData> user, User user,
String key, String key,
String value, String value,
) { ) {
super.editProfile(user, key, value);
debugPrint('Editing key: $key with $value'); debugPrint('Editing key: $key with $value');
} }

View file

@ -1,16 +1,15 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart'; 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. /// 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. /// For additional data profileData can be used.
class User<T extends ProfileData> { class User {
String? firstName; String? firstName;
String? lastName; String? lastName;
Uint8List? image; Uint8List? image;
T? profileData; ProfileData? profileData;
User( User(
this.firstName, this.firstName,
@ -39,9 +38,9 @@ class User<T extends ProfileData> {
} }
/// ProfileData is used to store custom/addintional data for a 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 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. /// The Builditems method is used to make the list of field to house the user data.
abstract class ProfileData { abstract class ProfileData {
const ProfileData(); const ProfileData();
@ -53,45 +52,4 @@ abstract class ProfileData {
Map<String, dynamic> mapWidget(VoidCallback update, BuildContext context); Map<String, dynamic> mapWidget(VoidCallback update, BuildContext context);
ProfileData create(); ProfileData create();
List<Widget> buildItems(
Map<String, dynamic> items,
Map<String, dynamic> typeMap,
double spacing,
Function(String, String) updateProfile, {
ItemBuilder? itemBuilder,
ItemBuilderOptions? itemBuilderOptions,
}) {
var widgets = <Widget>[];
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;
}
} }

View file

@ -1,9 +1,11 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:profile/profile.dart'; import 'package:profile/profile.dart';
/// ProfileService can be extended and set for the profilePage. The following method can be overriden. /// 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. /// EditProfile is called when a user changes and submits a standard textfields.
/// ///
@ -11,19 +13,9 @@ import 'package:profile/profile.dart';
abstract class ProfileService { abstract class ProfileService {
const ProfileService(); const ProfileService();
deleteProfile() async {} FutureOr<void> pageBottomAction();
editProfile<T extends ProfileData>( FutureOr<void> editProfile(User user, String key, String value);
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);
}
}
}
uploadImage(BuildContext context) async {} FutureOr<void> uploadImage(BuildContext context);
} }

View file

@ -11,8 +11,8 @@ class ItemBuilder {
final ItemBuilderOptions options; final ItemBuilderOptions options;
Widget build( Widget build(String key, GlobalKey<FormState> formKey, dynamic value,
String key, dynamic value, Widget? widget, Function(String) updateItem) { Widget? widget, Function(String) updateItem) {
if (widget == null) { if (widget == null) {
var controller = TextEditingController( var controller = TextEditingController(
text: '${value ?? ''}', text: '${value ?? ''}',
@ -26,8 +26,6 @@ class ItemBuilder {
inputDecoration = options.inputDecoration; inputDecoration = options.inputDecoration;
} }
final formKey = GlobalKey<FormState>();
return Form( return Form(
key: formKey, key: formKey,
child: TextFormField( child: TextFormField(

View file

@ -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<String, dynamic> items;
final Map<String, dynamic> typeMap;
final double spacing;
final Function(String, String) updateProfile;
final ItemBuilder? itemBuilder;
final ItemBuilderOptions? itemBuilderOptions;
@override
State<ItemList> createState() => _ItemListState();
}
class _ItemListState extends State<ItemList> {
Map<String, GlobalKey<FormState>> formKeys = {};
@override
void initState() {
super.initState();
for (var item in widget.items.entries) {
formKeys.addAll(
{item.key: GlobalKey<FormState>()},
);
}
}
@override
Widget build(BuildContext context) {
var widgets = <Widget>[];
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<FormState>(),
item.value,
widget.typeMap[item.key],
(value) {
widget.updateProfile(item.key, value);
},
),
)
: widgets.add(
widget.itemBuilder!.build(
item.key,
formKeys['item.key'] ?? GlobalKey<FormState>(),
item.value,
widget.typeMap[item.key],
(value) {
widget.updateProfile(item.key, value);
},
),
);
widgets.add(SizedBox(
height: widget.spacing,
));
}
return Column(
children: widgets,
);
}
}

View file

@ -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. /// 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. /// ItemBuilder is used to determine how the user data is represented.
/// ///
@ -29,7 +29,7 @@ class ProfilePage extends StatefulWidget {
this.showAvatar = true, this.showAvatar = true,
this.itemBuilder, this.itemBuilder,
this.itemBuilderOptions, this.itemBuilderOptions,
this.deleteProfileText = 'Delete profile', this.bottomActionText,
}) : super(key: key); }) : super(key: key);
/// User containing all the user data. /// User containing all the user data.
@ -47,8 +47,8 @@ class ProfilePage extends StatefulWidget {
/// Whether to show the users avatar. /// Whether to show the users avatar.
final bool showAvatar; final bool showAvatar;
/// Sets the text for the [InkWell] at the bottom of the profile page. If null deleting the profile is disabled. /// Sets the text for the [InkWell] at the bottom of the profile page. The [InkWell] is disabled when null.
final String? deleteProfileText; final String? bottomActionText;
/// Itembuilder is used the build each field in the user. /// Itembuilder is used the build each field in the user.
final ItemBuilder? itemBuilder; final ItemBuilder? itemBuilder;
@ -72,7 +72,7 @@ class _ProfilePageState extends State<ProfilePage> {
style: widget.style, style: widget.style,
customAvatar: widget.customAvatar, customAvatar: widget.customAvatar,
showAvatar: widget.showAvatar, showAvatar: widget.showAvatar,
deleteProfileText: widget.deleteProfileText, bottomActionText: widget.bottomActionText,
itemBuilder: widget.itemBuilder, itemBuilder: widget.itemBuilder,
itemBuilderOptions: widget.itemBuilderOptions, itemBuilderOptions: widget.itemBuilderOptions,
key: UniqueKey(), key: UniqueKey(),

View file

@ -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/avatar/avatar.dart';
import 'package:profile/src/widgets/item_builder/item_builder.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_builder_options.dart';
import 'package:profile/src/widgets/item_builder/item_list.dart';
import 'package:profile/src/widgets/profile/profile_style.dart'; import 'package:profile/src/widgets/profile/profile_style.dart';
class ProfileWrapper extends StatefulWidget { class ProfileWrapper extends StatefulWidget {
@ -17,7 +18,7 @@ class ProfileWrapper extends StatefulWidget {
this.showAvatar = true, this.showAvatar = true,
this.itemBuilder, this.itemBuilder,
this.itemBuilderOptions, this.itemBuilderOptions,
this.deleteProfileText = 'Delete profile', this.bottomActionText,
}) : super(key: key); }) : super(key: key);
final User user; final User user;
@ -25,7 +26,7 @@ class ProfileWrapper extends StatefulWidget {
final ProfileStyle style; final ProfileStyle style;
final Widget? customAvatar; final Widget? customAvatar;
final bool showAvatar; final bool showAvatar;
final String? deleteProfileText; final String? bottomActionText;
final ItemBuilder? itemBuilder; final ItemBuilder? itemBuilder;
final Function rebuild; final Function rebuild;
final ItemBuilderOptions? itemBuilderOptions; final ItemBuilderOptions? itemBuilderOptions;
@ -37,6 +38,10 @@ class ProfileWrapper extends StatefulWidget {
class _ProfileWrapperState extends State<ProfileWrapper> { class _ProfileWrapperState extends State<ProfileWrapper> {
List<Widget> defaultItems = []; List<Widget> defaultItems = [];
GlobalKey<FormState> firstNameKey = GlobalKey<FormState>();
GlobalKey<FormState> lastNameKey = GlobalKey<FormState>();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -45,41 +50,69 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
ItemBuilder builder = ItemBuilder( ItemBuilder builder = ItemBuilder(
options: widget.itemBuilderOptions ?? ItemBuilderOptions(), options: widget.itemBuilderOptions ?? ItemBuilderOptions(),
); );
defaultItems defaultItems.add(
.add(builder.build('firstName', widget.user.firstName, null, (v) { builder.build(
widget.user.firstName = v; '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( defaultItems.add(
SizedBox( SizedBox(
height: widget.style.betweenDefaultItemPadding, height: widget.style.betweenDefaultItemPadding,
), ),
); );
defaultItems defaultItems.add(
.add(builder.build('lastName', widget.user.lastName, null, (v) { builder.build(
widget.user.lastName = v; '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 { } else {
defaultItems.add(widget.itemBuilder! defaultItems.add(
.build('firstName', widget.user.firstName, null, (v) { widget.itemBuilder!.build(
widget.user.firstName = v; '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( defaultItems.add(
SizedBox( SizedBox(
height: widget.style.betweenDefaultItemPadding, height: widget.style.betweenDefaultItemPadding,
), ),
); );
defaultItems.add(widget.itemBuilder! defaultItems.add(
.build('lastName', widget.user.lastName, null, (v) { widget.itemBuilder!.build(
widget.user.lastName = v; '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<ProfileWrapper> {
height: widget.style.betweenDefaultItemPadding, height: widget.style.betweenDefaultItemPadding,
), ),
...defaultItems, ...defaultItems,
...widget.user.profileData!.buildItems( ItemList(
widget.user.profileData!.toMap(), widget.user.profileData!.toMap(),
widget.user.profileData!.mapWidget( widget.user.profileData!.mapWidget(
() { () {
@ -124,19 +157,19 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
itemBuilder: widget.itemBuilder, itemBuilder: widget.itemBuilder,
itemBuilderOptions: widget.itemBuilderOptions, itemBuilderOptions: widget.itemBuilderOptions,
), ),
if (widget.deleteProfileText != null) if (widget.bottomActionText != null)
SizedBox( SizedBox(
height: widget.style.betweenDefaultItemPadding, height: widget.style.betweenDefaultItemPadding,
), ),
const Spacer(), const Spacer(),
if (widget.deleteProfileText != null) if (widget.bottomActionText != null)
InkWell( InkWell(
onTap: () { onTap: () {
widget.service.deleteProfile(); widget.service.pageBottomAction();
}, },
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text(widget.deleteProfileText!), child: Text(widget.bottomActionText!),
), ),
), ),
], ],

View file

@ -5,21 +5,15 @@ class TestProfileService extends ProfileService {
TestProfileService(); TestProfileService();
@override @override
void deleteProfile() { void pageBottomAction() {}
super.deleteProfile();
}
@override @override
void editProfile<T extends ProfileData>( void editProfile(
User<ProfileData> user, User user,
String key, String key,
String value, String value,
) { ) {}
super.editProfile(user, key, value);
}
@override @override
Future<void> uploadImage(BuildContext context) async { Future<void> uploadImage(BuildContext context) async {}
super.uploadImage(context);
}
} }