Initial Image Picker implementation

pull/13/head
singerdmx 4 years ago
parent b6390153b0
commit 341b9b015d
  1. 5
      app/assets/sample_data.json
  2. 7
      app/pubspec.lock
  3. 13
      lib/models/documents/document.dart
  4. 78
      lib/models/documents/nodes/embed.dart
  5. 20
      lib/widgets/editor.dart
  6. 31
      lib/widgets/image.dart
  7. 2
      lib/widgets/toolbar.dart
  8. 7
      pubspec.lock
  9. 1
      pubspec.yaml

@ -9,9 +9,8 @@
"insert":"\n" "insert":"\n"
}, },
{ {
"insert":{ "insert": {
"_type":"hr", "image":"https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
"_inline":false
} }
}, },
{ {

@ -149,6 +149,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.2" version: "1.9.2"
photo_view:
dependency: transitive
description:
name: photo_view
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.3"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:

@ -160,11 +160,14 @@ class Document {
} }
Object _normalize(Object data) { Object _normalize(Object data) {
return data is String if (data is String) {
? data return data;
: data is Embeddable }
? data
: Embeddable.fromJson(data); if (data is Embeddable) {
return data;
}
return Embeddable.fromJson(data);
} }
close() { close() {

@ -1,84 +1,28 @@
import 'dart:collection';
import 'package:collection/collection.dart';
import 'package:quiver_hashcode/hashcode.dart';
class Embeddable { class Embeddable {
static const TYPE_KEY = '_type';
static const INLINE_KEY = '_inline';
final String type; final String type;
final bool inline; final dynamic data;
final Map<String, dynamic> _data;
Embeddable(this.type, this.inline, Map<String, dynamic> data) Embeddable(this.type, this.data)
: assert(type != null), : assert(type != null),
assert(inline != null), assert(data != null);
assert(!data.containsKey(TYPE_KEY)),
assert(!data.containsKey(INLINE_KEY)),
_data = Map.from(data);
Map<String, dynamic> get data => UnmodifiableMapView(_data);
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, dynamic> m = Map<String, dynamic>.from(_data); Map<String, String> m = {type: data};
m[TYPE_KEY] = type;
m[INLINE_KEY] = inline;
return m; return m;
} }
static Embeddable fromJson(Map<String, dynamic> json) { static Embeddable fromJson(Map<String, dynamic> json) {
String type = json[TYPE_KEY] as String; Map<String, dynamic> m = Map<String, dynamic>.from(json);
bool inline = json[INLINE_KEY] as bool; assert(m.length == 1, 'Embeddable map has one key');
Map<String, dynamic> data = Map<String, dynamic>.from(json);
data.remove(TYPE_KEY);
data.remove(INLINE_KEY);
if (inline) {
return Span(type, data: data);
}
return BlockEmbed(type, data: data);
}
@override
bool operator ==(dynamic other) {
if (identical(this, other)) {
return true;
}
if (other is! Embeddable) {
return false;
}
final typedOther = other;
return typedOther.type == type &&
typedOther.inline == inline &&
DeepCollectionEquality().equals(typedOther._data, _data);
}
@override
int get hashCode {
if (_data.isEmpty) {
return hash2(type, inline);
}
final dataHash = hashObjects( return BlockEmbed(m.keys.first, m.values.first);
_data.entries.map((e) => hash2(e.key, e.value)),
);
return hash3(type, inline, dataHash);
} }
} }
class Span extends Embeddable {
Span(
String type, {
Map<String, dynamic> data = const {},
}) : super(type, true, data);
}
class BlockEmbed extends Embeddable { class BlockEmbed extends Embeddable {
BlockEmbed( BlockEmbed(String type, String data) : super(type, data);
String type, {
Map<String, dynamic> data = const {}, static final BlockEmbed horizontalRule = BlockEmbed('divider', 'hr');
}) : super(type, false, data);
static final BlockEmbed horizontalRule = BlockEmbed('hr'); static BlockEmbed image(String imageUrl) => BlockEmbed('image', imageUrl);
static BlockEmbed image(String source) =>
BlockEmbed('image', data: {'source': source});
} }

@ -13,6 +13,7 @@ import 'package:flutter_quill/models/documents/nodes/container.dart'
import 'package:flutter_quill/models/documents/nodes/leaf.dart'; import 'package:flutter_quill/models/documents/nodes/leaf.dart';
import 'package:flutter_quill/models/documents/nodes/line.dart'; import 'package:flutter_quill/models/documents/nodes/line.dart';
import 'package:flutter_quill/models/documents/nodes/node.dart'; import 'package:flutter_quill/models/documents/nodes/node.dart';
import 'package:flutter_quill/widgets/image.dart';
import 'package:flutter_quill/widgets/raw_editor.dart'; import 'package:flutter_quill/widgets/raw_editor.dart';
import 'package:flutter_quill/widgets/text_selection.dart'; import 'package:flutter_quill/widgets/text_selection.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -73,13 +74,15 @@ abstract class RenderAbstractEditor {
Widget _defaultEmbedBuilder(BuildContext context, Embed node) { Widget _defaultEmbedBuilder(BuildContext context, Embed node) {
switch (node.value.type) { switch (node.value.type) {
case 'hr': case 'divider':
final style = QuillStyles.getStyles(context, true); final style = QuillStyles.getStyles(context, true);
return Divider( return Divider(
height: style.paragraph.style.fontSize * style.paragraph.style.height, height: style.paragraph.style.fontSize * style.paragraph.style.height,
thickness: 2, thickness: 2,
color: Colors.grey.shade200, color: Colors.grey.shade200,
); );
case 'image':
return _buildImage(context, node.value.data);
default: default:
throw UnimplementedError( throw UnimplementedError(
'Embeddable type "${node.value.type}" is not supported by default embed ' 'Embeddable type "${node.value.type}" is not supported by default embed '
@ -88,6 +91,21 @@ Widget _defaultEmbedBuilder(BuildContext context, Embed node) {
} }
} }
Widget _buildImage(BuildContext context, String imageUrl) {
return GestureDetector(
child: Image.network(imageUrl),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ImageTapWrapper(imageProvider: NetworkImage(imageUrl)),
),
);
},
);
}
class QuillEditor extends StatefulWidget { class QuillEditor extends StatefulWidget {
final QuillController controller; final QuillController controller;
final FocusNode focusNode; final FocusNode focusNode;

@ -0,0 +1,31 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:photo_view/photo_view.dart';
class ImageTapWrapper extends StatelessWidget {
const ImageTapWrapper({
this.imageProvider,
});
final ImageProvider imageProvider;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
constraints: BoxConstraints.expand(
height: MediaQuery.of(context).size.height,
),
child: GestureDetector(
onTapDown: (_) {
Navigator.pop(context);
},
child: PhotoView(
imageProvider: imageProvider,
),
),
),
);
}
}

@ -435,7 +435,7 @@ class _ImageButtonState extends State<ImageButton> {
if (imageUploadUrl != null) if (imageUploadUrl != null)
{ {
widget.controller.replaceText( widget.controller.replaceText(
index, length, BlockEmbed(imageUploadUrl), null) index, length, BlockEmbed.image(imageUploadUrl), null)
} }
}); });
}, },

@ -135,6 +135,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.2" version: "1.9.2"
photo_view:
dependency: "direct main"
description:
name: photo_view
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.3"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:

@ -19,6 +19,7 @@ dependencies:
url_launcher: ^5.7.10 url_launcher: ^5.7.10
flutter_colorpicker: ^0.3.4 flutter_colorpicker: ^0.3.4
image_picker: ^0.6.7+17 image_picker: ^0.6.7+17
photo_view: ^0.10.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

Loading…
Cancel
Save