first test version

Functionality has been added and is working. No test or documentation has been written yet.
This commit is contained in:
Jacques Doeleman 2022-09-19 12:11:58 +02:00
parent ce4054f478
commit 38b076416f
9 changed files with 129 additions and 94 deletions

View file

@ -58,8 +58,8 @@ class MyProfileService extends ProfileService {
}
@override
uploadImage() {
return super.uploadImage();
uploadImage(context) {
return super.uploadImage(context);
}
}
@ -73,40 +73,38 @@ class MyProfileData extends ProfileData {
int justMyString;
@override
Map<String, dynamic> mapWidget(Function update) {
Map<String, dynamic> mapWidget(Function update, BuildContext context) {
return {
'justMyString': Container(
'justMyString': SizedBox(
height: 100,
width: 300,
child: Row(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: justMyString == 1 ? Colors.green : Colors.blue,
backgroundColor: justMyString == 1 ? Colors.green : Colors.blue,
),
onPressed: () {
justMyString = 1;
update();
print(justMyString);
},
child: const Text('1'),
),
const Spacer(),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: justMyString == 2 ? Colors.green : Colors.blue,
backgroundColor: justMyString == 2 ? Colors.green : Colors.blue,
),
onPressed: () {
justMyString = 2;
update();
print(justMyString);
},
child: const Text('2'),
),
const Spacer(),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: justMyString == 3 ? Colors.green : Colors.blue,
backgroundColor: justMyString == 3 ? Colors.green : Colors.blue,
),
onPressed: () {
justMyString = 3;
@ -117,7 +115,7 @@ class MyProfileData extends ProfileData {
const Spacer(),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: justMyString == 4 ? Colors.green : Colors.blue,
backgroundColor: justMyString == 4 ? Colors.green : Colors.blue,
),
onPressed: () {
justMyString = 4;

View file

@ -7,7 +7,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
version: "2.9.0"
boolean_selector:
dependency: transitive
description:
@ -21,21 +21,14 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
collection:
dependency: transitive
description:
@ -56,7 +49,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
flutter:
dependency: "direct main"
description: flutter
@ -87,28 +80,28 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.11"
version: "0.12.12"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
version: "0.1.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
version: "1.8.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
profile:
dependency: "direct main"
description:
@ -127,7 +120,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
version: "1.9.0"
stack_trace:
dependency: transitive
description:
@ -148,21 +141,21 @@ packages:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.1.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
version: "0.4.12"
vector_math:
dependency: transitive
description:

View file

@ -1,6 +1,9 @@
library profile;
export 'src/widgets/profile/profile_page.dart';
export 'src/widgets/profile/profile_style.dart';
export 'src/widgets/avatar/avatar_style.dart';
export 'src/services/profile_service.dart';
export 'src/widgets/item_builder/item_builder.dart';
export 'src/models/user.dart';
export 'src/widgets/item_builder/item_builder_options.dart';

View file

@ -2,8 +2,6 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:profile/profile.dart';
import 'package:profile/src/widgets/item_builder/item_builder.dart';
import 'package:profile/src/widgets/item_builder/item_builder_options.dart';
class User<T extends ProfileData> {
String? firstName;
@ -44,7 +42,7 @@ abstract class ProfileData {
Map<String, dynamic> toMap();
Map<String, dynamic> mapWidget(Function update);
Map<String, dynamic> mapWidget(VoidCallback update, BuildContext context);
ProfileData create();
@ -58,12 +56,13 @@ abstract class ProfileData {
}) {
var widgets = <Widget>[];
ItemBuilder builder = ItemBuilder(
options: itemBuilderOptions ?? const ItemBuilderOptions(),
options: itemBuilderOptions ?? ItemBuilderOptions(),
);
for (var item in items.entries) {
itemBuilder == null
? widgets.add(
builder.build(
item.key,
item.value,
typeMap[item.key],
(value) {
@ -73,6 +72,7 @@ abstract class ProfileData {
)
: widgets.add(
itemBuilder.build(
item.key,
item.value,
typeMap[item.key],
(value) {

View file

@ -1,12 +1,10 @@
import 'package:flutter/material.dart';
import 'package:profile/profile.dart';
abstract class ProfileService {
const ProfileService();
deleteProfile() {
print("Request to delete profile");
// TODO(anyone) project specific
}
deleteProfile() {}
editProfile<T extends ProfileData>(User user, String key, String value) {
if (user.profileData != null) {
@ -19,8 +17,5 @@ abstract class ProfileService {
}
}
uploadImage() {
print('Request to change picture');
// TODO(anyone) open image picker and update profile
}
uploadImage(BuildContext context) async {}
}

View file

@ -7,13 +7,15 @@ class Avatar extends StatelessWidget {
const Avatar({
Key? key,
this.image,
this.name,
this.firstName,
this.lastName,
this.avatar,
this.style = const AvatarStyle(),
}) : super(key: key);
final Uint8List? image;
final String? name;
final String? firstName;
final String? lastName;
final Widget? avatar;
final AvatarStyle style;
@ -22,9 +24,12 @@ class Avatar extends StatelessWidget {
return Column(
children: [
_avatar(),
if (name != null)
const SizedBox(
height: 16,
),
if (firstName != null || lastName != null)
Text(
name!,
'${firstName ?? ''} ${lastName ?? ''}',
style: style.displayNameStyle,
)
],
@ -39,22 +44,26 @@ class Avatar extends StatelessWidget {
return Container(
width: style.width,
height: style.height,
decoration: const BoxDecoration(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: MemoryImage(image!),
fit: BoxFit.fill,
),
),
child: Image.memory(image!),
);
} else if (name != null && name!.isNotEmpty) {
} else if (firstName != null || lastName != null) {
return Container(
width: style.width,
height: style.height,
decoration: BoxDecoration(
color: _generateColorWithIntials(name!),
color: _generateColorWithIntials(firstName, lastName),
shape: BoxShape.circle,
),
child: Center(
child: Text(
_getInitials(name!),
style: const TextStyle(fontSize: 40),
_getInitials(firstName, lastName),
),
),
);
@ -67,15 +76,13 @@ class Avatar extends StatelessWidget {
}
}
String _getInitials(String name) {
var nameList = name.split(' ');
return nameList.first[0] + nameList.last[0];
String _getInitials(String? firstName, String? lastName) {
return (firstName?[0] ?? '') + (lastName?[0] ?? '');
}
Color _generateColorWithIntials(String name) {
var nameList = name.split(' ');
var uniqueInitialId = nameList.first.toLowerCase().codeUnitAt(0) +
nameList.last.toLowerCase().codeUnitAt(0);
Color _generateColorWithIntials(String? firstName, String? lastName) {
var uniqueInitialId = (firstName?.toLowerCase().codeUnitAt(0) ?? 0) +
(lastName?.toLowerCase().codeUnitAt(0) ?? 0);
return Colors.primaries[uniqueInitialId % Colors.primaries.length];
}

View file

@ -8,19 +8,42 @@ class ItemBuilder {
final ItemBuilderOptions options;
Widget build(dynamic value, Widget? widget, Function(String) updateItem) {
Widget build(
String key, dynamic value, Widget? widget, Function(String) updateItem) {
if (widget == null) {
var controller = TextEditingController(
text: '$value',
text: '${value ?? ''}',
);
return TextField(
late InputDecoration inputDecoration;
if (options.inputDecorationField != null &&
options.inputDecorationField![key] != null) {
inputDecoration = options.inputDecorationField![key]!;
} else {
inputDecoration = options.inputDecoration;
}
final formKey = GlobalKey<FormState>();
return Form(
key: formKey,
child: TextFormField(
controller: controller,
decoration: options.inputDecoration,
decoration: inputDecoration,
readOnly: options.readOnly,
onSubmitted: (s) {
updateItem(s);
onFieldSubmitted: (value) {
if (formKey.currentState!.validate()) {
updateItem(value);
}
},
validator: (value) {
if (options.validators != null &&
options.validators![key] != null) {
return options.validators![key]!(value);
}
return null;
},
),
);
}
return widget;

View file

@ -1,11 +1,15 @@
import 'package:flutter/material.dart';
class ItemBuilderOptions {
const ItemBuilderOptions({
ItemBuilderOptions({
this.inputDecoration = const InputDecoration(),
this.inputDecorationField,
this.readOnly = false,
this.validators,
});
final InputDecoration inputDecoration;
final Map<String, InputDecoration>? inputDecorationField;
final bool readOnly;
final Map<String, String? Function(String?)>? validators;
}

View file

@ -1,9 +1,6 @@
import 'package:flutter/material.dart';
import 'package:profile/profile.dart';
import 'package:profile/src/widgets/avatar/avatar.dart';
import 'package:profile/src/widgets/item_builder/item_builder_options.dart';
import 'package:profile/src/widgets/profile/profile_style.dart';
class ProfilePage extends StatefulWidget {
const ProfilePage({
@ -81,52 +78,50 @@ class ProfileWrapper extends StatefulWidget {
}
class _ProfileWrapperState extends State<ProfileWrapper> {
late List<Widget> profileItems;
List<Widget> defaultItems = [];
@override
void initState() {
super.initState();
profileItems = widget.user.profileData!.buildItems(
widget.user.profileData!.toMap(),
widget.user.profileData!.mapWidget(() {
widget.rebuild();
}),
widget.style.betweenDefaultItemPadding,
(key, value) {
widget.service.editProfile(widget.user, key, value);
},
itemBuilder: widget.itemBuilder,
itemBuilderOptions: widget.itemBuilderOptions,
);
if (widget.itemBuilder == null) {
ItemBuilder builder = ItemBuilder(
options: widget.itemBuilderOptions ?? const ItemBuilderOptions(),
options: widget.itemBuilderOptions ?? ItemBuilderOptions(),
);
defaultItems.add(builder.build(widget.user.firstName, null, (v) {
defaultItems
.add(builder.build('firstName', widget.user.firstName, null, (v) {
widget.user.firstName = v;
widget.service.editProfile(widget.user, 'firstName', v);
}));
defaultItems.add(
SizedBox(
height: widget.style.betweenDefaultItemPadding,
),
);
defaultItems.add(builder.build(widget.user.lastName, null, (v) {
defaultItems
.add(builder.build('lastName', widget.user.lastName, null, (v) {
widget.user.lastName = v;
widget.service.editProfile(widget.user, 'lastName', v);
}));
} else {
defaultItems
.add(widget.itemBuilder!.build(widget.user.firstName, null, (v) {
defaultItems.add(widget.itemBuilder!
.build('firstName', widget.user.firstName, null, (v) {
widget.user.firstName = v;
widget.service.editProfile(widget.user, 'firstname', v);
}));
defaultItems.add(
SizedBox(
height: widget.style.betweenDefaultItemPadding,
),
);
defaultItems
.add(widget.itemBuilder!.build(widget.user.lastName, null, (v) {
defaultItems.add(widget.itemBuilder!
.build('lastName', widget.user.lastName, null, (v) {
widget.user.lastName = v;
widget.service.editProfile(widget.user, 'lastName', v);
}));
}
}
@ -134,17 +129,19 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Padding(
padding: widget.style.pagePadding,
child: Column(
children: [
if (widget.showAvatar)
InkWell(
onTap: () {
widget.service.uploadImage();
onTap: () async {
await widget.service.uploadImage(context);
},
child: Avatar(
name: '${widget.user.firstName} ${widget.user.lastName}',
firstName: widget.user.firstName,
lastName: widget.user.lastName,
style: widget.style.avatarStyle,
avatar: widget.customAvatar,
image: widget.user.image,
@ -155,11 +152,26 @@ class _ProfileWrapperState extends State<ProfileWrapper> {
height: widget.style.betweenDefaultItemPadding,
),
...defaultItems,
...profileItems,
...widget.user.profileData!.buildItems(
widget.user.profileData!.toMap(),
widget.user.profileData!.mapWidget(
() {
widget.rebuild();
},
context,
),
widget.style.betweenDefaultItemPadding,
(key, value) {
widget.service.editProfile(widget.user, key, value);
},
itemBuilder: widget.itemBuilder,
itemBuilderOptions: widget.itemBuilderOptions,
),
if (widget.showDeleteProfile)
SizedBox(
height: widget.style.betweenDefaultItemPadding,
),
const Spacer(),
if (widget.showDeleteProfile)
InkWell(
onTap: () {