I can't implement insert image from url with Quill. Some errors are capture by me:



import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_docsx/colors.dart';
import 'package:flutter_quill/flutter_quill.dart' as quill hide Text;
import 'package:flutter_quill/extensions.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:file_picker/file_picker.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class DocumentScreen extends ConsumerStatefulWidget {
final String id;
const DocumentScreen({
Key? key,
required this.id,
}) : super(key: key);
@override
ConsumerState<ConsumerStatefulWidget> createState() => _DocumentScreenState();
}
class _DocumentScreenState extends ConsumerState<DocumentScreen> {
TextEditingController titleController = TextEditingController(text: 'Untitled Document');
quill.QuillController? _controller = quill.QuillController.basic();
final FocusNode _focusNode = FocusNode();
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
titleController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: kWhiteColor,
elevation: 0,
actions: [
Padding(
padding: const EdgeInsets.all(10.0),
child: ElevatedButton.icon(
onPressed: () {
},
icon: const Icon(
Icons.lock,
size: 16,
),
label: const Text('Share'),
style: ElevatedButton.styleFrom(
backgroundColor: kBlueColor,
),
),
),
],
title: Padding(
padding: const EdgeInsets.symmetric(vertical: 9.0),
child: Row(
children: [
GestureDetector(
onTap: () {
// Routemaster.of(context).replace('/');
},
child: Image.asset(
'assets/images/docs-logo.png',
height: 40,
),
),
const SizedBox(width: 10),
SizedBox(
width: 180,
child: TextField(
controller: titleController,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: kBlueColor,
),
),
contentPadding: EdgeInsets.only(left: 10),
),
onSubmitted: (value) {}
// (value) => updateTitle(ref, value),
),
),
],
),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: kGreyColor,
width: 0.1,
),
),
),
),
),
body: Center(
child: Column(
children: [
const SizedBox(height: 10),
quill.QuillToolbar.basic(
controller: _controller!,
embedButtons: FlutterQuillEmbeds.buttons(
// if omit, "image" button only allows adding images from url.
// onImagePickCallback: _onImagePickCallback,
// onVideoPickCallback: _onVideoPickCallback,
),
afterButtonPressed: _focusNode.requestFocus,
),
const SizedBox(height: 10),
Expanded(
child: SizedBox(
width: 750,
child: Card(
color: kWhiteColor,
elevation: 5,
child: Padding(
padding: const EdgeInsets.all(30.0),
child: quill.QuillEditor(
controller: _controller!,
readOnly: false,
embedBuilders: FlutterQuillEmbeds.builders(),
focusNode: _focusNode,
autoFocus: false,
expands: false,
scrollable: true,
padding: EdgeInsets.zero,
scrollController: ScrollController(),
),
),
),
),
)
],
),
),
);
}
}
I have read all relate references and documents about Quill flutter as well as guides to add image to Quill but I failed. I do as the same as they do, however, the error occurred weirdly. "!kIsWeb. Please provide image EmbedBuilder for Web" or "Embeddable type "image" is not supported by supplied embed builders" is log bug although I have the line "embedBuilders: FlutterQuillEmbeds.builders()". I have tried to build a new version similar to the following source code but everything is still error as before:
https://github.com/singerdmx/flutter-quill/blob/master/example/lib/pages/home_page.dart https://pub.dev/packages/flutter_quill
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_docsx/colors.dart';
import 'package:flutter_quill/flutter_quill.dart' as quill hide Text;
import 'package:flutter_quill/extensions.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:file_picker/file_picker.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
class DocumentScreen extends ConsumerStatefulWidget {
final String id;
const DocumentScreen({
Key? key,
required this.id,
}) : super(key: key);
@override
ConsumerState<ConsumerStatefulWidget> createState() => _DocumentScreenState();
}
class _DocumentScreenState extends ConsumerState<DocumentScreen> {
TextEditingController titleController = TextEditingController(text: 'Untitled Document');
quill.QuillController? _controller = quill.QuillController.basic();
final FocusNode _focusNode = FocusNode();
late Widget quillEditor;
late Widget toolbar;
// ErrorModel? errorModel;
// SocketRepository socketRepository = SocketRepository();
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
titleController.dispose();
}
Future<String> _onImagePickCallback(File file) async {
// Copies the picked file from temporary cache to applications directory
final appDocDir = await getApplicationDocumentsDirectory();
final copiedFile = await file.copy('${appDocDir.path}/${basename(file.path)}');
return copiedFile.path.toString();
}
Future<String> _onVideoPickCallback(File file) async {
// Copies the picked file from temporary cache to applications directory
final appDocDir = await getApplicationDocumentsDirectory();
final copiedFile = await file.copy('${appDocDir.path}/${basename(file.path)}');
return copiedFile.path.toString();
}
Future<String?> _webImagePickImpl(
OnImagePickCallback onImagePickCallback) async {
final result = await FilePicker.platform.pickFiles();
if (result == null) {
return null;
}
// Take first, because we don't allow picking multiple files.
final fileName = result.files.first.name;
final file = File(fileName);
return onImagePickCallback(file);
}
Future<String> _onImagePaste(Uint8List imageBytes) async {
// Saves the image to applications directory
final appDocDir = await getApplicationDocumentsDirectory();
final file = await File(
'${appDocDir.path}/${basename('${DateTime.now().millisecondsSinceEpoch}.png')}')
.writeAsBytes(imageBytes, flush: true);
return file.path.toString();
}
_buildWelcomeEditor(BuildContext context) {
if (kIsWeb) {
quillEditor = MouseRegion(
cursor: SystemMouseCursors.text,
child: quill.QuillEditor(
controller: _controller!,
scrollController: ScrollController(),
scrollable: true,
focusNode: _focusNode,
autoFocus: false,
readOnly: false,
placeholder: 'Add content',
expands: false,
padding: EdgeInsets.zero,
// onTapUp: (details, p1) {
// return _onTripleClickSelection();
// },
embedBuilders: FlutterQuillEmbeds.builders(),
),
);
}else{
quillEditor = MouseRegion(
cursor: SystemMouseCursors.text,
child: quill.QuillEditor(
controller: _controller!,
scrollController: ScrollController(),
scrollable: true,
focusNode: _focusNode,
autoFocus: false,
readOnly: false,
placeholder: 'Add content',
enableSelectionToolbar: isMobile(),
expands: false,
padding: EdgeInsets.zero,
onImagePaste: _onImagePaste,
// onTapUp: (details, p1) {
// return _onTripleClickSelection();
// },
embedBuilders: FlutterQuillEmbeds.builders(),
),
);
}
if (kIsWeb) {
toolbar = quill.QuillToolbar.basic(
controller: _controller!,
embedButtons: FlutterQuillEmbeds.buttons(
onImagePickCallback: _onImagePickCallback,
webImagePickImpl: _webImagePickImpl,
),
showAlignmentButtons: true,
afterButtonPressed: _focusNode.requestFocus,
);
} else {
toolbar = quill.QuillToolbar.basic(
controller: _controller!,
embedButtons: FlutterQuillEmbeds.buttons(
onImagePickCallback: _onImagePickCallback,
onVideoPickCallback: _onVideoPickCallback,
),
showAlignmentButtons: true,
afterButtonPressed: _focusNode.requestFocus,
);
}
}
@override
Widget build(BuildContext context) {
_buildWelcomeEditor(context);
return Scaffold(
appBar: AppBar(
backgroundColor: kWhiteColor,
elevation: 0,
actions: [
Padding(
padding: const EdgeInsets.all(10.0),
child: ElevatedButton.icon(
onPressed: () {
},
icon: const Icon(
Icons.lock,
size: 16,
),
label: const Text('Share'),
style: ElevatedButton.styleFrom(
backgroundColor: kBlueColor,
),
),
),
],
title: Padding(
padding: const EdgeInsets.symmetric(vertical: 9.0),
child: Row(
children: [
GestureDetector(
onTap: () {
// Routemaster.of(context).replace('/');
},
child: Image.asset(
'assets/images/docs-logo.png',
height: 40,
),
),
const SizedBox(width: 10),
SizedBox(
width: 180,
child: TextField(
controller: titleController,
decoration: const InputDecoration(
border: InputBorder.none,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: kBlueColor,
),
),
contentPadding: EdgeInsets.only(left: 10),
),
onSubmitted: (value) {}
// (value) => updateTitle(ref, value),
),
),
],
),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: kGreyColor,
width: 0.1,
),
),
),
),
),
body: Center(
child: Column(
children: [
const SizedBox(height: 10),
// quill.QuillToolbar.basic(
// controller: _controller!,
// embedButtons: FlutterQuillEmbeds.buttons(
// // onImagePickCallback: _onImagePickCallback,
// // onVideoPickCallback: _onVideoPickCallback,
// ),
// afterButtonPressed: _focusNode.requestFocus,
// ),
toolbar,
const SizedBox(height: 10),
Expanded(
child: SizedBox(
width: 750,
child: Card(
color: kWhiteColor,
elevation: 5,
child: Padding(
padding: const EdgeInsets.all(30.0),
// child: quill.QuillEditor(
// controller: _controller!,
// readOnly: false,
// // embedBuilders: [
// // ...FlutterQuillEmbeds.builders(),
// // NotesEmbedBuilder(addEditNote: _addEditNote)
// // ],
// embedBuilders: FlutterQuillEmbeds.builders(),
// focusNode: _focusNode,
// autoFocus: false,
// expands: false,
// scrollable: true,
// padding: EdgeInsets.zero,
// scrollController: ScrollController(),
// ),
child: quillEditor,
),
),
),
)
],
),
),
);
}
}
class NotesEmbedBuilder implements quill.EmbedBuilder {
NotesEmbedBuilder({required this.addEditNote});
Future<void> Function(BuildContext context, {quill.Document? document}) addEditNote;
@override
String get key => 'notes';
@override
Widget build(
BuildContext context,
quill.QuillController controller,
Embed node,
bool readOnly,
) {
final notes = NotesBlockEmbed(node.value.data).document;
return Material(
color: Colors.transparent,
child: ListTile(
title: Text(
notes.toPlainText().replaceAll('\n', ' '),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
leading: const Icon(Icons.notes),
onTap: () => addEditNote(context, document: notes),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: const BorderSide(color: Colors.grey),
),
),
);
}
}
class NotesBlockEmbed extends quill.CustomBlockEmbed {
const NotesBlockEmbed(String value) : super(noteType, value);
static const String noteType = 'notes';
static NotesBlockEmbed fromDocument(quill.Document document) =>
NotesBlockEmbed(jsonEncode(document.toDelta().toJson()));
quill.Document get document => quill.Document.fromJson(jsonDecode(data));
}
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building TextLine(dirty, dependencies: [MediaQuery,
_LocalizationsScope-[GlobalKey#0dd6d], _QuillPressedKeysAccess], state: _TextLineState#9d5f1):
Assertion failed:
file:///C:/Users/Kid/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_quill_extensions-0.1.0/lib/embeds/builders.dart:29:12
!kIsWeb
"Please provide image EmbedBuilder for Web"
The relevant error-causing widget was:
TextLine
TextLine:file:///C:/Users/Kid/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_quill-6.1.5/lib/src/widgets/raw_editor.dart:667:22
class ScreenBlogEditor extends StatefulWidget {
const ScreenBlogEditor({Key? key}) : super(key: key);
@override
State<ScreenBlogEditor> createState() => _ScreenBlogEditorState();
}
class _ScreenBlogEditorState extends State<ScreenBlogEditor> with AutomaticKeepAliveClientMixin {
QuillController _controller = QuillController.basic();
// Method to pick an image from the device's gallery
Future<void> _pickImage() async {
// Initialize an instance of ImagePicker
final ImagePicker _picker = ImagePicker();
// Use ImagePicker to select an image from the gallery
final XFile? pickedImage = await _picker.pickImage(source: ImageSource.gallery);
// Check if an image was successfully picked
if (pickedImage != null) {
// Extract the file path of the picked image
final String imagePath = pickedImage.path;
// Create a File object from the picked image path
final File file = File(imagePath);
// Create a Delta representing the image to insert into the editor
final Delta imageDelta = Delta()
// Insert a new line before the image
..insert("\n")
// Insert the image data as a map
..insert({
// 'image' key represents the image data, in this case, the file path
'image': file.path.toString(),
})
// Insert a new line after the image
..insert("\n");
// Compose the image Delta into the Quill controller
_controller.compose(
imageDelta, // Delta representing the image
// Set the text selection to the end of the inserted image
TextSelection.collapsed(offset: imageDelta.length),
// Specify that the change was made locally
ChangeSource.local,
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: QuillEditor.basic(
configurations: QuillEditorConfigurations(
controller: _controller,
readOnly: false,
showCursor: true,
embedBuilders: kIsWeb ? FlutterQuillEmbeds.editorWebBuilders() : FlutterQuillEmbeds.editorBuilders(),
),
),
),
),
QuillToolbar(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
QuillToolbarToggleStyleButton(controller: _controller, attribute: Attribute.bold),
QuillToolbarToggleStyleButton(controller: _controller, attribute: Attribute.italic),
QuillToolbarToggleStyleButton(controller: _controller, attribute: Attribute.underline),
QuillToolbarLinkStyleButton(controller: _controller),
QuillToolbarCustomButton(
controller: _controller,
options: QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.image),
onPressed: _pickImage,
),
),
],
),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Get the Quill editor data as plain text
String quillText = getQuillText();
print("Quill Editor Text: $quillText");
},
child: Icon(Icons.check),
),
);
}
String getQuillText() {
// Retrieve the plain text from the Quill editor
return "Chal Chal ${_controller.document.toDelta().toJson()}";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With