dartlangeditorflutterflutter-appsflutter-examplesflutter-packageflutter-widgetquillquill-deltaquilljsreactquillrich-textrich-text-editorwysiwygwysiwyg-editor
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
5.5 KiB
194 lines
5.5 KiB
1 year ago
|
import 'dart:convert' show base64;
|
||
2 years ago
|
import 'dart:io' show File;
|
||
3 years ago
|
|
||
1 year ago
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||
4 years ago
|
import 'package:flutter/material.dart';
|
||
3 years ago
|
import 'package:flutter_quill/flutter_quill.dart';
|
||
4 years ago
|
import 'package:photo_view/photo_view.dart';
|
||
3 years ago
|
|
||
1 year ago
|
import '../../models/config/image/editor/image_configurations.dart';
|
||
1 year ago
|
import '../../utils/utils.dart';
|
||
1 year ago
|
import '../image/editor/image_embed_types.dart';
|
||
3 years ago
|
|
||
3 years ago
|
const List<String> imageFileExtensions = [
|
||
|
'.jpeg',
|
||
|
'.png',
|
||
|
'.jpg',
|
||
|
'.gif',
|
||
|
'.webp',
|
||
|
'.tif',
|
||
|
'.heic'
|
||
|
];
|
||
|
|
||
3 years ago
|
String getImageStyleString(QuillController controller) {
|
||
|
final String? s = controller
|
||
|
.getAllSelectionStyles()
|
||
|
.firstWhere((s) => s.attributes.containsKey(Attribute.style.key),
|
||
2 years ago
|
orElse: Style.new)
|
||
3 years ago
|
.attributes[Attribute.style.key]
|
||
|
?.value;
|
||
|
return s ?? '';
|
||
|
}
|
||
|
|
||
1 year ago
|
/// [imageProviderBuilder] To override the return value pass value to it
|
||
|
/// [imageSource] The source of the image in the quill delta json document
|
||
|
/// It could be http, file, network, asset, or base 64 image
|
||
|
ImageProvider getImageProviderByImageSource(
|
||
|
String imageSource, {
|
||
|
required ImageEmbedBuilderProviderBuilder? imageProviderBuilder,
|
||
1 year ago
|
required String assetsPrefix,
|
||
1 year ago
|
required BuildContext context,
|
||
1 year ago
|
}) {
|
||
|
if (imageProviderBuilder != null) {
|
||
1 year ago
|
return imageProviderBuilder(context, imageSource);
|
||
1 year ago
|
}
|
||
|
|
||
|
if (isImageBase64(imageSource)) {
|
||
|
return MemoryImage(base64.decode(imageSource));
|
||
|
}
|
||
|
|
||
|
if (isHttpBasedUrl(imageSource)) {
|
||
|
return NetworkImage(imageSource);
|
||
|
}
|
||
1 year ago
|
|
||
|
if (imageSource.startsWith(assetsPrefix)) {
|
||
1 year ago
|
return AssetImage(imageSource);
|
||
|
}
|
||
1 year ago
|
|
||
|
// File image
|
||
|
if (kIsWeb) {
|
||
|
return NetworkImage(imageSource);
|
||
|
}
|
||
1 year ago
|
return FileImage(File(imageSource));
|
||
|
}
|
||
|
|
||
1 year ago
|
Image getImageWidgetByImageSource(
|
||
1 year ago
|
String imageSource, {
|
||
1 year ago
|
required BuildContext context,
|
||
2 years ago
|
required ImageEmbedBuilderProviderBuilder? imageProviderBuilder,
|
||
|
required ImageErrorWidgetBuilder? imageErrorWidgetBuilder,
|
||
1 year ago
|
required String assetsPrefix,
|
||
2 years ago
|
double? width,
|
||
|
double? height,
|
||
|
AlignmentGeometry alignment = Alignment.center,
|
||
|
}) {
|
||
1 year ago
|
return Image(
|
||
|
image: getImageProviderByImageSource(
|
||
1 year ago
|
context: context,
|
||
1 year ago
|
imageSource,
|
||
1 year ago
|
imageProviderBuilder: imageProviderBuilder,
|
||
1 year ago
|
assetsPrefix: assetsPrefix,
|
||
1 year ago
|
),
|
||
2 years ago
|
width: width,
|
||
|
height: height,
|
||
|
alignment: alignment,
|
||
|
errorBuilder: imageErrorWidgetBuilder,
|
||
|
);
|
||
3 years ago
|
}
|
||
|
|
||
|
String standardizeImageUrl(String url) {
|
||
|
if (url.contains('base64')) {
|
||
|
return url.split(',')[1];
|
||
|
}
|
||
|
return url;
|
||
3 years ago
|
}
|
||
|
|
||
|
/// This is a bug of Gallery Saver Package.
|
||
|
/// It can not save image that's filename does not end with it's file extension
|
||
|
/// like below.
|
||
|
// "https://firebasestorage.googleapis.com/v0/b/eventat-4ba96.appspot.com/o/2019-Metrology-Events.jpg?alt=media&token=bfc47032-5173-4b3f-86bb-9659f46b362a"
|
||
|
/// If imageUrl does not end with it's file extension,
|
||
|
/// file extension is added to image url for saving.
|
||
|
String appendFileExtensionToImageUrl(String url) {
|
||
|
final endsWithImageFileExtension = imageFileExtensions
|
||
|
.firstWhere((s) => url.toLowerCase().endsWith(s), orElse: () => '');
|
||
|
if (endsWithImageFileExtension.isNotEmpty) {
|
||
|
return url;
|
||
|
}
|
||
|
|
||
|
final imageFileExtension = imageFileExtensions
|
||
|
.firstWhere((s) => url.toLowerCase().contains(s), orElse: () => '');
|
||
|
|
||
|
return url + imageFileExtension;
|
||
3 years ago
|
}
|
||
4 years ago
|
|
||
|
class ImageTapWrapper extends StatelessWidget {
|
||
|
const ImageTapWrapper({
|
||
3 years ago
|
required this.imageUrl,
|
||
1 year ago
|
required this.configurations,
|
||
1 year ago
|
required this.assetsPrefix,
|
||
1 year ago
|
super.key,
|
||
4 years ago
|
});
|
||
|
|
||
3 years ago
|
final String imageUrl;
|
||
1 year ago
|
final QuillEditorImageEmbedConfigurations configurations;
|
||
1 year ago
|
final String assetsPrefix;
|
||
3 years ago
|
|
||
3 years ago
|
@override
|
||
4 years ago
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
body: Container(
|
||
|
constraints: BoxConstraints.expand(
|
||
2 years ago
|
height: MediaQuery.sizeOf(context).height,
|
||
4 years ago
|
),
|
||
3 years ago
|
child: Stack(
|
||
|
children: [
|
||
|
PhotoView(
|
||
1 year ago
|
imageProvider: getImageProviderByImageSource(
|
||
1 year ago
|
context: context,
|
||
2 years ago
|
imageUrl,
|
||
1 year ago
|
imageProviderBuilder: configurations.imageProviderBuilder,
|
||
1 year ago
|
assetsPrefix: assetsPrefix,
|
||
2 years ago
|
),
|
||
1 year ago
|
errorBuilder: configurations.imageErrorWidgetBuilder,
|
||
3 years ago
|
loadingBuilder: (context, event) {
|
||
|
return Container(
|
||
|
color: Colors.black,
|
||
3 years ago
|
child: const Center(
|
||
3 years ago
|
child: CircularProgressIndicator(),
|
||
|
),
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
Positioned(
|
||
3 years ago
|
right: 10,
|
||
2 years ago
|
top: MediaQuery.paddingOf(context).top + 10.0,
|
||
3 years ago
|
child: InkWell(
|
||
|
onTap: () {
|
||
|
Navigator.pop(context);
|
||
|
},
|
||
|
child: Stack(
|
||
|
children: [
|
||
|
Opacity(
|
||
|
opacity: 0.2,
|
||
|
child: Container(
|
||
3 years ago
|
height: 30,
|
||
|
width: 30,
|
||
|
decoration: const BoxDecoration(
|
||
3 years ago
|
shape: BoxShape.circle,
|
||
|
color: Colors.black87,
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
Positioned(
|
||
|
top: 0,
|
||
|
bottom: 0,
|
||
|
left: 0,
|
||
|
right: 0,
|
||
1 year ago
|
child: Icon(
|
||
|
Icons.close,
|
||
|
color: Colors.grey[400],
|
||
|
size: 28,
|
||
|
),
|
||
3 years ago
|
)
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
],
|
||
4 years ago
|
),
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|