Fix: Display of ProfilePicture for Web

master
Rafael 2024-06-02 18:58:43 +02:00
parent cad532da0b
commit d121aa3c77
5 changed files with 69 additions and 55 deletions

View File

@ -2,9 +2,15 @@ import 'package:flutter/material.dart';
class UserTile extends StatelessWidget { class UserTile extends StatelessWidget {
final String text; final String text;
final String? profileImageUrl;
final void Function()? onTap; final void Function()? onTap;
const UserTile({super.key, required this.text, required this.onTap}); const UserTile({
super.key,
required this.text,
this.profileImageUrl,
required this.onTap,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -19,7 +25,14 @@ class UserTile extends StatelessWidget {
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Row( child: Row(
children: [ children: [
// icon // Profile image
if (profileImageUrl != null && profileImageUrl!.isNotEmpty)
CircleAvatar(
backgroundImage: NetworkImage(profileImageUrl!),
radius: 24,
),
// Icon if profile image is not set
if (profileImageUrl == null || profileImageUrl!.isEmpty)
const Icon(Icons.person), const Icon(Icons.person),
const SizedBox(width: 20), const SizedBox(width: 20),

View File

@ -70,6 +70,7 @@ class ConversationsPage extends StatelessWidget {
Map<String, dynamic> userData, BuildContext context) { Map<String, dynamic> userData, BuildContext context) {
return UserTile( return UserTile(
text: userData[Constants.dbFieldUsersEmail], text: userData[Constants.dbFieldUsersEmail],
profileImageUrl: userData[Constants.dbFieldUsersProfilePic],
onTap: () { onTap: () {
// tapped on a user -> go to chat page // tapped on a user -> go to chat page
Navigator.push( Navigator.push(

View File

@ -6,10 +6,10 @@ import 'package:firebase_storage/firebase_storage.dart';
import 'package:image_cropper/image_cropper.dart'; import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'dart:io' as io; import 'dart:io' as io;
import 'dart:typed_data';
import '../constants.dart'; import '../constants.dart';
import '../models/user_profile.dart'; import '../models/user_profile.dart';
import '../utils/helper.dart';
class EditProfilePage extends StatefulWidget { class EditProfilePage extends StatefulWidget {
final UserProfile userData; final UserProfile userData;
@ -25,7 +25,8 @@ class EditProfilePageState extends State<EditProfilePage> {
late TextEditingController _nameController; late TextEditingController _nameController;
late TextEditingController _bioController; late TextEditingController _bioController;
String? profileImageUrl; String? profileImageUrl;
io.File? _profileImage; io.File? _profileImage; // for android/ios
Uint8List? _webProfileImage; // for web
@override @override
void initState() { void initState() {
@ -38,12 +39,6 @@ class EditProfilePageState extends State<EditProfilePage> {
} }
Future<void> _pickImage() async { Future<void> _pickImage() async {
if (kIsWeb) {
showMsg(context, 'Limitation of the web version',
'Due to limitations of the component used in the web version, setting a profile picture is currently available only on Android and iOS.');
return;
}
final pickedFile = final pickedFile =
await ImagePicker().pickImage(source: ImageSource.gallery); await ImagePicker().pickImage(source: ImageSource.gallery);
@ -57,7 +52,7 @@ class EditProfilePageState extends State<EditProfilePage> {
CropAspectRatioPreset.ratio3x2, CropAspectRatioPreset.ratio3x2,
CropAspectRatioPreset.original, CropAspectRatioPreset.original,
CropAspectRatioPreset.ratio4x3, CropAspectRatioPreset.ratio4x3,
CropAspectRatioPreset.ratio16x9 CropAspectRatioPreset.ratio16x9,
], ],
uiSettings: [ uiSettings: [
AndroidUiSettings( AndroidUiSettings(
@ -71,33 +66,43 @@ class EditProfilePageState extends State<EditProfilePage> {
title: 'Cropper', title: 'Cropper',
minimumAspectRatio: 1.0, minimumAspectRatio: 1.0,
), ),
/* WebUiSettings( if (kIsWeb)
WebUiSettings(
context: context, context: context,
presentStyle: CropperPresentStyle.page, presentStyle: CropperPresentStyle.page,
boundary: const CroppieBoundary( boundary: const CroppieBoundary(
width: 400, width: 400,
height: 400, height: 400,
), ),
viewPort: viewPort: const CroppieViewPort(
const CroppieViewPort(width: 360, height: 360, type: 'circle'), width: 360, height: 360, type: 'circle'),
enableExif: true, enableExif: true,
enableZoom: true, enableZoom: true,
showZoomer: true, showZoomer: true,
),*/ ),
], ],
); );
if (croppedFile != null) { if (croppedFile != null) {
if (kIsWeb) {
// web specific
Uint8List webProfileImage = await croppedFile.readAsBytes();
setState(() {
_webProfileImage = webProfileImage;
});
} else {
setState(() { setState(() {
_profileImage = io.File(croppedFile.path); // convert cropped file _profileImage = io.File(croppedFile.path); // convert cropped file
}); });
} }
} }
} }
}
void _clearProfileImage() { void _clearProfileImage() {
setState(() { setState(() {
_profileImage = null; _profileImage = null;
_webProfileImage = null; // web only
profileImageUrl = null; profileImageUrl = null;
widget.userData.profilePictureUrl = null; widget.userData.profilePictureUrl = null;
}); });
@ -110,13 +115,16 @@ class EditProfilePageState extends State<EditProfilePage> {
if (_formKey.currentState!.validate()) { if (_formKey.currentState!.validate()) {
String uid = FirebaseAuth.instance.currentUser!.uid; String uid = FirebaseAuth.instance.currentUser!.uid;
if (_profileImage != null) { if (_profileImage != null || _webProfileImage != null) {
final storageRef = FirebaseStorage.instance final storageRef = FirebaseStorage.instance
.ref() .ref()
.child(Constants.dbStoragePathProfiles) .child(Constants.dbStoragePathProfiles)
.child(uid); // filename = userid .child(uid); // filename = userid
if (!kIsWeb) { if (kIsWeb && _webProfileImage != null) {
await storageRef.putData(_webProfileImage!);
profileImageUrl = await storageRef.getDownloadURL();
} else if (_profileImage != null) {
await storageRef.putFile(_profileImage!); await storageRef.putFile(_profileImage!);
profileImageUrl = await storageRef.getDownloadURL(); profileImageUrl = await storageRef.getDownloadURL();
} }
@ -165,6 +173,7 @@ class EditProfilePageState extends State<EditProfilePage> {
children: [ children: [
buildAvatar(context), buildAvatar(context),
if (_profileImage != null || if (_profileImage != null ||
_webProfileImage != null ||
widget.userData.profilePictureUrl != null) widget.userData.profilePictureUrl != null)
IconButton( IconButton(
icon: Ink( icon: Ink(
@ -221,29 +230,20 @@ class EditProfilePageState extends State<EditProfilePage> {
radius: 50, radius: 50,
backgroundImage: _profileImage != null backgroundImage: _profileImage != null
? FileImage(_profileImage!) as ImageProvider ? FileImage(_profileImage!) as ImageProvider
: ((widget.userData.profilePictureUrl != null && : _webProfileImage != null
widget.userData.profilePictureUrl!.isNotEmpty) ? MemoryImage(_webProfileImage!)
as ImageProvider // web specific
: (widget.userData.profilePictureUrl != null &&
widget.userData.profilePictureUrl!.isNotEmpty
? NetworkImage(widget.userData.profilePictureUrl!) ? NetworkImage(widget.userData.profilePictureUrl!)
as ImageProvider as ImageProvider
: null), : null),
child: ClipOval( child: ClipOval(
child: _profileImage == null && child: _profileImage == null &&
_webProfileImage == null &&
widget.userData.profilePictureUrl == null widget.userData.profilePictureUrl == null
? const Icon(Icons.person, size: 50) ? const Icon(Icons.person, size: 50)
: SizedBox( : null,
width: 100,
height: 100,
child: _profileImage != null
? (kIsWeb
? Image.network(_profileImage!.path)
: Image.file(_profileImage!, fit: BoxFit.cover))
: ((widget.userData.profilePictureUrl != null &&
widget.userData.profilePictureUrl!.isNotEmpty)
? Image.network(
widget.userData.profilePictureUrl!,
fit: BoxFit.cover)
: null),
),
), ),
), ),
Positioned( Positioned(

View File

@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
@ -610,13 +609,13 @@ class _UserProfilePageState extends State<UserProfilePage> {
), ),
CircleAvatar( CircleAvatar(
radius: 50, radius: 50,
backgroundImage: (!kIsWeb && backgroundImage:
(profileImageUrl != null && profileImageUrl!.isNotEmpty)) ((profileImageUrl != null && profileImageUrl!.isNotEmpty))
? NetworkImage(profileImageUrl!) ? NetworkImage(profileImageUrl!)
: null, : null,
child: (profileImageUrl == null || profileImageUrl!.isEmpty) child: (profileImageUrl == null || profileImageUrl!.isEmpty)
? const Icon(Icons.person, size: 50) ? const Icon(Icons.person, size: 50)
: (kIsWeb ? const Icon(Icons.broken_image, size: 50) : null), : null,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text(myData.name, style: const TextStyle(fontSize: 24)), Text(myData.name, style: const TextStyle(fontSize: 24)),

View File

@ -53,7 +53,8 @@
serviceWorkerVersion: serviceWorkerVersion, serviceWorkerVersion: serviceWorkerVersion,
}, },
onEntrypointLoaded: function(engineInitializer) { onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) { let config = { renderer: 'html' };
engineInitializer.initializeEngine(config).then(function(appRunner) {
appRunner.runApp(); appRunner.runApp();
}); });
} }