cofounderella/lib/pages/edit_profile_page.dart

268 lines
8.4 KiB
Dart
Raw Normal View History

2024-06-02 01:07:39 +02:00
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
2024-06-02 01:07:39 +02:00
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
2024-06-02 01:07:39 +02:00
import 'dart:io' as io;
2024-06-02 18:58:43 +02:00
import 'dart:typed_data';
import '../constants.dart';
2024-05-30 16:37:34 +02:00
import '../models/user_profile.dart';
class EditProfilePage extends StatefulWidget {
2024-05-30 16:37:34 +02:00
final UserProfile userData;
const EditProfilePage({super.key, required this.userData});
@override
EditProfilePageState createState() => EditProfilePageState();
}
class EditProfilePageState extends State<EditProfilePage> {
final _formKey = GlobalKey<FormState>();
late TextEditingController _nameController;
late TextEditingController _bioController;
2024-06-02 01:07:39 +02:00
String? profileImageUrl;
2024-06-02 18:58:43 +02:00
io.File? _profileImage; // for android/ios
Uint8List? _webProfileImage; // for web
@override
void initState() {
super.initState();
2024-05-30 16:37:34 +02:00
_nameController = TextEditingController(text: widget.userData.name);
_bioController = TextEditingController(text: widget.userData.bio);
if (widget.userData.profilePictureUrl != null) {
profileImageUrl = widget.userData.profilePictureUrl;
}
}
Future<void> _pickImage() async {
final pickedFile =
await ImagePicker().pickImage(source: ImageSource.gallery);
2024-06-02 01:07:39 +02:00
if (pickedFile != null) {
2024-06-02 01:07:39 +02:00
CroppedFile? croppedFile = await ImageCropper().cropImage(
sourcePath: pickedFile.path,
aspectRatioPresets: [
CropAspectRatioPreset.square,
CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.original,
CropAspectRatioPreset.ratio4x3,
2024-06-02 18:58:43 +02:00
CropAspectRatioPreset.ratio16x9,
2024-06-02 01:07:39 +02:00
],
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Cropper',
toolbarColor: Colors.deepOrange,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
IOSUiSettings(
title: 'Cropper',
minimumAspectRatio: 1.0,
),
2024-06-07 02:18:20 +02:00
if (kIsWeb && mounted)
2024-06-02 18:58:43 +02:00
WebUiSettings(
context: context,
presentStyle: CropperPresentStyle.page,
boundary: const CroppieBoundary(
width: 400,
height: 400,
),
viewPort: const CroppieViewPort(
2024-06-17 20:39:56 +02:00
width: 360,
height: 360,
type: 'circle',
),
2024-06-02 18:58:43 +02:00
enableExif: true,
enableZoom: true,
showZoomer: true,
2024-06-02 01:07:39 +02:00
),
],
);
if (croppedFile != null) {
2024-06-02 18:58:43 +02:00
if (kIsWeb) {
// web specific
Uint8List webProfileImage = await croppedFile.readAsBytes();
setState(() {
_webProfileImage = webProfileImage;
});
} else {
setState(() {
_profileImage = io.File(croppedFile.path); // convert cropped file
});
}
2024-06-02 01:07:39 +02:00
}
}
}
void _clearProfileImage() {
setState(() {
_profileImage = null;
2024-06-02 18:58:43 +02:00
_webProfileImage = null; // web only
2024-05-30 16:37:34 +02:00
profileImageUrl = null;
widget.userData.profilePictureUrl = null;
});
}
Future<void> _saveProfile() async {
2024-05-30 16:37:34 +02:00
String nameTrim = _nameController.text.trim();
String bioTrim = _bioController.text.trim();
if (_formKey.currentState!.validate()) {
String uid = FirebaseAuth.instance.currentUser!.uid;
2024-06-02 18:58:43 +02:00
if (_profileImage != null || _webProfileImage != null) {
final storageRef = FirebaseStorage.instance
.ref()
.child(Constants.dbStoragePathProfiles)
.child(uid); // filename = userid
2024-06-02 01:07:39 +02:00
2024-06-02 18:58:43 +02:00
if (kIsWeb && _webProfileImage != null) {
await storageRef.putData(_webProfileImage!);
profileImageUrl = await storageRef.getDownloadURL();
} else if (_profileImage != null) {
2024-06-02 01:07:39 +02:00
await storageRef.putFile(_profileImage!);
profileImageUrl = await storageRef.getDownloadURL();
}
}
2024-05-30 16:37:34 +02:00
Map<String, dynamic> resultValues = {
Constants.dbFieldUsersName: nameTrim,
Constants.dbFieldUsersBio: bioTrim,
Constants.dbFieldUsersProfilePic: profileImageUrl,
};
await FirebaseFirestore.instance
.collection(Constants.dbCollectionUsers)
.doc(uid)
2024-05-30 16:37:34 +02:00
.update(resultValues);
2024-05-30 16:37:34 +02:00
_close(resultValues);
}
}
2024-05-30 16:37:34 +02:00
/// close this page and return selected values
void _close(Map<String, dynamic> map) {
Navigator.pop(context, {
Constants.dbFieldUsersProfilePic: map[Constants.dbFieldUsersProfilePic],
Constants.dbFieldUsersName: map[Constants.dbFieldUsersName],
Constants.dbFieldUsersBio: map[Constants.dbFieldUsersBio],
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Edit Profile'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: ListView(
children: [
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
2024-05-30 16:37:34 +02:00
buildAvatar(context),
if (_profileImage != null ||
2024-06-02 18:58:43 +02:00
_webProfileImage != null ||
2024-05-30 16:37:34 +02:00
widget.userData.profilePictureUrl != null)
IconButton(
icon: Ink(
decoration: const ShapeDecoration(
shape: CircleBorder(),
),
child: const Icon(
Icons.delete,
color: Colors.red,
size: 32,
),
),
onPressed: _clearProfileImage,
),
],
),
),
const SizedBox(height: 16),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Name'),
validator: (value) {
2024-05-30 16:37:34 +02:00
if (value == null || value.trim().isEmpty) {
return 'Please enter a name';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _bioController,
decoration: const InputDecoration(labelText: 'Bio'),
maxLines: 3,
2024-05-30 16:37:34 +02:00
maxLength: 4096,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _saveProfile,
child: const Text('Save'),
),
],
),
),
),
);
}
2024-05-30 16:37:34 +02:00
Widget buildAvatar(BuildContext context) {
return GestureDetector(
onTap: _pickImage,
child: Stack(
children: [
CircleAvatar(
2024-06-07 02:18:20 +02:00
radius: 80,
2024-05-30 16:37:34 +02:00
backgroundImage: _profileImage != null
2024-06-03 16:35:33 +02:00
? FileImage(_profileImage!)
: (_webProfileImage != null
2024-06-02 18:58:43 +02:00
? MemoryImage(_webProfileImage!)
: (widget.userData.profilePictureUrl != null &&
widget.userData.profilePictureUrl!.isNotEmpty
? NetworkImage(widget.userData.profilePictureUrl!)
2024-06-03 16:35:33 +02:00
: null as ImageProvider<Object>?)),
2024-05-30 16:37:34 +02:00
child: ClipOval(
child: _profileImage == null &&
2024-06-02 18:58:43 +02:00
_webProfileImage == null &&
2024-05-30 16:37:34 +02:00
widget.userData.profilePictureUrl == null
2024-06-07 02:18:20 +02:00
? const Icon(Icons.person, size: 80)
2024-06-02 18:58:43 +02:00
: null,
2024-05-30 16:37:34 +02:00
),
),
Positioned(
bottom: 0,
right: 0,
child: IconButton(
icon: Ink(
padding: const EdgeInsets.all(4),
2024-06-02 01:07:39 +02:00
decoration: ShapeDecoration(
color: Theme.of(context).colorScheme.surfaceContainer,
2024-06-02 01:07:39 +02:00
shape: const CircleBorder(),
),
2024-06-07 02:18:20 +02:00
child: const Icon(Icons.camera_alt),
2024-06-02 01:07:39 +02:00
),
2024-05-30 16:37:34 +02:00
onPressed: _pickImage,
),
),
],
),
);
}
}