Save image option (#593)

* Save image option

* Update android versions

* Refactor code
pull/596/head
X Code 3 years ago committed by GitHub
parent 379f35d71f
commit 565f6316cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      example/android/app/build.gradle
  2. 2
      example/android/build.gradle
  3. 1076
      example/assets/sample_data.json
  4. 33
      lib/src/utils/simple_dialog_item.dart
  5. 68
      lib/src/widgets/editor.dart
  6. 39
      lib/src/widgets/embeds/image.dart
  7. 1
      pubspec.yaml

@ -44,7 +44,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.app"
minSdkVersion 17
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()

File diff suppressed because one or more lines are too long

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
class SimpleDialogItem extends StatelessWidget {
const SimpleDialogItem(
{required this.icon,
required this.color,
required this.text,
required this.onPressed,
Key? key})
: super(key: key);
final IconData icon;
final Color color;
final String text;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return SimpleDialogOption(
onPressed: onPressed,
child: Row(
children: [
Icon(icon, size: 36, color: color),
Padding(
padding: const EdgeInsetsDirectional.only(start: 16),
child:
Text(text, style: const TextStyle(fontWeight: FontWeight.bold)),
),
],
),
);
}
}

@ -1,5 +1,3 @@
import 'dart:convert';
import 'dart:io' as io;
import 'dart:math' as math;
import 'package:flutter/cupertino.dart';
@ -8,13 +6,14 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:string_validator/string_validator.dart';
import 'package:gallery_saver/gallery_saver.dart';
import '../models/documents/document.dart';
import '../models/documents/nodes/container.dart' as container_node;
import '../models/documents/nodes/leaf.dart' as leaf;
import '../models/documents/nodes/line.dart';
import '../utils/platform_helper.dart';
import '../utils/simple_dialog_item.dart';
import '../utils/string_helper.dart';
import 'box.dart';
import 'controller.dart';
@ -187,38 +186,53 @@ Widget defaultEmbedBuilder(
final a = getAlignment(_attrs['mobileAlignment']);
image = Padding(
padding: EdgeInsets.all(m),
child: imageUrl.startsWith('http')
? Image.network(imageUrl, width: w, height: h, alignment: a)
: isBase64(imageUrl)
? Image.memory(base64.decode(imageUrl),
width: w, height: h, alignment: a)
: Image.file(io.File(imageUrl),
width: w, height: h, alignment: a));
child: imageByUrl(imageUrl, width: w, height: h, alignment: a));
}
}
image ??= imageUrl.startsWith('http')
? Image.network(imageUrl)
: isBase64(imageUrl)
? Image.memory(base64.decode(imageUrl))
: Image.file(io.File(imageUrl));
image ??= imageByUrl(imageUrl);
if (!readOnly) {
if (!readOnly || !isMobile() || isImageBase64(imageUrl)) {
return image;
}
/// We provide option menu only for mobile platform excluding base64
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageTapWrapper(
imageProvider: imageUrl.startsWith('http')
? NetworkImage(imageUrl)
: isBase64(imageUrl)
? Image.memory(base64.decode(imageUrl))
as ImageProvider<Object>?
: FileImage(io.File(imageUrl)),
)));
showDialog(
context: context,
builder: (context) => Padding(
padding: const EdgeInsets.fromLTRB(50, 0, 50, 0),
child: SimpleDialog(
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(10))),
children: [
SimpleDialogItem(
icon: Icons.save,
color: Colors.greenAccent,
text: 'Save',
onPressed: () {
// TODO: improve this
GallerySaver.saveImage(imageUrl).then((_) =>
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Saved'))));
},
),
SimpleDialogItem(
icon: Icons.zoom_in,
color: Colors.cyanAccent,
text: 'Zoom',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageTapWrapper(
imageUrl: imageUrl)));
},
)
]),
));
},
child: image);
case 'video':

@ -1,12 +1,45 @@
import 'dart:convert';
import 'dart:io' as io;
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:string_validator/string_validator.dart';
bool isImageBase64(String imageUrl) {
return !imageUrl.startsWith('http') && isBase64(imageUrl);
}
Widget imageByUrl(String imageUrl,
{double? width,
double? height,
AlignmentGeometry alignment = Alignment.center}) {
if (isImageBase64(imageUrl)) {
return Image.memory(base64.decode(imageUrl),
width: width, height: height, alignment: alignment);
}
if (imageUrl.startsWith('http')) {
return Image.network(imageUrl,
width: width, height: height, alignment: alignment);
}
return Image.file(io.File(imageUrl),
width: width, height: height, alignment: alignment);
}
class ImageTapWrapper extends StatelessWidget {
const ImageTapWrapper({
this.imageProvider,
required this.imageUrl,
});
final ImageProvider? imageProvider;
final String imageUrl;
ImageProvider _imageProviderByUrl(String imageUrl) {
if (imageUrl.startsWith('http')) {
return NetworkImage(imageUrl);
}
return FileImage(io.File(imageUrl));
}
@override
Widget build(BuildContext context) {
@ -18,7 +51,7 @@ class ImageTapWrapper extends StatelessWidget {
child: Stack(
children: [
PhotoView(
imageProvider: imageProvider,
imageProvider: _imageProviderByUrl(imageUrl),
loadingBuilder: (context, event) {
return Container(
color: Colors.black,

@ -27,6 +27,7 @@ dependencies:
youtube_player_flutter: ^8.0.0
diff_match_patch: ^0.4.1
i18n_extension: ^4.1.3
gallery_saver: ^2.3.2
dev_dependencies:
flutter_test:

Loading…
Cancel
Save