import 'dart:convert'; import 'dart:io' show File; import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:photo_view/photo_view.dart'; import '../../models/config/editor/image/image.dart'; import '../../utils/utils.dart'; import '../image/editor/image_embed_types.dart'; const List imageFileExtensions = [ '.jpeg', '.png', '.jpg', '.gif', '.webp', '.tif', '.heic' ]; String getImageStyleString(QuillController controller) { final String? s = controller .getAllSelectionStyles() .firstWhere((s) => s.attributes.containsKey(Attribute.style.key), orElse: Style.new) .attributes[Attribute.style.key] ?.value; return s ?? ''; } /// [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, required String assetsPrefix, required BuildContext context, }) { if (imageProviderBuilder != null) { return imageProviderBuilder(context, imageSource); } if (isImageBase64(imageSource)) { return MemoryImage(base64.decode(imageSource)); } if (isHttpBasedUrl(imageSource)) { return NetworkImage(imageSource); } if (imageSource.startsWith(assetsPrefix)) { return AssetImage(imageSource); } return FileImage(File(imageSource)); } Image getImageWidgetByImageSource( String imageSource, { required BuildContext context, required ImageEmbedBuilderProviderBuilder? imageProviderBuilder, required ImageErrorWidgetBuilder? imageErrorWidgetBuilder, required String assetsPrefix, double? width, double? height, AlignmentGeometry alignment = Alignment.center, }) { return Image( image: getImageProviderByImageSource( context: context, imageSource, imageProviderBuilder: imageProviderBuilder, assetsPrefix: assetsPrefix, ), width: width, height: height, alignment: alignment, errorBuilder: imageErrorWidgetBuilder, ); } String standardizeImageUrl(String url) { if (url.contains('base64')) { return url.split(',')[1]; } return url; } /// 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; } class ImageTapWrapper extends StatelessWidget { const ImageTapWrapper({ required this.imageUrl, required this.configurations, required this.assetsPrefix, super.key, }); final String imageUrl; final QuillEditorImageEmbedConfigurations configurations; final String assetsPrefix; @override Widget build(BuildContext context) { return Scaffold( body: Container( constraints: BoxConstraints.expand( height: MediaQuery.sizeOf(context).height, ), child: Stack( children: [ PhotoView( imageProvider: getImageProviderByImageSource( context: context, imageUrl, imageProviderBuilder: configurations.imageProviderBuilder, assetsPrefix: assetsPrefix, ), errorBuilder: configurations.imageErrorWidgetBuilder, loadingBuilder: (context, event) { return Container( color: Colors.black, child: const Center( child: CircularProgressIndicator(), ), ); }, ), Positioned( right: 10, top: MediaQuery.paddingOf(context).top + 10.0, child: InkWell( onTap: () { Navigator.pop(context); }, child: Stack( children: [ Opacity( opacity: 0.2, child: Container( height: 30, width: 30, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.black87, ), ), ), Positioned( top: 0, bottom: 0, left: 0, right: 0, child: Icon( Icons.close, color: Colors.grey[400], size: 28, ), ) ], ), ), ), ], ), ), ); } }