From 579768e24028e9d1f64ae3d6e70ea1f3932d6776 Mon Sep 17 00:00:00 2001 From: mike doornenbal Date: Fri, 2 Feb 2024 11:07:55 +0100 Subject: [PATCH] feat: add ci and linter --- .github/workflows/component-ci.yml | 14 ++ CHANGELOG.md | 4 + analysis_options.yaml | 11 +- example/pubspec.lock | 2 +- lib/flutter_profile.dart | 16 +- lib/src/models/change_password_config.dart | 3 +- lib/src/models/user.dart | 26 ++- lib/src/services/profile_service.dart | 7 +- lib/src/widgets/avatar/avatar.dart | 4 +- lib/src/widgets/avatar/avatar_wrapper.dart | 4 +- .../widgets/item_builder/item_builder.dart | 16 +- .../item_builder/item_builder_options.dart | 13 +- lib/src/widgets/item_builder/item_list.dart | 2 +- .../profile/change_password_widget.dart | 6 +- lib/src/widgets/profile/profile_page.dart | 94 ++++---- lib/src/widgets/profile/profile_style.dart | 9 +- lib/src/widgets/profile/profile_wrapper.dart | 207 +++++++++--------- pubspec.yaml | 7 +- test/profile_test.dart | 16 +- test/test_classes/test_profile_service.dart | 1 + 20 files changed, 259 insertions(+), 203 deletions(-) create mode 100644 .github/workflows/component-ci.yml diff --git a/.github/workflows/component-ci.yml b/.github/workflows/component-ci.yml new file mode 100644 index 0000000..ca69d49 --- /dev/null +++ b/.github/workflows/component-ci.yml @@ -0,0 +1,14 @@ +name: Iconica Standard Component CI Workflow +# Workflow Caller version: 2.0.0 + +on: + pull_request: + workflow_dispatch: + +jobs: + call-global-iconica-workflow: + uses: Iconica-Development/.github/.github/workflows/component-ci.yml@master + secrets: inherit + permissions: write-all + with: + subfolder: "." # add optional subfolder to run workflow in diff --git a/CHANGELOG.md b/CHANGELOG.md index 4364257..7a4b3a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.1 + +- Added Iconica CI and Iconica Linter + ## 1.2.0 - Added the posibilty to enable the user to change it's password. diff --git a/analysis_options.yaml b/analysis_options.yaml index a5744c1..31b4b51 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,9 @@ -include: package:flutter_lints/flutter.yaml +include: package:flutter_iconica_analysis/analysis_options.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +# Possible to overwrite the rules from the package + +analyzer: + exclude: + +linter: + rules: diff --git a/example/pubspec.lock b/example/pubspec.lock index b943d24..5fb46a4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -141,7 +141,7 @@ packages: path: ".." relative: true source: path - version: "1.1.6" + version: "1.2.0" flutter_test: dependency: "direct dev" description: flutter diff --git a/lib/flutter_profile.dart b/lib/flutter_profile.dart index 106dc42..45d3ed7 100644 --- a/lib/flutter_profile.dart +++ b/lib/flutter_profile.dart @@ -1,15 +1,15 @@ // SPDX-FileCopyrightText: 2022 Iconica // // SPDX-License-Identifier: BSD-3-Clause - +/// library flutter_profile; +export 'src/models/change_password_config.dart'; +export 'src/models/user.dart'; +export 'src/services/profile_service.dart'; +export 'src/widgets/avatar/avatar.dart'; +export 'src/widgets/avatar/avatar_wrapper.dart'; +export 'src/widgets/item_builder/item_builder.dart'; +export 'src/widgets/item_builder/item_builder_options.dart'; 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/services/profile_service.dart'; -export 'src/widgets/item_builder/item_builder.dart'; -export 'src/models/user.dart'; -export 'src/models/change_password_config.dart'; -export 'src/widgets/item_builder/item_builder_options.dart'; diff --git a/lib/src/models/change_password_config.dart b/lib/src/models/change_password_config.dart index 6816698..8f570ab 100644 --- a/lib/src/models/change_password_config.dart +++ b/lib/src/models/change_password_config.dart @@ -39,6 +39,7 @@ class ChangePasswordConfig { /// Error text to be shown when either of the textfields is empty. final String fieldRequiredErrorText; - /// Error text to be shown when the second password isn't equal to the first password. + /// Error text to be shown when the second password isn't equal + /// to the first password. final String notEqualErrorText; } diff --git a/lib/src/models/user.dart b/lib/src/models/user.dart index 45c54ec..f54f1a1 100644 --- a/lib/src/models/user.dart +++ b/lib/src/models/user.dart @@ -10,12 +10,6 @@ import 'package:flutter/material.dart'; /// /// For additional data profileData can be used. class User { - String? firstName; - String? lastName; - Uint8List? image; - String? imageUrl; - ProfileData? profileData; - User({ this.firstName, this.lastName, @@ -24,10 +18,6 @@ class User { this.profileData, }); - String get displayName => '${firstName ?? ''} ${lastName ?? ''}'; - String get initials => - '${(firstName?.isNotEmpty ?? false) ? firstName![0] : ''}${(lastName?.isNotEmpty ?? false) ? lastName![0] : ''}'; - factory User.fromMap(Map data) => User( firstName: data['first_name'], lastName: data['last_name'], @@ -35,6 +25,16 @@ class User { imageUrl: data['image_url'], profileData: data['profile_data'], ); + String? firstName; + String? lastName; + Uint8List? image; + String? imageUrl; + ProfileData? profileData; + + String get displayName => '${firstName ?? ''} ${lastName ?? ''}'; + String get initials => + '${(firstName?.isNotEmpty ?? false) ? firstName![0] : ''}' + '${(lastName?.isNotEmpty ?? false) ? lastName![0] : ''}'; Map toMap() => { 'first_name': firstName, @@ -47,9 +47,11 @@ 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 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 { const ProfileData(); diff --git a/lib/src/services/profile_service.dart b/lib/src/services/profile_service.dart index b3726d4..56394d4 100644 --- a/lib/src/services/profile_service.dart +++ b/lib/src/services/profile_service.dart @@ -6,9 +6,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_profile/src/models/user.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. /// -/// BottompageAction is called when the [InkWell] at the bottom of the page is tapped. +/// 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. /// @@ -22,6 +24,7 @@ abstract class ProfileService { FutureOr uploadImage( BuildContext context, { + // ignore: avoid_positional_boolean_parameters required Function(bool isUploading) onUploadStateChanged, }); diff --git a/lib/src/widgets/avatar/avatar.dart b/lib/src/widgets/avatar/avatar.dart index 81bf2e8..55e7db8 100644 --- a/lib/src/widgets/avatar/avatar.dart +++ b/lib/src/widgets/avatar/avatar.dart @@ -8,11 +8,11 @@ import 'package:flutter_profile/src/models/user.dart'; class Avatar extends StatelessWidget { const Avatar({ - Key? key, + super.key, this.user, this.size = 100, this.avatarBackgroundColor, - }) : super(key: key); + }); final User? user; final double size; diff --git a/lib/src/widgets/avatar/avatar_wrapper.dart b/lib/src/widgets/avatar/avatar_wrapper.dart index 7e45503..0a81962 100644 --- a/lib/src/widgets/avatar/avatar_wrapper.dart +++ b/lib/src/widgets/avatar/avatar_wrapper.dart @@ -8,15 +8,15 @@ import 'package:flutter_profile/src/widgets/avatar/avatar.dart'; class AvatarWrapper extends StatelessWidget { const AvatarWrapper({ - Key? key, required this.user, + super.key, this.showName = false, this.padding = const EdgeInsets.only(top: 16), this.size = 100, this.textStyle, this.customAvatar, this.avatarBackgroundColor, - }) : super(key: key); + }); final User user; final Widget? customAvatar; diff --git a/lib/src/widgets/item_builder/item_builder.dart b/lib/src/widgets/item_builder/item_builder.dart index d9c4f92..825980f 100644 --- a/lib/src/widgets/item_builder/item_builder.dart +++ b/lib/src/widgets/item_builder/item_builder.dart @@ -6,7 +6,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_input_library/flutter_input_library.dart'; import 'package:flutter_profile/src/widgets/item_builder/item_builder_options.dart'; -/// ItemBuilder is used to set the standard textfield for each undefined users data item. +/// ItemBuilder is used to set the standard textfield for each undefined +/// users data item. /// /// Options sets options for the textfield. class ItemBuilder { @@ -16,8 +17,13 @@ class ItemBuilder { final ItemBuilderOptions options; - Widget build(String key, dynamic value, Widget? widget, - Function(String) updateItem, Function(String?) saveItem) { + Widget build( + String key, + value, + Widget? widget, + Function(String) updateItem, + Function(String?) saveItem, + ) { if (widget == null) { var controller = TextEditingController( text: '${value ?? ''}', @@ -40,9 +46,7 @@ class ItemBuilder { onSaved: (newValue) { saveItem(newValue); }, - validator: (value) { - return options.validators?[key]?.call(value); - }, + validator: (value) => options.validators?[key]?.call(value), ); } return widget; diff --git a/lib/src/widgets/item_builder/item_builder_options.dart b/lib/src/widgets/item_builder/item_builder_options.dart index ec770fb..8c0d7c7 100644 --- a/lib/src/widgets/item_builder/item_builder_options.dart +++ b/lib/src/widgets/item_builder/item_builder_options.dart @@ -4,17 +4,22 @@ import 'package:flutter/material.dart'; -/// ItemBuilderOptions is a class to store all settings for a field in the profile page. +/// ItemBuilderOptions is a class to store all settings for a field in the +/// profile page. /// -/// InputDecoration sets the decoration for all standard textfields. This is overridden if a field specific decoration is set by inputDecorationField. +/// InputDecoration sets the decoration for all standard textfields. +/// This is overridden if a field specific decoration is set by +/// inputDecorationField. /// -/// inputDecorationField sets the inputdecoration by key of the user data field. So a field can have its own specific decoration. +/// inputDecorationField sets the inputdecoration by key of the user data field. +/// So a field can have its own specific decoration. /// /// Validator can be used to set a validator for the standard textfield. class ItemBuilderOptions { ItemBuilderOptions({ this.inputDecoration = const InputDecoration( - constraints: BoxConstraints(maxWidth: 200, maxHeight: 40)), + constraints: BoxConstraints(maxWidth: 200, maxHeight: 40), + ), this.inputDecorationField, this.readOnly = false, this.validators, diff --git a/lib/src/widgets/item_builder/item_list.dart b/lib/src/widgets/item_builder/item_list.dart index e7c4b47..fd9277d 100644 --- a/lib/src/widgets/item_builder/item_list.dart +++ b/lib/src/widgets/item_builder/item_list.dart @@ -40,7 +40,7 @@ class ItemList { (value) { updateProfile(item.key, value); }, - ) + ), }); } } diff --git a/lib/src/widgets/profile/change_password_widget.dart b/lib/src/widgets/profile/change_password_widget.dart index 71459f8..9a11ac7 100644 --- a/lib/src/widgets/profile/change_password_widget.dart +++ b/lib/src/widgets/profile/change_password_widget.dart @@ -89,7 +89,7 @@ class _ChangePasswordState extends State { Widget build(BuildContext context) { var theme = Theme.of(context); - void onTapSave() { + Future onTapSave() async { if ((_formKey.currentState?.validate() ?? false) && password2 != null) { widget.service.changePassword(password2!); } @@ -121,10 +121,10 @@ class _ChangePasswordState extends State { ), config.saveButtonBuilder?.call( context, - () => onTapSave(), + onTapSave, ) ?? FilledButton( - onPressed: () => onTapSave(), + onPressed: onTapSave, child: const Text('Save password'), ), const Spacer(), diff --git a/lib/src/widgets/profile/profile_page.dart b/lib/src/widgets/profile/profile_page.dart index 83e2afb..683a884 100644 --- a/lib/src/widgets/profile/profile_page.dart +++ b/lib/src/widgets/profile/profile_page.dart @@ -11,28 +11,37 @@ import 'package:flutter_profile/src/widgets/item_builder/item_builder_options.da import 'package:flutter_profile/src/widgets/profile/profile_style.dart'; import 'package:flutter_profile/src/widgets/profile/profile_wrapper.dart'; -/// The ProfilePage widget is able to show the data of a user. By default the user is able to change this data. The widget has a couple of parameters listed below: +/// The ProfilePage widget is able to show the data of a user. By default the +/// user is able to change this data. The widget has a couple of +/// parameters listed below: /// /// User will contain the data of the user which atleast contain a first name, last name and an avatar/image. Besides this information the [ProfileData] can be used to set custom user fields. /// /// With the use of the service set by a [ProfileService] some actions can be determined what should occur when the user does the following actions: Deleting/editing the profile or uploading an image. /// -/// The style can be used the set some style options regarding the whole form. This is done by setting a [ProfileStyle]. The following styling can be set: The style of the avatar, the padding of the page and default padding between items. +/// The style can be used the set some style options regarding the whole form. +/// This is done by setting a [ProfileStyle]. The following styling can be set: +/// The style of the avatar, the padding of the page and default +/// padding between items. /// /// CustomAvatar can be set to override the standard avatar using any [Widget]. /// -/// 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. /// -/// BottomActionText sets the text for the inkwell at the bottom of the page. If this is set the null then the [InkWell] 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. /// -/// ItemBuilderOptions can be used to just set the settings for fields instead of defining the field itself and how it is used. This field should not be used when the itemBuilder is set. +/// ItemBuilderOptions can be used to just set the settings for fields instead +/// of defining the field itself and how it is used. This field should not +/// be used when the itemBuilder is set. class ProfilePage extends StatefulWidget { const ProfilePage({ - Key? key, required this.user, required this.service, + super.key, this.style = const ProfileStyle(), this.customAvatar, this.showAvatar = true, @@ -49,7 +58,7 @@ class ProfilePage extends StatefulWidget { this.formKey, this.changePasswordConfig = const ChangePasswordConfig(enablePasswordChange: false), - }) : super(key: key); + }); /// User containing all the user data. final User user; @@ -69,10 +78,12 @@ class ProfilePage extends StatefulWidget { ///The background color of the avatar when no image is available. final Color? avatarBackgroundColor; - /// Whether you want to show the input fields, sometimes you just want to edit the avatar. + /// Whether you want to show the input fields, sometimes you just want + /// to edit the avatar. final bool showItems; - /// Sets the text for the [InkWell] at the bottom of the profile page. The [InkWell] is disabled when null. + /// 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. @@ -84,7 +95,8 @@ class ProfilePage extends StatefulWidget { /// Customize the parent widget for all fields final Widget Function(BuildContext context, Widget child)? wrapItemsBuilder; - /// Map keys of items that should be shown first before the default items and the rest of the items. + /// Map keys of items that should be shown first before the default + /// items and the rest of the items. final List prioritizedItems; /// Shows textfields for firstname and lastname if is set to true @@ -93,7 +105,8 @@ class ProfilePage extends StatefulWidget { /// Edit the direction and spacing between every item final WrapViewOptions? wrapViewOptions; - /// The map of extra widgets that might want to be added like empty SizedBoxes for styling. + /// The map of extra widgets that might want to be added like empty + /// SizedBoxes for styling. final Map? extraWidgets; /// Use the form key to save on any custom callback @@ -108,39 +121,38 @@ class ProfilePage extends StatefulWidget { class _ProfilePageState extends State { @override - Widget build(BuildContext context) { - return ProfileWrapper( - service: widget.service, - user: widget.user, - rebuild: () { - setState(() {}); - }, - style: widget.style, - customAvatar: widget.customAvatar, - showAvatar: widget.showAvatar, - showItems: widget.showItems, - bottomActionText: widget.bottomActionText, - itemBuilder: widget.itemBuilder, - itemBuilderOptions: widget.itemBuilderOptions, - prioritizedItems: widget.prioritizedItems, - showDefaultItems: widget.showDefaultItems, - wrapItemsBuilder: widget.wrapItemsBuilder, - wrapViewOptions: widget.wrapViewOptions, - extraWidgets: widget.extraWidgets, - formKey: widget.formKey, - avatarBackgroundColor: widget.avatarBackgroundColor, - changePasswordConfig: widget.changePasswordConfig, - ); - } + Widget build(BuildContext context) => ProfileWrapper( + service: widget.service, + user: widget.user, + rebuild: () { + setState(() {}); + }, + style: widget.style, + customAvatar: widget.customAvatar, + showAvatar: widget.showAvatar, + showItems: widget.showItems, + bottomActionText: widget.bottomActionText, + itemBuilder: widget.itemBuilder, + itemBuilderOptions: widget.itemBuilderOptions, + prioritizedItems: widget.prioritizedItems, + showDefaultItems: widget.showDefaultItems, + wrapItemsBuilder: widget.wrapItemsBuilder, + wrapViewOptions: widget.wrapViewOptions, + extraWidgets: widget.extraWidgets, + formKey: widget.formKey, + avatarBackgroundColor: widget.avatarBackgroundColor, + changePasswordConfig: widget.changePasswordConfig, + ); } class WrapViewOptions { - WrapViewOptions( - {this.direction, - this.spacing, - this.wrapAlignment, - this.runSpacing, - this.clipBehavior}); + WrapViewOptions({ + this.direction, + this.spacing, + this.wrapAlignment, + this.runSpacing, + this.clipBehavior, + }); Axis? direction; double? spacing; double? runSpacing; diff --git a/lib/src/widgets/profile/profile_style.dart b/lib/src/widgets/profile/profile_style.dart index 2212537..5ddbdfe 100644 --- a/lib/src/widgets/profile/profile_style.dart +++ b/lib/src/widgets/profile/profile_style.dart @@ -4,11 +4,13 @@ import 'package:flutter/material.dart'; -/// ProfielStyle is used to set a couple of style paramaters for the whole profile page. +/// ProfielStyle is used to set a couple of style paramaters for the +/// whole profile page. /// /// AvatarStyle is used to set some styling for the avatar using [AvatarStyle]. /// -/// PagePaddign is used to set the padding around the whole profile page with its parent. +/// PagePaddign is used to set the padding around the whole profile page +/// with its parent. /// /// BetweenDefaultitemPadding sets te padding between each user data item. class ProfileStyle { @@ -22,7 +24,8 @@ class ProfileStyle { /// AvatarStyle can be used to set some avatar styling parameters. final TextStyle avatarTextStyle; - /// PagePadding can be set to determine the padding of the whole page againt the profile page parent. + /// PagePadding can be set to determine the padding of the whole page + /// againt the profile page parent. final EdgeInsetsGeometry pagePadding; /// BetweenDefaultItemPadding sets the diff --git a/lib/src/widgets/profile/profile_wrapper.dart b/lib/src/widgets/profile/profile_wrapper.dart index 07c9f63..037cff6 100644 --- a/lib/src/widgets/profile/profile_wrapper.dart +++ b/lib/src/widgets/profile/profile_wrapper.dart @@ -47,7 +47,7 @@ class ProfileWrapper extends StatefulWidget { final String? bottomActionText; final ItemBuilder? itemBuilder; final WrapViewOptions? wrapViewOptions; - final Function rebuild; + final Function() rebuild; final ItemBuilderOptions? itemBuilderOptions; final bool showDefaultItems; final bool showItems; @@ -56,7 +56,8 @@ class ProfileWrapper extends StatefulWidget { final GlobalKey? formKey; final ChangePasswordConfig changePasswordConfig; - /// Map keys of items that should be shown first before the default items and the rest of the items. + /// Map keys of items that should be shown first before the default items and + /// the rest of the items. final List prioritizedItems; @override @@ -78,7 +79,7 @@ class _ProfileWrapperState extends State { super.initState(); if (widget.showDefaultItems) { if (widget.itemBuilder == null) { - ItemBuilder builder = ItemBuilder( + var builder = ItemBuilder( options: widget.itemBuilderOptions ?? ItemBuilderOptions(), ); defaultItems.addAll({ @@ -89,7 +90,7 @@ class _ProfileWrapperState extends State { (value) { submitAllChangedFields(); }, - (v) { + (v) async { if (widget.user.firstName != v) { widget.user.firstName = v; widget.service.editProfile(widget.user, 'first_name', v); @@ -103,7 +104,7 @@ class _ProfileWrapperState extends State { (value) { submitAllChangedFields(); }, - (v) { + (v) async { if (widget.user.lastName != v) { widget.user.lastName = v; widget.service.editProfile(widget.user, 'last_name', v); @@ -120,7 +121,7 @@ class _ProfileWrapperState extends State { (value) { submitAllChangedFields(); }, - (v) { + (v) async { if (widget.user.firstName != v) { widget.user.firstName = v; widget.service.editProfile(widget.user, 'first_name', v); @@ -134,7 +135,7 @@ class _ProfileWrapperState extends State { (value) { submitAllChangedFields(); }, - (v) { + (v) async { if (widget.user.lastName != v) { widget.user.lastName = v; widget.service.editProfile(widget.user, 'last_name', v); @@ -147,28 +148,28 @@ class _ProfileWrapperState extends State { widgets.addAll(widget.extraWidgets ?? {}); widgets.addAll(defaultItems); if (widget.user.profileData != null) { - widgets.addAll(ItemList( - Map.fromEntries(widget.user.profileData!.toMap().entries), - widget.user.profileData!.mapWidget( - () { - widget.rebuild(); + widgets.addAll( + ItemList( + Map.fromEntries(widget.user.profileData!.toMap().entries), + widget.user.profileData!.mapWidget( + () { + widget.rebuild(); + }, + context, + ), + (key, value) async { + if (widget.user.profileData?.toMap()[key] == null) { + widget.service.editProfile(widget.user, key, value); + } else if (widget.user.profileData?.toMap()[key] != value) { + widget.service.editProfile(widget.user, key, value); + } }, - context, - ), - (key, value) { - if (widget.user.toMap()['profile_data'][key] == null) { - widget.service.editProfile(widget.user, key, value); - } else if (widget.user.toMap()['profile_data'][key] != value) { - widget.service.editProfile(widget.user, key, value); - } - }, - () { - submitAllChangedFields(); - }, - itemBuilder: widget.itemBuilder, - itemBuilderOptions: widget.itemBuilderOptions, - formKey: _formKey, - ).getItemList()); + submitAllChangedFields, + itemBuilder: widget.itemBuilder, + itemBuilderOptions: widget.itemBuilderOptions, + formKey: _formKey, + ).getItemList(), + ); } var items = Wrap( @@ -195,92 +196,90 @@ class _ProfileWrapperState extends State { } @override - Widget build(BuildContext context) { - return Material( - color: Colors.transparent, - child: SingleChildScrollView( - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: Padding( - padding: widget.style.pagePadding, - child: Column( - children: [ - if (widget.showAvatar) ...[ - InkWell( - onTap: () => widget.service.uploadImage( - context, - onUploadStateChanged: (isUploading) => setState( - () { - _isUploadingImage = isUploading; - }, + Widget build(BuildContext context) => Material( + color: Colors.transparent, + child: SingleChildScrollView( + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: Padding( + padding: widget.style.pagePadding, + child: Column( + children: [ + if (widget.showAvatar) ...[ + InkWell( + onTap: () async => widget.service.uploadImage( + context, + onUploadStateChanged: (bool isUploading) => setState( + () { + _isUploadingImage = isUploading; + }, + ), + ), + child: AvatarWrapper( + avatarBackgroundColor: widget.avatarBackgroundColor, + user: widget.user, + textStyle: widget.style.avatarTextStyle, + customAvatar: _isUploadingImage + ? Container( + width: 100, + height: 100, + decoration: const BoxDecoration( + color: Colors.black, + shape: BoxShape.circle, + ), + child: const CircularProgressIndicator(), + ) + : widget.customAvatar, ), ), - child: AvatarWrapper( - avatarBackgroundColor: widget.avatarBackgroundColor, - user: widget.user, - textStyle: widget.style.avatarTextStyle, - customAvatar: _isUploadingImage - ? Container( - width: 100, - height: 100, - decoration: const BoxDecoration( - color: Colors.black, - shape: BoxShape.circle, - ), - child: const CircularProgressIndicator(), - ) - : widget.customAvatar, + SizedBox( + height: widget.style.betweenDefaultItemPadding, ), - ), - SizedBox( - height: widget.style.betweenDefaultItemPadding, - ), - ], - if (widget.showItems) Form(key: _formKey, child: child), - if (widget.changePasswordConfig.enablePasswordChange) ...[ - Expanded( - child: ChangePassword( - config: widget.changePasswordConfig, - service: widget.service, - wrapViewOptions: widget.wrapViewOptions, - wrapItemsBuilder: widget.wrapItemsBuilder, - itemBuilder: widget.itemBuilder, - itemBuilderOptions: widget.itemBuilderOptions, - style: widget.style, + ], + if (widget.showItems) Form(key: _formKey, child: child), + if (widget.changePasswordConfig.enablePasswordChange) ...[ + Expanded( + child: ChangePassword( + config: widget.changePasswordConfig, + service: widget.service, + wrapViewOptions: widget.wrapViewOptions, + wrapItemsBuilder: widget.wrapItemsBuilder, + itemBuilder: widget.itemBuilder, + itemBuilderOptions: widget.itemBuilderOptions, + style: widget.style, + ), ), - ), - ], - if (widget.bottomActionText != null) ...[ - SizedBox( - height: widget.style.betweenDefaultItemPadding, - ), - if (!widget.changePasswordConfig.enablePasswordChange) ...[ + ], + if (widget.bottomActionText != null) ...[ + SizedBox( + height: widget.style.betweenDefaultItemPadding, + ), + if (!widget.changePasswordConfig.enablePasswordChange) ...[ + const Spacer(), + ], + InkWell( + onTap: () async { + widget.service.pageBottomAction(); + }, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + widget.bottomActionText!, + style: widget.style.bottomActionTextStyle, + ), + ), + ), + ], + if (widget.bottomActionText == null && + !widget.changePasswordConfig.enablePasswordChange) ...[ const Spacer(), ], - InkWell( - onTap: () { - widget.service.pageBottomAction(); - }, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - widget.bottomActionText!, - style: widget.style.bottomActionTextStyle, - ), - ), - ), ], - if (widget.bottomActionText == null && - !widget.changePasswordConfig.enablePasswordChange) ...[ - const Spacer(), - ] - ], + ), ), ), ), - ), - ); - } + ); /// This calls onSaved on all the fiels which check if they have a new value void submitAllChangedFields() { diff --git a/pubspec.yaml b/pubspec.yaml index 0fb9f71..ed15053 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_profile description: Flutter profile package -version: 1.2.0 +version: 1.2.1 repository: https://github.com/Iconica-Development/flutter_profile publish_to: none @@ -23,6 +23,9 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_iconica_analysis: + git: + url: https://github.com/Iconica-Development/flutter_iconica_analysis + ref: 6.0.0 flutter: diff --git a/test/profile_test.dart b/test/profile_test.dart index 24912be..cf82c30 100644 --- a/test/profile_test.dart +++ b/test/profile_test.dart @@ -25,9 +25,9 @@ void main() { ), ); - final firstNameFinder = find.text('Firstname'); - final lastNameFinder = find.text('Lastname'); - final emailFinder = find.text('test@email.com'); + var firstNameFinder = find.text('Firstname'); + var lastNameFinder = find.text('Lastname'); + var emailFinder = find.text('test@email.com'); expect(firstNameFinder, findsOneWidget); expect(lastNameFinder, findsOneWidget); @@ -74,13 +74,13 @@ void main() { await tester.testTextInput.receiveAction(TextInputAction.send); await tester.pump(); - final firstNameFinder = find.text('Firstname'); - final firstNameEditedFinder = find.text('FirstEditedName'); + var firstNameFinder = find.text('Firstname'); + var firstNameEditedFinder = find.text('FirstEditedName'); - final lastNameFinder = find.text('Lastname'); + var lastNameFinder = find.text('Lastname'); - final emailFinder = find.text('test@email.com'); - final emailEditedFinder = find.text('edited@emial.com'); + var emailFinder = find.text('test@email.com'); + var emailEditedFinder = find.text('edited@emial.com'); expect(firstNameFinder, findsNothing); expect(firstNameEditedFinder, findsOneWidget); diff --git a/test/test_classes/test_profile_service.dart b/test/test_classes/test_profile_service.dart index 101e502..dac3477 100644 --- a/test/test_classes/test_profile_service.dart +++ b/test/test_classes/test_profile_service.dart @@ -23,6 +23,7 @@ class TestProfileService extends ProfileService { @override FutureOr uploadImage( BuildContext context, { + // ignore: avoid_positional_boolean_parameters required Function(bool isUploading) onUploadStateChanged, }) {}