✨ Crop image for profile picture, background
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:croppy/croppy.dart';
|
||||
import 'package:easy_localization/easy_localization.dart' hide TextDirection;
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -100,6 +101,7 @@ class IslandApp extends HookConsumerWidget {
|
||||
supportedLocales: context.supportedLocales,
|
||||
localizationsDelegates: [
|
||||
...context.localizationDelegates,
|
||||
CroppyLocalizations.delegate,
|
||||
], // this contains the cupertino one
|
||||
locale: context.locale,
|
||||
builder: (context, child) {
|
||||
|
@ -29,6 +29,7 @@ class AppRouter extends RootStackRouter {
|
||||
page: EditPublisherRoute.page,
|
||||
path: '/account/me/publishers/:id/edit',
|
||||
),
|
||||
AutoRoute(page: AccountProfileRoute.page, path: '/account/:name'),
|
||||
AutoRoute(page: PostComposeRoute.page, path: '/posts/compose'),
|
||||
AutoRoute(page: PostDetailRoute.page, path: '/posts/:id'),
|
||||
AutoRoute(page: PostEditRoute.page, path: '/posts/:id/edit'),
|
||||
|
@ -31,54 +31,61 @@ class AccountScreen extends HookConsumerWidget {
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Card(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (user.value?.profile.background != null)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 7,
|
||||
child: CloudFileWidget(
|
||||
item: user.value!.profile.background!,
|
||||
fit: BoxFit.cover,
|
||||
GestureDetector(
|
||||
child: Card(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (user.value?.profile.background != null)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 7,
|
||||
child: CloudFileWidget(
|
||||
item: user.value!.profile.background!,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
ProfilePictureWidget(
|
||||
fileId: user.value?.profile.pictureId,
|
||||
radius: 24,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 4,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text(user.value!.nick).bold().fontSize(16),
|
||||
Text('@${user.value!.name}'),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
user.value!.profile.bio ?? 'No description yet.',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 16, vertical: 16),
|
||||
],
|
||||
),
|
||||
).padding(horizontal: 8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 16,
|
||||
children: [
|
||||
ProfilePictureWidget(
|
||||
fileId: user.value?.profile.pictureId,
|
||||
radius: 24,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 4,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Text(user.value!.nick).bold().fontSize(16),
|
||||
Text('@${user.value!.name}'),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
user.value!.profile.bio ?? 'No description yet.',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 16, vertical: 16),
|
||||
],
|
||||
),
|
||||
).padding(horizontal: 8),
|
||||
onTap: () {
|
||||
context.router.push(
|
||||
AccountProfileRoute(name: user.value!.name),
|
||||
);
|
||||
},
|
||||
),
|
||||
const Gap(8),
|
||||
ListTile(
|
||||
minTileHeight: 48,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:croppy/croppy.dart' hide cropImage;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -176,10 +177,23 @@ class EditPublisherScreen extends HookConsumerWidget {
|
||||
final background = useState<SnCloudFile?>(null);
|
||||
|
||||
void setPicture(String position) async {
|
||||
final result = await ref
|
||||
var result = await ref
|
||||
.read(imagePickerProvider)
|
||||
.pickImage(source: ImageSource.gallery);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
result = await cropImage(
|
||||
context,
|
||||
image: result,
|
||||
allowedAspectRatios: [
|
||||
if (position == 'background')
|
||||
CropAspectRatio(height: 7, width: 16)
|
||||
else
|
||||
CropAspectRatio(height: 1, width: 1),
|
||||
],
|
||||
);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:croppy/croppy.dart' hide cropImage;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
@ -25,10 +26,23 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
||||
final submitting = useState(false);
|
||||
|
||||
void updateProfilePicture(String position) async {
|
||||
final result = await ref
|
||||
var result = await ref
|
||||
.read(imagePickerProvider)
|
||||
.pickImage(source: ImageSource.gallery);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
result = await cropImage(
|
||||
context,
|
||||
image: result,
|
||||
allowedAspectRatios: [
|
||||
if (position == 'background')
|
||||
CropAspectRatio(height: 7, width: 16)
|
||||
else
|
||||
CropAspectRatio(height: 1, width: 1),
|
||||
],
|
||||
);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:croppy/croppy.dart' hide cropImage;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -244,10 +245,23 @@ class EditChatScreen extends HookConsumerWidget {
|
||||
}, [chat]);
|
||||
|
||||
void setPicture(String position) async {
|
||||
final result = await ref
|
||||
var result = await ref
|
||||
.read(imagePickerProvider)
|
||||
.pickImage(source: ImageSource.gallery);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
result = await cropImage(
|
||||
context,
|
||||
image: result,
|
||||
allowedAspectRatios: [
|
||||
if (position == 'background')
|
||||
CropAspectRatio(height: 7, width: 16)
|
||||
else
|
||||
CropAspectRatio(height: 1, width: 1),
|
||||
],
|
||||
);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:croppy/croppy.dart' show CropAspectRatio;
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -158,10 +159,23 @@ class EditRealmScreen extends HookConsumerWidget {
|
||||
}, [realm]);
|
||||
|
||||
void setPicture(String position) async {
|
||||
final result = await ref
|
||||
var result = await ref
|
||||
.read(imagePickerProvider)
|
||||
.pickImage(source: ImageSource.gallery);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
result = await cropImage(
|
||||
context,
|
||||
image: result,
|
||||
allowedAspectRatios: [
|
||||
if (position == 'background')
|
||||
CropAspectRatio(height: 7, width: 16)
|
||||
else
|
||||
CropAspectRatio(height: 1, width: 1),
|
||||
],
|
||||
);
|
||||
if (result == null) return;
|
||||
if (!context.mounted) return;
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
|
@ -1,11 +1,43 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:croppy/croppy.dart';
|
||||
import 'package:cross_file/cross_file.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
import 'package:tus_client_dart/tus_client_dart.dart';
|
||||
|
||||
Future<XFile?> cropImage(
|
||||
BuildContext context, {
|
||||
required XFile image,
|
||||
List<CropAspectRatio?>? allowedAspectRatios,
|
||||
}) async {
|
||||
final result = await showMaterialImageCropper(
|
||||
context,
|
||||
imageProvider:
|
||||
kIsWeb ? NetworkImage(image.path) : FileImage(File(image.path)),
|
||||
showLoadingIndicatorOnSubmit: true,
|
||||
allowedAspectRatios: allowedAspectRatios,
|
||||
);
|
||||
if (result == null) return null; // Cancelled operation
|
||||
final croppedFile = result.uiImage;
|
||||
final croppedBytes = await croppedFile.toByteData(
|
||||
format: ImageByteFormat.png,
|
||||
);
|
||||
if (croppedBytes == null) {
|
||||
return image;
|
||||
}
|
||||
croppedFile.dispose();
|
||||
return XFile.fromData(
|
||||
croppedBytes.buffer.asUint8List(),
|
||||
path: image.path,
|
||||
mimeType: image.mimeType,
|
||||
);
|
||||
}
|
||||
|
||||
Completer<SnCloudFile?> putMediaToCloud({
|
||||
required dynamic fileData, // Can be XFile or List<int> (Uint8List)
|
||||
required String atk,
|
||||
|
Reference in New Issue
Block a user