Fix: Display of ProfilePicture for Web
parent
cad532da0b
commit
d121aa3c77
|
@ -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),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue