diff --git a/CHANGELOG.md b/CHANGELOG.md index 45a82501..7480006f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,4 +78,19 @@ * Support display local image besides network image. ## [0.2.8] -* Support display local image besides network image in stable branch. \ No newline at end of file +* Support display local image besides network image in stable branch. + +## [0.2.9] +* Update TextInputConfiguration autocorrect to true. + +## [0.2.10] +* Update TextInputConfiguration autocorrect to true in stable branch. + +## [0.2.11] +* Fix static analysis error. + +## [0.2.12] +* Support placeholder. + +## [0.3.0] +* Line Height calculated based on font size. \ No newline at end of file diff --git a/README.md b/README.md index e0c87ee0..73c5079e 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,73 @@ # FlutterQuill -Rich text editor and a [Quill] component for [Flutter]. +FlutterQuill is a rich text editor and a [Quill] component for [Flutter]. -This library is a WYSIWYG editor built for the modern mobile platform only and web is under development. You can join [Slack Group] for discussion. +This library is a WYSIWYG editor built for the modern mobile platform, with web compatibility under development. You can join our [Slack Group] for discussion. https://pub.dev/packages/flutter_quill +## Usage + +See the `example` directory for a minimal example of how to use FlutterQuill. You typically just need to instantiate a controller: + +``` +QuillController _controller = QuillController.basic(); +``` + +and then embed the toolbar and the editor, within your app. For example: + +```dart +Column( + children: [ + QuillToolbar.basic(controller: _controller), + Expanded( + child: Container( + child: QuillEditor.basic( + controller: _controller, + readOnly: false, // true for view only mode + ), + ), + ) + ], +) +``` +Check out [Sample Page] for advanced usage. + +## Input / Output + +This library uses [Quill] as an internal data format. + +* Use `_controller.document.toDelta()` to extract the deltas. +* Use `_controller.document.toPlainText()` to extract plain text. + +FlutterQuill provides some JSON serialisation support, so that you can save and open documents. To save a document as JSON, do something like the following: + +``` +var json = jsonEncode(_controller.document.toDelta().toJson()); +``` + +You can then write this to storage. + +To open a FlutterQuill editor with an existing JSON representation that you've previously stored, you can do something like this: + +``` +var myJSON = jsonDecode(incomingJSONText); +_controller = QuillController( + document: Document.fromJson(myJSON), + selection: TextSelection.collapsed(offset: 0)); +``` + +## Configuration + +The `QuillToolbar` class lets you customise which formatting options are available. +[Sample Page] provides sample code for advanced usage and configuration. + +## Web + Default branch `master` is on channel `master`. To use channel `stable`, switch to branch `stable`. Branch `master` on channel `master` supports web. To run the app on web do the following: + 1) Change flutter channel to master using `flutter channel master`, followed by `flutter upgrade`. 2) Enable web using `flutter config --enable-web` and restart the IDE. 3) Upon successful execution of step 1 and 2 you should see `Chrome` as one of the devices which you run `flutter devices`. @@ -30,8 +89,9 @@ For web development, [ReactQuill] is recommended to use for compatibility. One client and affiliated collaborator of **[FlutterQuill]** is Bullet Journal App: https://bulletjournal.us/home/index.html -[Quill]: https://quilljs.com +[Quill]: https://quilljs.com/docs/formats [Flutter]: https://github.com/flutter/flutter [FlutterQuill]: https://pub.dev/packages/flutter_quill [ReactQuill]: https://github.com/zenoamaro/react-quill [Slack Group]: https://join.slack.com/t/bulletjournal1024/shared_invite/zt-fys7t9hi-ITVU5PGDen1rNRyCjdcQ2g +[Sample Page]: https://github.com/singerdmx/flutter-quill/blob/master/app/lib/pages/home_page.dart diff --git a/app/lib/pages/home_page.dart b/app/lib/pages/home_page.dart index 422d0954..cee58494 100644 --- a/app/lib/pages/home_page.dart +++ b/app/lib/pages/home_page.dart @@ -10,6 +10,8 @@ import 'package:flutter_quill/widgets/controller.dart'; import 'package:flutter_quill/widgets/default_styles.dart'; import 'package:flutter_quill/widgets/editor.dart'; import 'package:flutter_quill/widgets/toolbar.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:tuple/tuple.dart'; import 'read_only_page.dart'; @@ -103,41 +105,44 @@ class _HomePageState extends State { focusNode: _focusNode, autoFocus: false, readOnly: false, + placeholder: 'Add content', enableInteractiveSelection: true, expands: false, padding: EdgeInsets.zero, customStyles: DefaultStyles( - h1: DefaultTextBlockStyle( - TextStyle( - fontSize: 32.0, - color: Colors.black, - height: 1.15, - fontWeight: FontWeight.w300, - ), - Tuple2(16.0, 0.0), - Tuple2(0.0, 0.0), - null), - sizeSmall: TextStyle(fontSize: 9.0)), + h1: DefaultTextBlockStyle( + TextStyle( + fontSize: 32.0, + color: Colors.black, + height: 1.15, + fontWeight: FontWeight.w300, + ), + Tuple2(16.0, 0.0), + Tuple2(0.0, 0.0), + null), + sizeSmall: TextStyle(fontSize: 9.0), + ), ), ), Container( - padding: - EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.9), child: QuillToolbar.basic( controller: _controller, - uploadFileCallback: _fakeUploadImageCallBack), - ) + onImagePickCallback: _onImagePickCallback), + ), ], ), ); } - Future _fakeUploadImageCallBack(File file) async { - print(file); - var completer = new Completer(); - completer.complete( - 'https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png'); - return completer.future; + // Renders the image picked by imagePicker from local file storage + // You can also upload the picked image to any server (eg : AWS s3 or Firebase) and then return the uploaded image URL + Future _onImagePickCallback(File file) async { + if (file == null) return null; + // Copies the picked file from temporary cache to applications directory + Directory appDocDir = await getApplicationDocumentsDirectory(); + File copiedFile = + await file.copy('${appDocDir.path}/${basename(file.path)}'); + return copiedFile.path.toString(); } Widget _buildMenuBar(BuildContext context) { @@ -156,7 +161,7 @@ class _HomePageState extends State { void _readOnly() { Navigator.push( - context, + super.context, MaterialPageRoute( builder: (BuildContext context) => ReadOnlyPage(), ), diff --git a/app/pubspec.lock b/app/pubspec.lock index f4e16a57..53c35ed4 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -78,6 +78,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" + file_picker: + dependency: transitive + description: + name: file_picker + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.6" flutter: dependency: "direct main" description: flutter @@ -103,7 +124,7 @@ packages: path: ".." relative: true source: path - version: "0.2.8" + version: "0.3.0" flutter_test: dependency: "direct dev" description: flutter @@ -141,7 +162,7 @@ packages: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.6.7+17" + version: "0.6.7+22" image_picker_platform_interface: dependency: transitive description: @@ -177,6 +198,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.27" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+8" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+3" pedantic: dependency: transitive description: @@ -191,6 +247,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.10.3" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" plugin_platform_interface: dependency: transitive description: @@ -198,6 +261,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" quill_delta: dependency: transitive description: @@ -343,6 +413,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.4+1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" zone_local: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 3a6814e0..4d471487 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -28,6 +28,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 + path_provider: ^1.6.27 flutter_quill: path: ../ diff --git a/example/main.dart b/example/main.dart index b79b35e5..a523c5a6 100644 --- a/example/main.dart +++ b/example/main.dart @@ -1,6 +1,3 @@ -import 'dart:async'; -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_quill/widgets/controller.dart'; import 'package:flutter_quill/widgets/editor.dart'; @@ -19,8 +16,7 @@ class _HomePageState extends State { return Scaffold( body: Column( children: [ - QuillToolbar.basic( - controller: _controller, uploadFileCallback: _uploadImageCallBack), + QuillToolbar.basic(controller: _controller), Expanded( child: Container( child: QuillEditor.basic( @@ -32,9 +28,4 @@ class _HomePageState extends State { ], )); } - - Future _uploadImageCallBack(File file) async { - // call upload file API and return file's absolute url - return new Completer().future; - } } diff --git a/lib/models/documents/attribute.dart b/lib/models/documents/attribute.dart index d7865e7d..7b363bed 100644 --- a/lib/models/documents/attribute.dart +++ b/lib/models/documents/attribute.dart @@ -24,6 +24,7 @@ class Attribute { Attribute.link.key: Attribute.link, Attribute.color.key: Attribute.color, Attribute.background.key: Attribute.background, + Attribute.placeholder.key: Attribute.placeholder, Attribute.header.key: Attribute.header, Attribute.indent.key: Attribute.indent, Attribute.align.key: Attribute.align, @@ -53,6 +54,8 @@ class Attribute { static final BackgroundAttribute background = BackgroundAttribute(null); + static final PlaceholderAttribute placeholder = PlaceholderAttribute(); + static final HeaderAttribute header = HeaderAttribute(); static final IndentAttribute indent = IndentAttribute(); @@ -78,7 +81,8 @@ class Attribute { Attribute.strikeThrough.key, Attribute.link.key, Attribute.color.key, - Attribute.background.key + Attribute.background.key, + Attribute.placeholder.key, }; static final Set blockKeys = { @@ -222,6 +226,11 @@ class BackgroundAttribute extends Attribute { : super('background', AttributeScope.INLINE, val); } +/// This is custom attribute for hint +class PlaceholderAttribute extends Attribute { + PlaceholderAttribute() : super('placeholder', AttributeScope.INLINE, true); +} + class HeaderAttribute extends Attribute { HeaderAttribute({int level}) : super('header', AttributeScope.BLOCK, level); } diff --git a/lib/models/documents/document.dart b/lib/models/documents/document.dart index bcfd5152..1573f9af 100644 --- a/lib/models/documents/document.dart +++ b/lib/models/documents/document.dart @@ -237,6 +237,22 @@ class Document { _root.remove(node); } } + + bool isEmpty() { + if (root.children.length != 1) { + return false; + } + + final Node node = root.children.first; + if (!node.isLast) { + return false; + } + + Delta delta = node.toDelta(); + return delta.length == 1 && + delta.first.data == '\n' && + delta.first.key == 'insert'; + } } enum ChangeSource { diff --git a/lib/widgets/default_styles.dart b/lib/widgets/default_styles.dart index 861bb3cd..4cad7bbb 100644 --- a/lib/widgets/default_styles.dart +++ b/lib/widgets/default_styles.dart @@ -54,6 +54,7 @@ class DefaultStyles { final TextStyle sizeLarge; // 'large' final TextStyle sizeHuge; // 'huge' final TextStyle link; + final DefaultTextBlockStyle placeHolder; final DefaultTextBlockStyle lists; final DefaultTextBlockStyle quote; final DefaultTextBlockStyle code; @@ -70,6 +71,7 @@ class DefaultStyles { this.underline, this.strikeThrough, this.link, + this.placeHolder, this.lists, this.quote, this.code, @@ -86,7 +88,7 @@ class DefaultStyles { fontSize: 16.0, height: 1.3, ); - Tuple2 baseSpacing = Tuple2(6.0, 10); + Tuple2 baseSpacing = Tuple2(6.0, 0); String fontFamily; switch (themeData.platform) { case TargetPlatform.iOS: @@ -135,7 +137,7 @@ class DefaultStyles { Tuple2(0.0, 0.0), null), paragraph: DefaultTextBlockStyle( - baseStyle, baseSpacing, Tuple2(0.0, 0.0), null), + baseStyle, Tuple2(0.0, 0.0), Tuple2(0.0, 0.0), null), bold: TextStyle(fontWeight: FontWeight.bold), italic: TextStyle(fontStyle: FontStyle.italic), underline: TextStyle(decoration: TextDecoration.underline), @@ -144,6 +146,15 @@ class DefaultStyles { color: themeData.accentColor, decoration: TextDecoration.underline, ), + placeHolder: DefaultTextBlockStyle( + defaultTextStyle.style.copyWith( + fontSize: 20.0, + height: 1.5, + color: Colors.grey.withOpacity(0.6), + ), + Tuple2(0.0, 0.0), + Tuple2(0.0, 0.0), + null), lists: DefaultTextBlockStyle( baseStyle, baseSpacing, Tuple2(0.0, 6.0), null), quote: DefaultTextBlockStyle( @@ -188,6 +199,7 @@ class DefaultStyles { underline: other.underline ?? this.underline, strikeThrough: other.strikeThrough ?? this.strikeThrough, link: other.link ?? this.link, + placeHolder: other.placeHolder ?? this.placeHolder, lists: other.lists ?? this.lists, quote: other.quote ?? this.quote, code: other.code ?? this.code, diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index 018ef0b2..29472aae 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -134,6 +134,7 @@ class QuillEditor extends StatefulWidget { final bool autoFocus; final bool showCursor; final bool readOnly; + final String placeholder; final bool enableInteractiveSelection; final double minHeight; final double maxHeight; @@ -154,6 +155,7 @@ class QuillEditor extends StatefulWidget { @required this.autoFocus, this.showCursor, @required this.readOnly, + this.placeholder, this.enableInteractiveSelection, this.minHeight, this.maxHeight, @@ -256,6 +258,7 @@ class _QuillEditorState extends State widget.scrollable, widget.padding, widget.readOnly, + widget.placeholder, widget.onLaunchUrl, ToolbarOptions( copy: true, @@ -359,6 +362,9 @@ class _QuillEditorSelectionGestureDetectorBuilder } bool _onTapping(TapUpDetails details) { + if (_state.widget.controller.document.isEmpty()) { + return false; + } TextPosition pos = getRenderEditor().getPositionForOffset(details.globalPosition); containerNode.ChildQuery result = diff --git a/lib/widgets/raw_editor.dart b/lib/widgets/raw_editor.dart index 3ff24e55..fbbb8a83 100644 --- a/lib/widgets/raw_editor.dart +++ b/lib/widgets/raw_editor.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -32,6 +34,7 @@ class RawEditor extends StatefulWidget { final bool scrollable; final EdgeInsetsGeometry padding; final bool readOnly; + final String placeholder; final ValueChanged onLaunchUrl; final ToolbarOptions toolbarOptions; final bool showSelectionHandles; @@ -58,6 +61,7 @@ class RawEditor extends StatefulWidget { this.scrollable, this.padding, this.readOnly, + this.placeholder, this.onLaunchUrl, this.toolbarOptions, this.showSelectionHandles, @@ -374,7 +378,7 @@ class RawEditorState extends EditorState inputType: TextInputType.multiline, readOnly: widget.readOnly, obscureText: false, - autocorrect: false, + autocorrect: true, inputAction: TextInputAction.newline, keyboardAppearance: widget.keyboardAppearance, textCapitalization: widget.textCapitalization, @@ -503,13 +507,21 @@ class RawEditorState extends EditorState _focusAttachment.reparent(); super.build(context); + Document _doc = widget.controller.document; + if (_doc.isEmpty() && + !widget.focusNode.hasFocus && + widget.placeholder != null) { + _doc = Document.fromJson(jsonDecode( + '[{"attributes":{"placeholder":true},"insert":"${widget.placeholder}\\n"}]')); + } + Widget child = CompositedTransformTarget( link: _toolbarLayerLink, child: Semantics( child: _Editor( key: _editorKey, - children: _buildChildren(context), - document: widget.controller.document, + children: _buildChildren(_doc, context), + document: _doc, selection: widget.controller.selection, hasFocus: _hasFocus, textDirection: _textDirection, @@ -562,30 +574,13 @@ class RawEditorState extends EditorState requestKeyboard(); } - _buildChildren(BuildContext context) { + _buildChildren(Document doc, BuildContext context) { final result = []; Map indentLevelCounts = {}; - for (Node node in widget.controller.document.root.children) { + for (Node node in doc.root.children) { if (node is Line) { - TextLine textLine = TextLine( - line: node, - textDirection: _textDirection, - embedBuilder: widget.embedBuilder, - styles: _styles, - ); - EditableTextLine editableTextLine = EditableTextLine( - node, - null, - textLine, - 0, - _getVerticalSpacingForLine(node, _styles), - _textDirection, - widget.controller.selection, - widget.selectionColor, - widget.enableInteractiveSelection, - _hasFocus, - MediaQuery.of(context).devicePixelRatio, - _cursorCont); + EditableTextLine editableTextLine = + _getEditableTextLineFromNode(node, context); result.add(editableTextLine); } else if (node is Block) { Map attrs = node.style.attributes; @@ -612,6 +607,30 @@ class RawEditorState extends EditorState return result; } + EditableTextLine _getEditableTextLineFromNode( + Line node, BuildContext context) { + TextLine textLine = TextLine( + line: node, + textDirection: _textDirection, + embedBuilder: widget.embedBuilder, + styles: _styles, + ); + EditableTextLine editableTextLine = EditableTextLine( + node, + null, + textLine, + 0, + _getVerticalSpacingForLine(node, _styles), + _textDirection, + widget.controller.selection, + widget.selectionColor, + widget.enableInteractiveSelection, + _hasFocus, + MediaQuery.of(context).devicePixelRatio, + _cursorCont); + return editableTextLine; + } + Tuple2 _getVerticalSpacingForLine( Line line, DefaultStyles defaultStyles) { Map attrs = line.style.attributes; diff --git a/lib/widgets/text_line.dart b/lib/widgets/text_line.dart index 53bb07f8..8608e69b 100644 --- a/lib/widgets/text_line.dart +++ b/lib/widgets/text_line.dart @@ -48,7 +48,7 @@ class TextLine extends StatelessWidget { StrutStyle.fromTextStyle(textSpan.style, forceStrutHeight: true); final textAlign = _getTextAlign(); RichText child = RichText( - text: _buildTextSpan(context), + text: TextSpan(children: [textSpan]), textAlign: textAlign, textDirection: textDirection, strutStyle: strutStyle, @@ -88,6 +88,11 @@ class TextLine extends StatelessWidget { TextStyle textStyle = TextStyle(); + if (line.style.containsKey(Attribute.placeholder.key)) { + textStyle = defaultStyles.placeHolder.style; + return TextSpan(children: children, style: textStyle); + } + Attribute header = line.style.attributes[Attribute.header.key]; Map m = { Attribute.h1: defaultStyles.h1.style, diff --git a/lib/widgets/toolbar.dart b/lib/widgets/toolbar.dart index 69de725a..ae0a6553 100644 --- a/lib/widgets/toolbar.dart +++ b/lib/widgets/toolbar.dart @@ -1,7 +1,9 @@ import 'dart:io'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart'; import 'package:flutter_quill/models/documents/attribute.dart'; import 'package:flutter_quill/models/documents/nodes/embed.dart'; @@ -14,7 +16,7 @@ import 'controller.dart'; double iconSize = 18.0; double kToolbarHeight = iconSize * 2; -typedef UploadFileCallback = Future Function(File file); +typedef OnImagePickCallback = Future Function(File file); class InsertEmbedButton extends StatelessWidget { final QuillController controller; @@ -490,7 +492,7 @@ class ImageButton extends StatefulWidget { final QuillController controller; - final UploadFileCallback uploadFileCallback; + final OnImagePickCallback onImagePickCallback; final ImageSource imageSource; @@ -499,7 +501,7 @@ class ImageButton extends StatefulWidget { @required this.icon, @required this.controller, @required this.imageSource, - this.uploadFileCallback}) + this.onImagePickCallback}) : assert(icon != null), assert(controller != null), super(key: key); @@ -509,16 +511,20 @@ class ImageButton extends StatefulWidget { } class _ImageButtonState extends State { + List _paths; + String _directoryPath; + String _extension; final _picker = ImagePicker(); + FileType _pickingType = FileType.any; Future _pickImage(ImageSource source) async { final PickedFile pickedFile = await _picker.getImage(source: source); final File file = File(pickedFile.path); - if (file == null || widget.uploadFileCallback == null) return null; + if (file == null || widget.onImagePickCallback == null) return null; // We simply return the absolute path to selected file. try { - String url = await widget.uploadFileCallback(file); + String url = await widget.onImagePickCallback(file); print('Image uploaded and its url is $url'); return url; } catch (error) { @@ -527,6 +533,42 @@ class _ImageButtonState extends State { return null; } + Future _pickImageWeb() async { + try { + _directoryPath = null; + _paths = (await FilePicker.platform.pickFiles( + type: _pickingType, + allowMultiple: false, + allowedExtensions: (_extension?.isNotEmpty ?? false) + ? _extension?.replaceAll(' ', '')?.split(',') + : null, + )) + ?.files; + } on PlatformException catch (e) { + print("Unsupported operation" + e.toString()); + } catch (ex) { + print(ex); + } + var _fileName = + _paths != null ? _paths.map((e) => e.name).toString() : '...'; + + if (_paths != null) { + File file = File(_fileName); + if (file == null || widget.onImagePickCallback == null) return null; + // We simply return the absolute path to selected file. + try { + String url = await widget.onImagePickCallback(file); + print('Image uploaded and its url is $url'); + return url; + } catch (error) { + print('Upload image error $error'); + } + return null; + } else { + // User canceled the picker + } + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -541,7 +583,7 @@ class _ImageButtonState extends State { onPressed: () { final index = widget.controller.selection.baseOffset; final length = widget.controller.selection.extentOffset - index; - final image = _pickImage(widget.imageSource); + final image = kIsWeb ? _pickImageWeb() : _pickImage(widget.imageSource); image.then((imageUploadUrl) => { if (imageUploadUrl != null) { @@ -891,7 +933,7 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { bool showLink = true, bool showHistory = true, bool showHorizontalRule = false, - UploadFileCallback uploadFileCallback}) { + OnImagePickCallback onImagePickCallback}) { iconSize = toolbarIconSize; return QuillToolbar(key: key, children: [ Visibility( @@ -974,22 +1016,22 @@ class QuillToolbar extends StatefulWidget implements PreferredSizeWidget { ), SizedBox(width: 0.6), Visibility( - visible: uploadFileCallback != null, + visible: onImagePickCallback != null, child: ImageButton( icon: Icons.image, controller: controller, imageSource: ImageSource.gallery, - uploadFileCallback: uploadFileCallback, + onImagePickCallback: onImagePickCallback, ), ), SizedBox(width: 0.6), Visibility( - visible: uploadFileCallback != null, + visible: onImagePickCallback != null, child: ImageButton( icon: Icons.photo_camera, controller: controller, imageSource: ImageSource.camera, - uploadFileCallback: uploadFileCallback, + onImagePickCallback: onImagePickCallback, ), ), Visibility( diff --git a/pubspec.lock b/pubspec.lock index 4afa0337..9842e845 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -71,6 +71,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + file_picker: + dependency: "direct main" + description: + name: file_picker + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.6" flutter: dependency: "direct main" description: flutter @@ -82,7 +89,7 @@ packages: name: flutter_colorpicker url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "0.3.5" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -127,14 +134,14 @@ packages: name: image_picker url: "https://pub.dartlang.org" source: hosted - version: "0.6.7+17" + version: "0.6.7+22" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.1.6" js: dependency: transitive description: @@ -169,7 +176,7 @@ packages: name: pedantic url: "https://pub.dartlang.org" source: hosted - version: "1.9.2" + version: "1.10.0" photo_view: dependency: "direct main" description: @@ -314,7 +321,7 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.5+1" + version: "0.1.5+3" url_launcher_windows: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d9cff81e..93c91366 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill description: One client and affiliated collaborator of Flutter Quill is Bullet Journal App. -version: 0.2.8 +version: 0.3.0 #author: bulletjournal homepage: https://bulletjournal.us/home/index.html repository: https://github.com/singerdmx/flutter-quill.git @@ -18,9 +18,11 @@ dependencies: tuple: ^1.0.3 url_launcher: ^5.7.10 flutter_colorpicker: ^0.3.4 - image_picker: ^0.6.7+17 + image_picker: ^0.6.7+22 photo_view: ^0.10.3 universal_html: ^1.2.1 + file_picker: ^2.1.6 + dev_dependencies: flutter_test: