Closed TamirYakov1 closed 23 hours ago
Can you provide any screenshot or video? By now most of the common html tags are supported with attributes (just the ones that are registered on the package)
Since there aren't any response, i close this. This issue is already solved since we use flutter_quill_delta_from_html
package
Is there an existing issue for this?
The question
How can i parse the text to html (for server side use) and back from html so i will get it styled on the quill?
Flutter Quill version 9.3.3
Steps to reproduce
Expected results The text will be styled the same as i left it
Actual results The text is not styled expect italic and bold and bullets
Code sample
`import 'dart:convert';
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:quill_html_converter/quill_html_converter.dart'; import 'package:sviva_visits/common/extensions/extensions.dart'; import 'package:sviva_visits/common/widgets/conditional_parent.dart'; import 'package:sviva_visits/common/widgets/custom_fa_icon.dart'; import 'package:sviva_visits/common/widgets/gap.dart'; import 'package:sviva_visits/core/app_config/app_config.dart'; import 'package:sviva_visits/core/app_config/app_font_awesome_icons.dart'; import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart' as vsc;
class AppTextEditor extends StatefulWidget { final ValueSetter onTextChanged;
final ValueSetter? onCharCountUpdate;
final int? plainCharLimit;
final Function()? onEditorCreated;
// final QuillEditorController? controller; final bool isSmallEditor; final String? labelText; final double? height; final String? supportingText; final TextStyle? supportingTextStyle; final double? supportingTextPaddingTop; final double? supportingTextHeight; final Widget? supportingExtraWidget; final String? Function(String?)? validator; final String? error; final String? hintText; final bool isError; final String? initialValue; final bool isFieldsEnabled; final bool isLocked; final Function()? onFocusChange; final bool expands;
const AppTextEditor({ super.key, required this.onTextChanged, this.onCharCountUpdate, this.plainCharLimit, this.onEditorCreated, // this.controller, this.isSmallEditor = false, this.labelText, this.height, this.supportingTextPaddingTop, this.supportingText, this.supportingExtraWidget, this.supportingTextHeight, this.supportingTextStyle, this.error, this.validator, this.hintText, this.isError = false, this.initialValue, this.isFieldsEnabled = true, this.isLocked = false, this.onFocusChange, this.expands = false, });
@override State createState() => _AppTextEditorState();
}
class _AppTextEditorState extends State {
late QuillController controller;
late FocusNode focusNode;
@override void initState() { super.initState(); controller = QuillController.basic(); focusNode = FocusNode(); WidgetsBinding.instance.addPostFrameCallback((_) { final html = controller.document.toDelta().toHtml( options: ConverterOptions( converterOptions: vsc.OpConverterOptions( inlineStylesFlag: true, allowBackgroundClasses: true), ), ); final plainTextLength = controller.document .getPlainText(0, controller.document.length - 1) .replaceAll("\n", "") .length; widget.onCharCountUpdate?.call(plainTextLength); widget.onTextChanged(html); }); setupInitialText(); }
void setupInitialText() { final initialData = widget.initialValue ?? ''; if (initialData.isNotEmpty && initialData != "
") { try { controller.document = Document.fromHtml(initialData); } catch (e) { final decodedJson = jsonDecode('{"insert":"$initialData\n"}'); controller.document = Document.fromJson([decodedJson]); } } controller.document.changes.listen((event) { if (event.change.length > 0) { WidgetsBinding.instance.addPostFrameCallback((_) { final plainTextLength = controller.document .getPlainText(0, controller.document.length - 1) .replaceAll("\n", "") .length; final charLimit = widget.plainCharLimit; if (charLimit != null && plainTextLength > charLimit) { controller.undo(); return; } else { widget.onTextChanged(controller.document.toDelta().toHtml( options: ConverterOptions( converterOptions: vsc.OpConverterOptions( inlineStylesFlag: true, allowBackgroundClasses: true), ), )); widget.onCharCountUpdate?.call(plainTextLength); } }); } }); }// @override // void didUpdateWidget(covariant AppTextEditor oldWidget) { // super.didUpdateWidget(oldWidget); // if (widget.initialValue != oldWidget.initialValue) { // setupInitialText(); // } // }
@override Widget build(BuildContext context) { return ConditionalParent( parentBuilder: (Widget child) { return IgnorePointer( child: Stack( alignment: Alignment.center, children: [ child, const Positioned( left: 20, child: Padding( padding: EdgeInsets.only(top: 20.0), child: CustomFaIcon( iconUnicode: AppFontAwesomeIcons.penSlashUnicode, fontSize: 16, color: AppColors.hintColor, ), ), ), ], ), ); }, condition: !widget.isFieldsEnabled, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (widget.labelText != null) Padding( padding: const EdgeInsets.symmetric(horizontal: 14), child: Text.rich( TextSpan(children:[
if (widget.validator != null)
const WidgetSpan(
child: Text(
'*',
style: TextStyle(
color: Colors.red,
fontSize: 14,
),
),
),
const WidgetSpan(child: Gap.widthW(3)),
WidgetSpan(
child: Text(
widget.labelText!,
style: const TextStyle(
fontSize: 14,
color: AppColors.primaryBlack,
fontWeight: FontWeight.w400,
),
),
),
]),
),
),
Container(
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: widget.error != null
? AppColors.errorColor
: AppColors.gray,
),
borderRadius: BorderRadius.circular(8),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
showAlignmentButtons: true,
showDirection: true,
controller: controller,
sharedConfigurations: const QuillSharedConfigurations(
locale: Locale('he')))),
GestureDetector(
onTap: () {
focusNode.requestFocus();
},
child: Container(
color: widget.isLocked
? AppColors.disabledInputGray
: Colors.white,
height: widget.expands
? null
: widget.isSmallEditor
? 100
: widget.height ?? 300,
padding: const EdgeInsets.all(20),
child: FocusScope(
onFocusChange: (isFocusedChanged) {
if (widget.onFocusChange != null &&
focusNode.hasPrimaryFocus) {
widget.onFocusChange!();
}
},
child: QuillEditor.basic(
focusNode: focusNode,
configurations: QuillEditorConfigurations(
placeholder: widget.hintText,
controller: controller,
customStyles: DefaultStyles(
placeHolder: DefaultTextBlockStyle(
AppTextStyles.mainNormalHintTextStyle(
fontSize: 25.sp(context),
color: AppColors.hintColor
.withOpacity(0.7),
context),
VerticalSpacing(0, 0),
VerticalSpacing(0, 0),
null)),
onTapOutside: (p, _) => focusNode.unfocus(),
minHeight: widget.expands
? (widget.isSmallEditor
? 100
: widget.height ?? 300)
: null,
// expands: widget.expands,
sharedConfigurations:
const QuillSharedConfigurations(
locale: Locale('he')))),
)),
)
],
),
),
),
if (widget.isSmallEditor || widget.error != null)
SizedBox(
height: widget.supportingTextHeight ?? 20.h(context),
child: widget.supportingText == null &&
widget.error == null &&
widget.validator == null
? SizedBox(
height: 16.h(context),
)
: Padding(
padding: EdgeInsets.only(
left: calcWidth(16, context),
right: calcWidth(16, context),
top: widget.supportingTextPaddingTop ?? 0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.validator != null
? widget.error ??
getError(widget.initialValue ?? '') ??
''
: widget.error ?? widget.supportingText ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style:
widget.error != null || widget.validator != null
? AppTextStyles.errorTextStyle(context)
: widget.supportingTextStyle ??
AppTextStyles.mainSupportingTextStyle(
context),
),
if (widget.supportingExtraWidget != null)
widget.supportingExtraWidget!,
],
),
),
),
],
),
);
}
String? getError(String content) { return (widget.validator != null && widget.isError ? widget.validator!(content) : null); } } `