mirror of
https://github.com/Iconica-Development/flutter_profile.git
synced 2025-05-19 01:03:45 +02:00
Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
eda3a928cd | |||
|
3c95ad2c3f | ||
8d3be0083a | |||
|
91735f40cd | ||
|
2e0fcb50c0 | ||
|
9374ea9694 | ||
|
d6ab8e4218 | ||
|
6393eda1c1 | ||
|
28eb2b4c8b | ||
|
fe2dd0f169 | ||
|
6b292175ab | ||
|
597b5fa1f2 | ||
|
cdfe8bb405 | ||
|
b787554c63 | ||
|
5517eeef7b | ||
|
5d9a7de763 | ||
|
802265e43c | ||
|
b4d1306d85 | ||
|
ce3c599fa5 |
25 changed files with 588 additions and 689 deletions
14
.github/workflows/component-documentation.yml
vendored
Normal file
14
.github/workflows/component-documentation.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
name: Iconica Standard Component Documentation Workflow
|
||||
# Workflow Caller version: 1.0.0
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
call-iconica-component-documentation-workflow:
|
||||
uses: Iconica-Development/.github/.github/workflows/component-documentation.yml@master
|
||||
secrets: inherit
|
||||
permissions: write-all
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -23,7 +23,7 @@ migrate_working_dir/
|
|||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
|
@ -37,3 +37,8 @@ example/windows/
|
|||
example/web/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.metadata
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
||||
.fvmrc
|
||||
|
|
10
.metadata
10
.metadata
|
@ -1,10 +0,0 @@
|
|||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||
channel: stable
|
||||
|
||||
project_type: package
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -1,3 +1,19 @@
|
|||
## 1.6.0
|
||||
* Upgraded flutter_input_library to 3.6.0
|
||||
|
||||
## 1.5.0
|
||||
|
||||
- Updated flutter_input_library to 3.2.1
|
||||
- Added the option to give a `BoxFit` to the avatar image
|
||||
|
||||
## 1.4.0
|
||||
|
||||
- Added CONTRIBUTING.md, documentation and updated flutter_input_library to 3.2.1
|
||||
|
||||
## 1.3.0
|
||||
|
||||
- Field has been added so the user can provide it's current password for reauthentication.
|
||||
|
||||
## 1.2.1
|
||||
|
||||
- Added Iconica CI and Iconica Linter
|
||||
|
|
198
CONTRIBUTING.md
Normal file
198
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,198 @@
|
|||
# Contributing
|
||||
|
||||
First off, thanks for taking the time to contribute! ❤️
|
||||
|
||||
All types of contributions are encouraged and valued.
|
||||
See the [Table of Contents](#table-of-contents) for different ways to help and details about how we handle them.
|
||||
Please make sure to read the relevant section before making your contribution.
|
||||
It will make it a lot easier for us maintainers and smooth out the experience for all involved.
|
||||
Iconica looks forward to your contributions. 🎉
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Code of conduct](#code-of-conduct)
|
||||
- [I Have a Question](#i-have-a-question)
|
||||
- [I Want To Contribute](#i-want-to-contribute)
|
||||
- [Reporting Bugs](#reporting-bugs)
|
||||
- [Contributing code](#contributing-code)
|
||||
|
||||
## Code of conduct
|
||||
|
||||
### Legal notice
|
||||
|
||||
When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
|
||||
All accepted pull requests and other additions to this project will be considered intellectual property of Iconica.
|
||||
|
||||
All repositories should be kept clean of jokes, easter eggs and other unnecessary additions.
|
||||
|
||||
## I have a question
|
||||
|
||||
If you want to ask a question, we assume that you have read the available documentation found within the code.
|
||||
Before you ask a question, it is best to search for existing issues that might help you.
|
||||
In case you have found a suitable issue but still need clarification, you can ask your question
|
||||
It is also advisable to search the internet for answers first.
|
||||
|
||||
If you then still feel the need to ask a question and need clarification, we recommend the following:
|
||||
|
||||
- Open an issue.
|
||||
- Provide as much context as you can about what you're running into.
|
||||
|
||||
We will then take care of the issue as soon as possible.
|
||||
|
||||
## I want to contribute
|
||||
|
||||
### Reporting bugs
|
||||
|
||||
<!-- omit in toc -->
|
||||
|
||||
**Before submitting a bug report**
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more information.
|
||||
Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report.
|
||||
Please complete the following steps in advance to help us fix any potential bug as fast as possible.
|
||||
|
||||
- Make sure that you are using the latest version.
|
||||
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (If you are looking for support, you might want to check [this section](#i-have-a-question)).
|
||||
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error.
|
||||
- Also make sure to search the internet (including Stack Overflow) to see if users outside of Iconica have discussed the issue.
|
||||
- Collect information about the bug:
|
||||
- Stack trace (Traceback)
|
||||
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
|
||||
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
|
||||
- Time and date of occurance
|
||||
- Describe the expected result and actual result
|
||||
- Can you reliably reproduce the issue? And can you also reproduce it with older versions? Describe all steps that lead to the bug.
|
||||
|
||||
Once it's filed:
|
||||
|
||||
- The project team will label the issue accordingly.
|
||||
- A team member will try to reproduce the issue with your provided steps.
|
||||
If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for additional information.
|
||||
- If the team is able to reproduce the issue, it will be moved into the backlog, as well as marked with a priority, and the issue will be left to be [implemented by someone](#contributing-code).
|
||||
|
||||
### Contributing code
|
||||
|
||||
When you start working on your contribution, make sure you are aware of the relevant documentation and the functionality of the component you are working on.
|
||||
|
||||
When writing code, follow the style guidelines set by Dart: [Effective Dart](https://Dart.dev/guides/language/effective-Dart). This contains most information you will need to write clean Dart code that is well documented.
|
||||
|
||||
**Documentation**
|
||||
|
||||
As Effective Dart indicates, documenting your public methods with Dart doc comments is recommended.
|
||||
Aside from Effective Dart, we require specific information in the documentation of a method:
|
||||
|
||||
At the very least, your documentation should first name what the code does, then followed below by requirements for calling the method, the result of the method.
|
||||
Any references to internal variables or other methods should be done through [var] to indicate a reference.
|
||||
|
||||
If the method or class is complex enough (determined by the reviewers) an example is required.
|
||||
If unsure, add an example in the docs using code blocks.
|
||||
|
||||
For classes and methods, document the individual parameters with their implications.
|
||||
|
||||
> Tip: Remember that the shortest documentation can be written by having good descriptive names in the first place.
|
||||
|
||||
An example:
|
||||
|
||||
````Dart
|
||||
library iconica_utilities.bidirectional_sorter;
|
||||
|
||||
part 'sorter.Dart';
|
||||
part 'enum.Dart';
|
||||
|
||||
/// Generic sort method, allow sorting of list with primitives or complex types.
|
||||
/// Uses [SortDirection] to determine the direction, either Ascending or Descending,
|
||||
/// Gets called on [List] toSort of type [T] which cannot be shorter than 2.
|
||||
/// Optionally for complex types a [Comparable] [Function] can be given to compare complex types.
|
||||
/// ```
|
||||
/// List<TestObject> objects = [];
|
||||
/// for (int i = 0; i < 10; i++) {
|
||||
/// objects.add(TestObject(name: "name", id: i));
|
||||
/// }
|
||||
///
|
||||
/// sort<TestObject>(
|
||||
/// SortDirection.descending, objects, (object) => object.id);
|
||||
///
|
||||
/// ```
|
||||
/// In the above example a list of TestObjects is created, and then sorted in descending order.
|
||||
/// If the implementation of TestObject is as following:
|
||||
/// ```
|
||||
/// class TestObject {
|
||||
/// final String name;
|
||||
/// final int id;
|
||||
///
|
||||
/// TestObject({required this.name, required this.id});
|
||||
/// }
|
||||
/// ```
|
||||
/// And the list is logged to the console, the following will appear:
|
||||
/// ```
|
||||
/// [name9, name8, name7, name6, name5, name4, name3, name2, name1, name0]
|
||||
/// ```
|
||||
|
||||
void sort<T>(
|
||||
/// Determines the sorting direction, can be either Ascending or Descending
|
||||
SortDirection sortDirection,
|
||||
|
||||
/// Incoming list, which gets sorted
|
||||
List<T> toSort, [
|
||||
|
||||
/// Optional comparable, which is only necessary for complex types
|
||||
SortFieldGetter<T>? sortValueCallback,
|
||||
]) {
|
||||
if (toSort.length < 2) return;
|
||||
assert(
|
||||
toSort.whereType<Comparable>().isNotEmpty || sortValueCallback != null);
|
||||
BidirectionalSorter<T>(
|
||||
sortInstructions: <SortInstruction<T>>[
|
||||
SortInstruction(
|
||||
sortValueCallback ?? (t) => t as Comparable, sortDirection),
|
||||
],
|
||||
).sort(toSort);
|
||||
}
|
||||
|
||||
/// same functionality as [sort] but with the added functionality
|
||||
/// of sorting multiple values
|
||||
void sortMulti<T>(
|
||||
/// Incoming list, which gets sorted
|
||||
List<T> toSort,
|
||||
|
||||
/// list of comparables to sort multiple values at once,
|
||||
/// priority based on index
|
||||
List<SortInstruction<T>> sortValueCallbacks,
|
||||
) {
|
||||
if (toSort.length < 2) return;
|
||||
assert(sortValueCallbacks.isNotEmpty);
|
||||
BidirectionalSorter<T>(
|
||||
sortInstructions: sortValueCallbacks,
|
||||
).sort(toSort);
|
||||
}
|
||||
|
||||
````
|
||||
|
||||
**Tests**
|
||||
|
||||
For each public method that was created, excluding widgets, which contains any form of logic (e.g. Calculations, predicates or major side-effects) tests are required.
|
||||
|
||||
A set of tests is written for each method, covering at least each path within the method. For example:
|
||||
|
||||
```Dart
|
||||
void foo() {
|
||||
try {
|
||||
var bar = doSomething();
|
||||
if (bar) {
|
||||
doSomethingElse();
|
||||
} else {
|
||||
doSomethingCool();
|
||||
}
|
||||
} catch (_) {
|
||||
displayError();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The method above should result in 3 tests:
|
||||
|
||||
1. A test for the path leading to displayError by the cause of an exception
|
||||
2. A test for if bar is true, resulting in doSomethingElse()
|
||||
3. A test for if bar is false, resulting in the doSomethingCool() method being called.
|
||||
|
||||
This means that we require 100% coverage of each method you test.
|
|
@ -31,6 +31,7 @@ Underneath are all paramters, of the 'ProfilePage' widget, listed with an explan
|
|||
| itemBuilderOptions | The options used by the standard itemBuilder to alter the function and style of the textfields |
|
||||
| prioritizedItems | The items that are displayed at the top of the page. Before all the other items in the list and the default items |
|
||||
|
||||
By default input fields are saved after pressing 'enter' inside of the input field.
|
||||
|
||||
## Issues
|
||||
|
||||
|
@ -38,7 +39,7 @@ Please file any issues, bugs or feature request as an issue on our [GitHub](http
|
|||
|
||||
## Want to contribute
|
||||
|
||||
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](../CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_profile/pulls).
|
||||
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_profile/pulls).
|
||||
|
||||
## Author
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: android
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: ios
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: linux
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: macos
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: web
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: windows
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
|
@ -52,62 +52,67 @@ class _ProfileExampleState extends State<ProfileExample> {
|
|||
var width = MediaQuery.of(context).size.width;
|
||||
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: ProfilePage(
|
||||
changePasswordConfig:
|
||||
const ChangePasswordConfig(enablePasswordChange: true),
|
||||
wrapViewOptions: WrapViewOptions(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 16,
|
||||
),
|
||||
bottomActionText: 'Log out',
|
||||
itemBuilderOptions: ItemBuilderOptions(
|
||||
//no label for email
|
||||
validators: {
|
||||
'first_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'last_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'email': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
body: ProfilePage(
|
||||
changePasswordConfig:
|
||||
const ChangePasswordConfig(enablePasswordChange: true),
|
||||
wrapViewOptions: WrapViewOptions(
|
||||
spacing: 8,
|
||||
direction: Axis.vertical,
|
||||
),
|
||||
bottomActionText: 'Log out',
|
||||
itemBuilderOptions: ItemBuilderOptions(
|
||||
//no label for email
|
||||
validators: {
|
||||
'first_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
inputDecorationField: {
|
||||
'password_1': const InputDecoration(
|
||||
'last_name': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
'email': (String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Field empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
|
||||
inputDecorationField: {
|
||||
'current_password': const InputDecoration(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: 60,
|
||||
maxWidth: 200,
|
||||
maxWidth: 250,
|
||||
),
|
||||
),
|
||||
'password_2': const InputDecoration(
|
||||
hintText: 'Current password'),
|
||||
'password_1': const InputDecoration(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: 60,
|
||||
maxWidth: 200,
|
||||
maxWidth: 250,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
user: _user,
|
||||
service: ExampleProfileService(),
|
||||
style: ProfileStyle(
|
||||
avatarTextStyle: const TextStyle(fontSize: 20),
|
||||
pagePadding: EdgeInsets.only(
|
||||
top: 50,
|
||||
bottom: 50,
|
||||
left: width * 0.1,
|
||||
right: width * 0.1,
|
||||
),
|
||||
hintText: 'New password'),
|
||||
'password_2': const InputDecoration(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: 60,
|
||||
maxWidth: 250,
|
||||
),
|
||||
hintText: 'Repeat new password'),
|
||||
},
|
||||
),
|
||||
user: _user,
|
||||
service: ExampleProfileService(),
|
||||
style: ProfileStyle(
|
||||
avatarTextStyle: const TextStyle(fontSize: 20),
|
||||
pagePadding: EdgeInsets.only(
|
||||
top: 50,
|
||||
bottom: 50,
|
||||
left: width * 0.1,
|
||||
right: width * 0.1,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -17,7 +17,7 @@ class ExampleProfileData extends ProfileData {
|
|||
String? remarks;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> mapWidget(
|
||||
Map<String, Widget?> mapWidget(
|
||||
VoidCallback update,
|
||||
BuildContext context,
|
||||
) {
|
||||
|
@ -38,7 +38,7 @@ class ExampleProfileData extends ProfileData {
|
|||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
Map<String, String?> toMap() {
|
||||
return {'email': email, 'about': about, 'remarks': remarks};
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,10 @@ class ExampleProfileService extends ProfileService {
|
|||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> changePassword(String password) {
|
||||
debugPrint(password);
|
||||
FutureOr<bool> changePassword(
|
||||
BuildContext context, String currentPassword, String newPassword) {
|
||||
debugPrint('$currentPassword -> $newPassword');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,437 +0,0 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
cached_network_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image
|
||||
sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
cached_network_image_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_platform_interface
|
||||
sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
cached_network_image_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_web
|
||||
sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
flutter_input_library:
|
||||
dependency: transitive
|
||||
description:
|
||||
path: "."
|
||||
ref: "2.7.0"
|
||||
resolved-ref: "8eb1d80a9f08be0b7fe70078104d1a8851083edd"
|
||||
url: "https://github.com/Iconica-Development/flutter_input_library"
|
||||
source: git
|
||||
version: "2.7.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
flutter_profile:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.2.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.5"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: octo_image
|
||||
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "050e8e85e4b7fecdf2bb3682c1c64c4887a183720c802d323de8a5fd76d372dd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.11"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.22"
|
||||
path_provider_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_ios
|
||||
sha256: "03d639406f5343478352433f00d3c4394d52dac8df3d847869c5e2333e0bbce8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.11"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
path_provider_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_macos
|
||||
sha256: "2a97e7fbb7ae9dcd0dfc1220a78e9ec3e71da691912e617e8715ff2a13086ae8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.27.7"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: d21c022832f139b89922738e200c07387a49c549bf36c35654418e19ff76d161
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0+3"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "0c21a187d645aa65da5be6997c0c713eed61e049158870ae2de157e6897067ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0+2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "7b530acd9cb7c71b0019a1e7fa22c4105e675557a4400b6a401c71c5e0ade1ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0+3"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: d13ac5deea7327f027b3b97ee19ee210f68256ecf3f1a304bcfb992ee947637c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "11541eedefbcaec9de35aa82650b695297ce668662bbd6e3911a7fabdbde589f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0+2"
|
||||
sdks:
|
||||
dart: ">=3.2.0-194.0.dev <4.0.0"
|
||||
flutter: ">=3.10.0"
|
|
@ -25,13 +25,32 @@ class User {
|
|||
imageUrl: data['image_url'],
|
||||
profileData: data['profile_data'],
|
||||
);
|
||||
|
||||
/// The first name of the user.
|
||||
String? firstName;
|
||||
|
||||
/// The last name of the user.
|
||||
String? lastName;
|
||||
|
||||
/// The image of the user, stored as Uint8List.
|
||||
Uint8List? image;
|
||||
|
||||
/// The URL of the user's image.
|
||||
String? imageUrl;
|
||||
|
||||
/// Additional profile data for the user.
|
||||
ProfileData? profileData;
|
||||
|
||||
/// The display name of the user, which is a combination of
|
||||
/// the first name and the last name.
|
||||
/// If the first name or the last name is null,
|
||||
/// an empty string is used instead.
|
||||
String get displayName => '${firstName ?? ''} ${lastName ?? ''}';
|
||||
|
||||
/// The initials of the user, which are the first characters
|
||||
/// of the first name and the last name.
|
||||
/// If the first name or the last name is null or empty,
|
||||
/// an empty string is used instead.
|
||||
String get initials =>
|
||||
'${(firstName?.isNotEmpty ?? false) ? firstName![0] : ''}'
|
||||
'${(lastName?.isNotEmpty ?? false) ? lastName![0] : ''}';
|
||||
|
@ -47,19 +66,19 @@ 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.
|
||||
/// 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
|
||||
/// The Builditems method is used to make the list of
|
||||
/// field to house the user data.
|
||||
abstract class ProfileData {
|
||||
const ProfileData();
|
||||
|
||||
ProfileData fromMap(Map<String, dynamic> data);
|
||||
|
||||
Map<String, dynamic> toMap();
|
||||
Map<String, String?> toMap();
|
||||
|
||||
Map<String, dynamic> mapWidget(VoidCallback update, BuildContext context);
|
||||
Map<String, Widget?> mapWidget(VoidCallback update, BuildContext context);
|
||||
|
||||
ProfileData create();
|
||||
}
|
||||
|
|
|
@ -6,15 +6,18 @@ 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.
|
||||
/// 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
|
||||
/// 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.
|
||||
///
|
||||
/// UploadImage is called when te user presses the avatar.
|
||||
///
|
||||
/// changePassword is called when the user requests to change his password.
|
||||
/// Return true to clear the inputfields.
|
||||
abstract class ProfileService {
|
||||
const ProfileService();
|
||||
|
||||
|
@ -28,5 +31,9 @@ abstract class ProfileService {
|
|||
required Function(bool isUploading) onUploadStateChanged,
|
||||
});
|
||||
|
||||
FutureOr<void> changePassword(String password);
|
||||
FutureOr<bool> changePassword(
|
||||
BuildContext context,
|
||||
String currentPassword,
|
||||
String newPassword,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,12 +12,20 @@ class Avatar extends StatelessWidget {
|
|||
this.user,
|
||||
this.size = 100,
|
||||
this.avatarBackgroundColor,
|
||||
this.boxfit = BoxFit.contain,
|
||||
});
|
||||
|
||||
/// The user object containing user information.
|
||||
final User? user;
|
||||
|
||||
/// Size of the avatar.
|
||||
final double size;
|
||||
|
||||
/// Background color of the avatar.
|
||||
final Color? avatarBackgroundColor;
|
||||
|
||||
final BoxFit boxfit;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var imageProvider = _getImageProvider(user);
|
||||
|
@ -37,7 +45,7 @@ class Avatar extends StatelessWidget {
|
|||
image: hasImage
|
||||
? DecorationImage(
|
||||
image: imageProvider,
|
||||
fit: BoxFit.contain,
|
||||
fit: boxfit,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
@ -54,6 +62,7 @@ class Avatar extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
/// Returns the image provider based on user's image or image URL.
|
||||
ImageProvider? _getImageProvider(User? user) {
|
||||
if (user?.image != null) {
|
||||
return MemoryImage(user!.image!);
|
||||
|
@ -63,6 +72,8 @@ class Avatar extends StatelessWidget {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Generates a color based on the initials of the user's
|
||||
/// first name and last name.
|
||||
Color _generateColorWithIntials(String? firstName, String? lastName) {
|
||||
var idFirstName = 0;
|
||||
var idLastName = 0;
|
||||
|
|
|
@ -18,14 +18,27 @@ class AvatarWrapper extends StatelessWidget {
|
|||
this.avatarBackgroundColor,
|
||||
});
|
||||
|
||||
/// The user object containing user information.
|
||||
final User user;
|
||||
|
||||
/// Custom widget to be used as an avatar.
|
||||
final Widget? customAvatar;
|
||||
|
||||
/// Background color of the avatar.
|
||||
final Color? avatarBackgroundColor;
|
||||
|
||||
/// Whether to show the user's name beneath the avatar.
|
||||
final bool showName;
|
||||
|
||||
/// Padding around the avatar and the name.
|
||||
final EdgeInsets padding;
|
||||
final TextStyle? textStyle;
|
||||
|
||||
/// Size of the avatar.
|
||||
final double size;
|
||||
|
||||
/// Style for the user's name.
|
||||
final TextStyle? textStyle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var avatar = customAvatar ??
|
||||
|
@ -43,8 +56,8 @@ class AvatarWrapper extends StatelessWidget {
|
|||
padding: padding,
|
||||
child: Flexible(
|
||||
child: Text(
|
||||
style: textStyle,
|
||||
user.displayName,
|
||||
style: textStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -6,7 +6,7 @@ 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
|
||||
/// ItemBuilder is used to set the standard textfield for each undefined
|
||||
/// users data item.
|
||||
///
|
||||
/// Options sets options for the textfield.
|
||||
|
@ -19,14 +19,14 @@ class ItemBuilder {
|
|||
|
||||
Widget build(
|
||||
String key,
|
||||
value,
|
||||
String? value,
|
||||
Widget? widget,
|
||||
Function(String) updateItem,
|
||||
Function(String?) saveItem,
|
||||
void Function(String) updateItem,
|
||||
void Function(String?) saveItem,
|
||||
) {
|
||||
if (widget == null) {
|
||||
var controller = TextEditingController(
|
||||
text: '${value ?? ''}',
|
||||
text: value ?? '',
|
||||
);
|
||||
|
||||
var inputDecoration =
|
||||
|
@ -54,13 +54,15 @@ class ItemBuilder {
|
|||
|
||||
Widget buildPassword(
|
||||
String key,
|
||||
Function(String?) onChanged,
|
||||
TextEditingController controller,
|
||||
void Function(String?) onChanged,
|
||||
String? Function(String?) validator,
|
||||
) {
|
||||
var inputDecoration =
|
||||
options.inputDecorationField?[key] ?? options.inputDecoration;
|
||||
|
||||
return FlutterFormInputPassword(
|
||||
controller: controller,
|
||||
style: options.inputTextStyle,
|
||||
decoration: inputDecoration,
|
||||
onChanged: onChanged,
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// ItemBuilderOptions is a class to store all settings for a field in the
|
||||
/// 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
|
||||
/// 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.
|
||||
|
|
|
@ -16,46 +16,56 @@ class ItemList {
|
|||
this.itemBuilder,
|
||||
this.itemBuilderOptions,
|
||||
}) {
|
||||
for (var item in items.entries) {
|
||||
widgets.addAll({
|
||||
item.key: itemBuilder == null
|
||||
? builder.build(
|
||||
item.key,
|
||||
item.value,
|
||||
typeMap[item.key],
|
||||
(value) {
|
||||
saveProfile();
|
||||
},
|
||||
(value) {
|
||||
updateProfile(item.key, value);
|
||||
},
|
||||
)
|
||||
: itemBuilder!.build(
|
||||
item.key,
|
||||
item.value,
|
||||
typeMap[item.key],
|
||||
(value) {
|
||||
saveProfile();
|
||||
},
|
||||
(value) {
|
||||
updateProfile(item.key, value);
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
var itemBuilder = this.itemBuilder ?? builder;
|
||||
|
||||
widgets = {
|
||||
for (var item in items.entries) ...{
|
||||
item.key: itemBuilder.build(
|
||||
item.key,
|
||||
item.value,
|
||||
typeMap[item.key],
|
||||
(value) {
|
||||
saveProfile();
|
||||
},
|
||||
(value) {
|
||||
updateProfile(item.key, value);
|
||||
},
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Gets the map of item keys and their corresponding widgets.
|
||||
Map<String, Widget> getItemList() => widgets;
|
||||
|
||||
final Map<String, dynamic> items;
|
||||
final Map<String, dynamic> typeMap;
|
||||
final Function(String, String?) updateProfile;
|
||||
final Function() saveProfile;
|
||||
/// Map containing item keys and their values.
|
||||
final Map<String, String?> items;
|
||||
|
||||
/// Map containing item keys and their types.
|
||||
final Map<String, Widget?> typeMap;
|
||||
|
||||
/// Function to update the profile with a specific item's value.
|
||||
final void Function(String, String?) updateProfile;
|
||||
|
||||
/// Function to save the profile after an item value is updated.
|
||||
final void Function() saveProfile;
|
||||
|
||||
/// Builder for custom item widgets.
|
||||
final ItemBuilder? itemBuilder;
|
||||
|
||||
/// Options for the item builder.
|
||||
final ItemBuilderOptions? itemBuilderOptions;
|
||||
|
||||
/// Global key for the form associated with the item list.
|
||||
final GlobalKey<FormState> formKey;
|
||||
|
||||
Map<String, Widget> widgets = {};
|
||||
/// Map containing item keys and their corresponding widgets.
|
||||
late final Map<String, Widget> widgets;
|
||||
|
||||
/// `builder` is an instance of `ItemBuilder` which is used
|
||||
/// to build the items in the list.
|
||||
/// If `itemBuilderOptions` is not provided, a default
|
||||
/// `ItemBuilderOptions` instance is used.
|
||||
late ItemBuilder builder = ItemBuilder(
|
||||
options: itemBuilderOptions ?? ItemBuilderOptions(),
|
||||
);
|
||||
|
|
|
@ -13,12 +13,25 @@ class ChangePassword extends StatefulWidget {
|
|||
super.key,
|
||||
});
|
||||
|
||||
/// Configuration for changing password.
|
||||
final ChangePasswordConfig config;
|
||||
|
||||
/// Options for wrapping the items.
|
||||
final WrapViewOptions? wrapViewOptions;
|
||||
final ItemBuilder? itemBuilder;
|
||||
final ItemBuilderOptions? itemBuilderOptions;
|
||||
|
||||
/// Builder for wrapping items.
|
||||
final Widget Function(BuildContext context, Widget child)? wrapItemsBuilder;
|
||||
|
||||
/// Builder for creating items.
|
||||
final ItemBuilder? itemBuilder;
|
||||
|
||||
/// Options for item builder.
|
||||
final ItemBuilderOptions? itemBuilderOptions;
|
||||
|
||||
/// Styling options for the widget.
|
||||
final ProfileStyle style;
|
||||
|
||||
/// Profile service for managing user profile.
|
||||
final ProfileService service;
|
||||
|
||||
@override
|
||||
|
@ -32,6 +45,11 @@ class _ChangePasswordState extends State<ChangePassword> {
|
|||
|
||||
late final Widget? changePasswordChild;
|
||||
|
||||
late var currentPasswordController = TextEditingController();
|
||||
late var password1Controller = TextEditingController();
|
||||
late var password2Controller = TextEditingController();
|
||||
|
||||
String? currentPassword;
|
||||
String? password1;
|
||||
String? password2;
|
||||
|
||||
|
@ -51,8 +69,21 @@ class _ChangePasswordState extends State<ChangePassword> {
|
|||
runSpacing: widget.wrapViewOptions?.runSpacing ?? 0,
|
||||
clipBehavior: widget.wrapViewOptions?.clipBehavior ?? Clip.none,
|
||||
children: [
|
||||
builder.buildPassword(
|
||||
'current_password',
|
||||
currentPasswordController,
|
||||
(value) => currentPassword = value,
|
||||
(value) {
|
||||
if (currentPassword?.isEmpty ?? true) {
|
||||
return config.fieldRequiredErrorText;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
),
|
||||
builder.buildPassword(
|
||||
'password_1',
|
||||
password1Controller,
|
||||
(value) => password1 = value,
|
||||
(value) {
|
||||
if (password1?.isEmpty ?? true) {
|
||||
|
@ -64,6 +95,7 @@ class _ChangePasswordState extends State<ChangePassword> {
|
|||
),
|
||||
builder.buildPassword(
|
||||
'password_2',
|
||||
password2Controller,
|
||||
(value) => password2 = value,
|
||||
(value) {
|
||||
if (password2?.isEmpty ?? true) {
|
||||
|
@ -90,14 +122,26 @@ class _ChangePasswordState extends State<ChangePassword> {
|
|||
var theme = Theme.of(context);
|
||||
|
||||
Future<void> onTapSave() async {
|
||||
if ((_formKey.currentState?.validate() ?? false) && password2 != null) {
|
||||
widget.service.changePassword(password2!);
|
||||
if ((_formKey.currentState?.validate() ?? false) &&
|
||||
currentPassword != null &&
|
||||
password2 != null) {
|
||||
if (await widget.service
|
||||
.changePassword(context, currentPassword!, password2!)) {
|
||||
currentPasswordController.clear();
|
||||
password1Controller.clear();
|
||||
password2Controller.clear();
|
||||
|
||||
currentPassword = null;
|
||||
password1 = null;
|
||||
password2 = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: widget.style.betweenDefaultItemPadding * 2.5,
|
||||
|
@ -127,7 +171,6 @@ class _ChangePasswordState extends State<ChangePassword> {
|
|||
onPressed: onTapSave,
|
||||
child: const Text('Save password'),
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -153,9 +153,19 @@ class WrapViewOptions {
|
|||
this.runSpacing,
|
||||
this.clipBehavior,
|
||||
});
|
||||
|
||||
/// The direction to use as the main axis.
|
||||
Axis? direction;
|
||||
|
||||
/// The distance between adjacent children in the cross axis.
|
||||
double? spacing;
|
||||
double? runSpacing;
|
||||
Clip? clipBehavior;
|
||||
|
||||
/// How the children should be placed along the main axis.
|
||||
WrapAlignment? wrapAlignment;
|
||||
|
||||
/// The distance between adjacent children in the main axis.
|
||||
double? runSpacing;
|
||||
|
||||
/// Determines how visual overflow should be handled.
|
||||
Clip? clipBehavior;
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// ProfielStyle is used to set a couple of style paramaters for the
|
||||
/// 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
|
||||
/// PagePaddign is used to set the padding around the whole profile page
|
||||
/// with its parent.
|
||||
///
|
||||
/// BetweenDefaultitemPadding sets te padding between each user data item.
|
||||
|
@ -24,7 +24,7 @@ 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
|
||||
/// PagePadding can be set to determine the padding of the whole page
|
||||
/// againt the profile page parent.
|
||||
final EdgeInsetsGeometry pagePadding;
|
||||
|
||||
|
|
|
@ -38,22 +38,55 @@ class ProfileWrapper extends StatefulWidget {
|
|||
super.key,
|
||||
});
|
||||
|
||||
/// The user object containing user information.
|
||||
final User user;
|
||||
|
||||
/// The service for managing user profile.
|
||||
final ProfileService service;
|
||||
|
||||
/// The styling options for the profile.
|
||||
final ProfileStyle style;
|
||||
|
||||
/// Custom avatar widget.
|
||||
final Widget? customAvatar;
|
||||
|
||||
/// Flag to show or hide the avatar.
|
||||
final bool showAvatar;
|
||||
|
||||
/// Background color for the avatar.
|
||||
final Color? avatarBackgroundColor;
|
||||
|
||||
/// Text for the bottom action.
|
||||
final String? bottomActionText;
|
||||
|
||||
/// Builder for creating items.
|
||||
final ItemBuilder? itemBuilder;
|
||||
final WrapViewOptions? wrapViewOptions;
|
||||
final Function() rebuild;
|
||||
|
||||
/// Options for item builder.
|
||||
final ItemBuilderOptions? itemBuilderOptions;
|
||||
|
||||
/// Options for wrapping the view.
|
||||
final WrapViewOptions? wrapViewOptions;
|
||||
|
||||
/// Callback to rebuild the widget.
|
||||
final Function() rebuild;
|
||||
|
||||
/// Flag to show default items.
|
||||
final bool showDefaultItems;
|
||||
|
||||
/// Flag to show items.
|
||||
final bool showItems;
|
||||
|
||||
/// Builder for wrapping items.
|
||||
final Widget Function(BuildContext context, Widget child)? wrapItemsBuilder;
|
||||
final Map<String, Widget>? extraWidgets;
|
||||
|
||||
/// Key for the form.
|
||||
final GlobalKey<FormState>? formKey;
|
||||
|
||||
/// Additional widgets to be displayed.
|
||||
final Map<String, Widget>? extraWidgets;
|
||||
|
||||
/// Configuration for changing password.
|
||||
final ChangePasswordConfig changePasswordConfig;
|
||||
|
||||
/// Map keys of items that should be shown first before the default items and
|
||||
|
@ -74,9 +107,9 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_formKey = widget.formKey ?? GlobalKey<FormState>();
|
||||
|
||||
super.initState();
|
||||
if (widget.showDefaultItems) {
|
||||
if (widget.itemBuilder == null) {
|
||||
var builder = ItemBuilder(
|
||||
|
@ -196,50 +229,55 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Material(
|
||||
color: Colors.transparent,
|
||||
child: SingleChildScrollView(
|
||||
child: SizedBox(
|
||||
Widget build(BuildContext context) => Stack(
|
||||
children: [
|
||||
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;
|
||||
},
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: widget.style.pagePadding,
|
||||
child: Column(
|
||||
children: [
|
||||
if (widget.showAvatar) ...[
|
||||
InkWell(
|
||||
onTap: () async => widget.service.uploadImage(
|
||||
context,
|
||||
onUploadStateChanged: (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(
|
||||
],
|
||||
if (widget.showItems) ...[
|
||||
Form(
|
||||
key: _formKey,
|
||||
child: child,
|
||||
),
|
||||
],
|
||||
if (widget.changePasswordConfig.enablePasswordChange) ...[
|
||||
ChangePassword(
|
||||
config: widget.changePasswordConfig,
|
||||
service: widget.service,
|
||||
wrapViewOptions: widget.wrapViewOptions,
|
||||
|
@ -248,42 +286,35 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
|
|||
itemBuilderOptions: widget.itemBuilderOptions,
|
||||
style: widget.style,
|
||||
),
|
||||
),
|
||||
],
|
||||
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(),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.bottomActionText != null &&
|
||||
MediaQuery.of(Scaffold.of(context).context).viewInsets.bottom ==
|
||||
0.0) ...[
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: InkWell(
|
||||
onTap: () async => await widget.service.pageBottomAction(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
widget.bottomActionText!,
|
||||
style: widget.style.bottomActionTextStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
|
||||
/// This calls onSaved on all the fiels which check if they have a new value
|
||||
void submitAllChangedFields() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
_formKey.currentState!.save();
|
||||
}
|
||||
}
|
||||
|
|
12
pubspec.yaml
12
pubspec.yaml
|
@ -1,21 +1,19 @@
|
|||
name: flutter_profile
|
||||
description: Flutter profile package
|
||||
version: 1.2.1
|
||||
version: 1.6.0
|
||||
repository: https://github.com/Iconica-Development/flutter_profile
|
||||
|
||||
publish_to: none
|
||||
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||
|
||||
environment:
|
||||
sdk: ^3.0.0
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
cached_network_image: ^3.3.0
|
||||
|
||||
flutter_input_library:
|
||||
git:
|
||||
url: https://github.com/Iconica-Development/flutter_input_library
|
||||
ref: 2.7.0
|
||||
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||
version: ^3.6.0
|
||||
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
|
|
@ -13,7 +13,7 @@ class TestProfileData extends ProfileData {
|
|||
String? email;
|
||||
|
||||
@override
|
||||
Map<String, dynamic> mapWidget(
|
||||
Map<String, Widget?> mapWidget(
|
||||
VoidCallback update,
|
||||
BuildContext context,
|
||||
) =>
|
||||
|
@ -27,7 +27,7 @@ class TestProfileData extends ProfileData {
|
|||
);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() => {
|
||||
Map<String, String?> toMap() => {
|
||||
'email': email,
|
||||
};
|
||||
|
||||
|
|
|
@ -28,5 +28,10 @@ class TestProfileService extends ProfileService {
|
|||
}) {}
|
||||
|
||||
@override
|
||||
FutureOr<void> changePassword(String password) {}
|
||||
FutureOr<bool> changePassword(
|
||||
BuildContext context,
|
||||
String currentPassword,
|
||||
String newPassword,
|
||||
) =>
|
||||
true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue