diff --git a/CHANGELOG.md b/CHANGELOG.md index 2665f12..6c29809 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +### 2.1.9 +- Added a customisable option to make toolbar scrollable horizontally or vertically +- Fixed `onFocusChanged` method not returning focus on iOS properly +- Fixed text-change method is not returning text properly on Android +- Fixed moving cursor to end after `setDelta` +- Fix the white space in theme dark mode after adding minHeight property, thanks to `cabbagelol` for PR +- Fix Mobile Web- Clicking on toolbar icon does not toggle highlight color, and keyboard loses focus +- Updated `webview_flutter_android`, `webview_flutter_wkwebview` to latest versions +- Fix customToolBarList: ToolBarStyle.background adds table items #63 +- Improved `onEditorCreated` callback functionality + ### 2.1.8 - Added `setDelta` & `getDelta` methods, - Fixed bug:getText() returns empty string if QuillEditor contains only or '); + setHtmlText('This text is set by you 🫵'); }), textButton( text: 'Get Text', diff --git a/example/pubspec.lock b/example/pubspec.lock index 0f8f19e..19f23ad 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -238,7 +238,7 @@ packages: path: ".." relative: true source: path - version: "2.1.7" + version: "2.1.9" sky_engine: dependency: transitive description: flutter @@ -328,10 +328,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: "9e223788e1954087dac30d813dc151f8e12f09f1139f116ce20b5658893f3627" + sha256: "5906c9aa8c88ed372b2ad3c88c942790b4fb16f73fdd1c0647b4d747d9cf5b3f" url: "https://pub.dev" source: hosted - version: "3.4.4" + version: "3.4.5" webview_flutter_platform_interface: dependency: transitive description: @@ -344,10 +344,10 @@ packages: dependency: transitive description: name: webview_flutter_wkwebview - sha256: d601aba11ad8d4481e17a34a76fa1d30dee92dcbbe2c58b0df3120e9453099c7 + sha256: "9a78d963cce191dd6a9df547301fc5c008bf3dae95a323ec281fff1284e0a037" url: "https://pub.dev" source: hosted - version: "3.2.3" + version: "3.2.4" win32: dependency: transitive description: diff --git a/lib/src/quill_editor_wrapper.dart b/lib/src/quill_editor_wrapper.dart index b309316..a65781f 100644 --- a/lib/src/quill_editor_wrapper.dart +++ b/lib/src/quill_editor_wrapper.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:quill_html_editor/quill_html_editor.dart'; import 'package:quill_html_editor/src/utils/hex_color.dart'; import 'package:quill_html_editor/src/utils/string_util.dart'; @@ -124,26 +125,46 @@ class QuillHtmlEditorState extends State { bool isEnabled = true; late double _currentHeight; - + late StreamSubscription _keyboardSubscription; + bool _isKeyboardVisible = false; + bool _hasFocus = false; @override void initState() { isEnabled = widget.isEnabled; _currentHeight = widget.minHeight; + + var keyboardVisibilityController = KeyboardVisibilityController(); + + _keyboardSubscription = + keyboardVisibilityController.onChange.listen((bool visible) { + _isKeyboardVisible = visible; + if (!kIsWeb) { + if (!_isKeyboardVisible) { + _unFocus(); + } + if (widget.onFocusChanged != null) { + widget.onFocusChanged!(_hasFocus && _isKeyboardVisible); + } + } + }); super.initState(); } @override void dispose() { + _keyboardSubscription.cancel(); _webviewController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraints) { - _initialContent = _getQuillPage(width: constraints.maxWidth); - return _buildEditorView(context: context, width: constraints.maxWidth); - }); + return KeyboardVisibilityProvider( + child: LayoutBuilder(builder: (context, constraints) { + _initialContent = _getQuillPage(width: constraints.maxWidth); + return _buildEditorView(context: context, width: constraints.maxWidth); + }), + ); } Widget _buildEditorView( @@ -163,6 +184,9 @@ class QuillHtmlEditorState extends State { if (widget.text != null) { _setHtmlTextToEditor(htmlText: widget.text!); } + if (widget.onEditorCreated != null) { + widget.onEditorCreated!(); + } }); }, dartCallBacks: { @@ -200,6 +224,12 @@ class QuillHtmlEditorState extends State { DartCallback( name: 'OnTextChanged', callBack: (map) { + var tempText = ""; + if (tempText == map) { + return; + } else { + tempText = map; + } try { if (widget.controller._changeController != null) { String finalText = ""; @@ -224,26 +254,29 @@ class QuillHtmlEditorState extends State { DartCallback( name: 'FocusChanged', callBack: (map) { - if (widget.onFocusChanged != null) { - widget.onFocusChanged!(map?.toString() == 'true'); + _hasFocus = map?.toString() == 'true'; + if (kIsWeb) { + if (widget.onFocusChanged != null) { + widget.onFocusChanged!(_hasFocus); + } } }), DartCallback( name: 'OnSelectionChanged', callBack: (selection) { if (widget.onSelectionChanged != null) { + if (!_hasFocus) { + if (widget.onFocusChanged != null) { + _hasFocus = true; + widget.onFocusChanged!(_hasFocus); + } + } widget.onSelectionChanged!(selection != null ? SelectionModel.fromJson(jsonDecode(selection)) : SelectionModel(index: 0, length: 0)); } }), - DartCallback( - name: 'EditorLoaded', - callBack: (map) { - if (widget.onEditorCreated != null) { - widget.onEditorCreated!(); - } - }), + DartCallback(name: 'EditorLoaded', callBack: (map) {}), }, webSpecificParams: const WebSpecificParams( printDebugInfo: false, @@ -393,11 +426,14 @@ class QuillHtmlEditorState extends State { @@ -479,8 +520,8 @@ class QuillHtmlEditorState extends State {
-
-
+
+
@@ -591,6 +632,35 @@ class QuillHtmlEditorState extends State { console.log(e); } } + + /// observer to listen to the editor div changes + // select the target node + var target = document.querySelector('#editor'); + + // create an observer instance + var tempText = ""; + var observer = new MutationObserver(function(mutations) { + var text = quilleditor.root.innerHTML; + if(text != tempText){ + tempText = text; + if($kIsWeb) { + OnTextChanged(text); + } else { + OnTextChanged.postMessage(text); + } + onRangeChanged(); + quilleditor.focus(); + } + }); + + // configuration of the observer: + var config = { attributes: true, childList: true, characterData: true, subtree: true }; + + // pass in the target node, as well as the observer options + observer.observe(target, config); + + // stops the listener + // // observer.disconnect(); const Inline = Quill.import('blots/inline'); class RequirementBlot extends Inline {} @@ -606,6 +676,7 @@ class QuillHtmlEditorState extends State { var quilleditor = new Quill('#editor', { modules: { toolbar: '#toolbar-container', + matchVisual: false, table: true, history: { delay: 2000, @@ -644,21 +715,9 @@ class QuillHtmlEditorState extends State { OnSelectionChanged(getSelectionRange()); }else{ OnSelectionChanged.postMessage(getSelectionRange()); - } + } }); - - quilleditor.on('text-change', function(eventName, ...args) { - - // console.log('text changed'); - - if($kIsWeb) { - OnTextChanged(quilleditor.root.innerHTML); - } else { - OnTextChanged.postMessage(quilleditor.root.innerHTML); - } - onRangeChanged(); - }); - + function onRangeChanged() { try { var range = quilleditor.getSelection(true); @@ -724,16 +783,15 @@ class QuillHtmlEditorState extends State { quilleditor.root.addEventListener("blur", function() { if($kIsWeb) { FocusChanged(false); - } else { - FocusChanged.postMessage(false); - } + } }); quilleditor.root.addEventListener("focus", function() { if($kIsWeb) { FocusChanged(true); } else { - FocusChanged.postMessage(true); + var focus = quilleditor.hasFocus(); + FocusChanged.postMessage(focus); } }); @@ -798,7 +856,8 @@ class QuillHtmlEditorState extends State { } function setHtmlText(htmlString) { - quilleditor.clipboard.dangerouslyPasteHTML(htmlString); + quilleditor.clipboard.dangerouslyPasteHTML(htmlString); + setTimeout(() => quilleditor.setSelection(quilleditor.getSelection().index + 100, 0), 10); return ''; } @@ -806,12 +865,20 @@ class QuillHtmlEditorState extends State { try{ const obj = JSON.parse(deltaMap); quilleditor.setContents(obj); + setTimeout(() => quilleditor.setSelection(quilleditor.getSelection().index + 100, 0), 10); }catch(e){ console.log(e); } return ''; } + function scrollToView(){ + var target = document.querySelector('#editor'); + target.scrollIntoView(false); + return ''; + } + + function getDelta() { return JSON.stringify(quilleditor.getContents()); } diff --git a/lib/src/tool_bar.dart b/lib/src/tool_bar.dart index 979b4ec..4786eb3 100644 --- a/lib/src/tool_bar.dart +++ b/lib/src/tool_bar.dart @@ -120,7 +120,7 @@ class ToolBar extends StatefulWidget { /// relative to each other in the main axis. /// * [runAlignment], which controls how the runs are placed relative to each /// other in the cross axis. - final WrapCrossAlignment crossAxisAlignment; + final dynamic crossAxisAlignment; /// Determines the order to lay children out horizontally and how to interpret /// `start` and `end` in the horizontal direction. @@ -179,7 +179,78 @@ class ToolBar extends StatefulWidget { /// Defaults to [Clip.none]. final Clip clipBehavior; - ///[ToolBar] widget to show the quill toolbar + /// How the children should be placed along the main axis. + /// + /// For example, [MainAxisAlignment.start], the default, places the children + /// at the start (i.e., the left for a [Row] or the top for a [Column]) of the + /// main axis. + final MainAxisAlignment? mainAxisAlignment; + + /// How much space should be occupied in the main axis. + /// + /// After allocating space to children, there might be some remaining free + /// space. This value controls whether to maximize or minimize the amount of + /// free space, subject to the incoming layout constraints. + /// + /// If some children have a non-zero flex factors (and none have a fit of + /// [FlexFit.loose]), they will expand to consume all the available space and + /// there will be no remaining free space to maximize or minimize, making this + /// value irrelevant to the final layout. + final MainAxisSize? mainAxisSize; + + /// Determines the order to lay children out horizontally and how to interpret + /// `start` and `end` in the horizontal direction. + /// + /// Defaults to the ambient [Directionality]. + /// + /// If [textDirection] is [TextDirection.rtl], then the direction in which + /// text flows starts from right to left. Otherwise, if [textDirection] is + /// [TextDirection.ltr], then the direction in which text flows starts from + /// left to right. + /// + /// If the [direction] is [Axis.horizontal], this controls the order in which + /// the children are positioned (left-to-right or right-to-left), and the + /// meaning of the [mainAxisAlignment] property's [MainAxisAlignment.start] and + /// [MainAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.horizontal], and either the + /// [mainAxisAlignment] is either [MainAxisAlignment.start] or + /// [MainAxisAlignment.end], or there's more than one child, then the + /// [textDirection] (or the ambient [`Directionality]) must not be null. + /// + /// If the [direction] is [Axis.vertical], this controls the meaning of the + /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and + /// [CrossAxisAlignment.end] values. + /// + /// Determines the order to lay children out vertically and how to interpret + /// `start` and `end` in the vertical direction. + /// + /// Defaults to [VerticalDirection.down]. + /// + /// If the [direction] is [Axis.vertical], this controls which order children + /// are painted in (down or up), the meaning of the [mainAxisAlignment] + /// property's [MainAxisAlignment.start] and [MainAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.vertical], and either the [mainAxisAlignment] + /// is either [MainAxisAlignment.start] or [MainAxisAlignment.end], or there's + /// more than one child, then the [verticalDirection] must not be null. + /// + /// If the [direction] is [Axis.horizontal], this controls the meaning of the + /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and + /// [CrossAxisAlignment.end] values. + /// + /// If aligning items according to their baseline, which baseline to use. + /// + /// This must be set if using baseline alignment. There is no default because there is no + /// way for the framework to know the correct baseline _a priori_. + final TextBaseline? textBaseline; + + final bool? _isScrollable; + + ///[ToolBar] widget to show the quill + /// The toolbar items will be auto aligned based on the screen's width or height + /// The behaviour of the widget's alignment is similar to [Wrap] widget + ToolBar({ this.direction = Axis.horizontal, this.alignment = WrapAlignment.start, @@ -198,10 +269,47 @@ class ToolBar extends StatefulWidget { this.iconColor = Colors.black, this.activeIconColor = Colors.blue, this.toolBarColor = Colors.white, - }) : super( + this.mainAxisSize, + }) : assert(crossAxisAlignment is WrapCrossAlignment, + "Please pass WrapCrossAlignment, instead of CrossAxisAlignment"), + mainAxisAlignment = MainAxisAlignment.start, + textBaseline = TextBaseline.alphabetic, + _isScrollable = false, + super( key: controller.toolBarKey, ); + ///[ToolBar.scroll] shows the widget in a single row/column + ///Please define the [direction], to make it a row or a column + ///the direction defaults to [Axis.horizontal] + + ToolBar.scroll({ + this.direction = Axis.horizontal, + this.textDirection, + this.verticalDirection = VerticalDirection.down, + this.clipBehavior = Clip.none, + this.toolBarConfig, + required this.controller, + this.customButtons, + this.padding, + this.iconSize = 25, + this.iconColor = Colors.black, + this.activeIconColor = Colors.blue, + this.toolBarColor = Colors.white, + this.crossAxisAlignment = CrossAxisAlignment.start, + this.mainAxisAlignment = MainAxisAlignment.start, + this.mainAxisSize = MainAxisSize.min, + this.textBaseline = TextBaseline.alphabetic, + }) : assert(crossAxisAlignment is CrossAxisAlignment, + "Please pass CrossAxisAlignment, instead of WrapCrossAlignment"), + spacing = 0.0, + runSpacing = 0.0, + alignment = WrapAlignment.start, + runAlignment = WrapAlignment.start, + _isScrollable = true, + super( + key: controller.toolBarKey, + ); @override State createState() => ToolBarState(); } @@ -254,6 +362,25 @@ class ToolBarState extends State { @override Widget build(BuildContext context) { + if (widget._isScrollable!) { + return Container( + width: double.maxFinite, + decoration: BoxDecoration( + color: widget.toolBarColor, + ), + child: SingleChildScrollView( + scrollDirection: widget.direction, + child: Flex( + direction: widget.direction, + crossAxisAlignment: CrossAxisAlignment.start, + textDirection: widget.textDirection, + verticalDirection: widget.verticalDirection, + clipBehavior: widget.clipBehavior, + children: _generateToolBar(context), + ), + ), + ); + } return Container( width: double.maxFinite, decoration: BoxDecoration( @@ -397,6 +524,8 @@ class ToolBarState extends State { break; case ToolBarStyle.undo: case ToolBarStyle.redo: + case ToolBarStyle.editTable: + case ToolBarStyle.addTable: case ToolBarStyle.clearHistory: break; } @@ -475,15 +604,15 @@ class ToolBarState extends State { height: widget.iconSize, child: _getFontBackgroundColorWidget(i)), )); - + } else if (toolbarItem.style == ToolBarStyle.addTable) { tempToolBarList.add(Padding( padding: _buttonPadding, child: SizedBox( width: widget.iconSize, height: widget.iconSize, - child: _getTablePickerWidget(i)), + child: _getTablePickerWidget(i, context)), )); - + } else if (toolbarItem.style == ToolBarStyle.editTable) { tempToolBarList.add(EditTableDropDown( padding: _buttonPadding, iconColor: widget.iconColor!, @@ -611,6 +740,8 @@ class ToolBarState extends State { case ToolBarStyle.undo: case ToolBarStyle.redo: case ToolBarStyle.clearHistory: + case ToolBarStyle.editTable: + case ToolBarStyle.addTable: return {'format': 'undo', 'value': ''}; } } @@ -742,7 +873,6 @@ class ToolBarState extends State { _fontColorKey.currentState!.showOverlayOnTap(); } }, - position: ElTooltipPosition.bottomEnd, key: _fontColorKey, content: ColorPicker( onColorPicked: (color) { @@ -794,7 +924,6 @@ class ToolBarState extends State { Widget _getFontBackgroundColorWidget(int i) { return ElTooltip( - position: ElTooltipPosition.bottomEnd, onTap: () { if (_fontBgColorKey.currentState != null) { _fontBgColorKey.currentState!.showOverlayOnTap(); @@ -844,14 +973,17 @@ class ToolBarState extends State { ); } - Widget _getTablePickerWidget(int i) { + Widget _getTablePickerWidget(int i, BuildContext context) { return ElTooltip( color: widget.toolBarColor!, distance: 0, - position: ElTooltipPosition.bottomCenter, onTap: () { - if (_tablePickerKey.currentState != null) { - _tablePickerKey.currentState!.showOverlayOnTap(); + if (MediaQuery.of(context).size.width < 480) { + _showTablePickerDialog(context); + } else { + if (_tablePickerKey.currentState != null) { + _tablePickerKey.currentState!.showOverlayOnTap(); + } } }, key: _tablePickerKey, @@ -876,6 +1008,64 @@ class ToolBarState extends State { ), ); } + + void _showTablePickerDialog(BuildContext context) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + contentPadding: EdgeInsets.zero, + content: WebViewAware( + child: Builder( + builder: (context) { + return SizedBox( + width: 300, + height: 310, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + width: 15, + ), + const Expanded( + child: Text( + 'Select Rows x Columns', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.w600), + )), + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon(Icons.close)) + ], + ), + Expanded( + child: TablePicker( + rowCount: 8, + width: 300, + onTablePicked: (int row, int column) { + widget.controller.insertTable(row, column); + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox( + height: 10, + ) + ], + ), + ); + }, + ), + ), + ); + }); + } } ///[ToolBarItem] toolbaritem widget to show buttons based on style @@ -967,16 +1157,18 @@ class ToolBarItem extends StatelessWidget { return _getIconWidget(Icons.font_download_sharp); case ToolBarStyle.image: return _getIconWidget(Icons.image); - case ToolBarStyle.link: - case ToolBarStyle.video: - case ToolBarStyle.size: - return const SizedBox(); case ToolBarStyle.undo: return _getIconWidget(Icons.undo_sharp); case ToolBarStyle.redo: return _getIconWidget(Icons.redo_sharp); case ToolBarStyle.clearHistory: return _getIconWidget(Icons.layers_clear_sharp); + case ToolBarStyle.link: + case ToolBarStyle.video: + case ToolBarStyle.size: + case ToolBarStyle.addTable: + case ToolBarStyle.editTable: + return const SizedBox(); } } @@ -1105,7 +1297,13 @@ enum ToolBarStyle { redo, /// [clearHistory] to undo the editor change - clearHistory + clearHistory, + + /// [addTable] to add table to the editor + addTable, + + /// [editTable] to edit rows, columns or delete table + editTable, ///font - later releases } diff --git a/lib/src/widgets/edit_table_drop_down.dart b/lib/src/widgets/edit_table_drop_down.dart index 6ede4e6..c81233f 100644 --- a/lib/src/widgets/edit_table_drop_down.dart +++ b/lib/src/widgets/edit_table_drop_down.dart @@ -55,23 +55,17 @@ class _EditTableDropDownState extends State { child: ElTooltip( color: widget.dropDownColor, distance: 0, - position: ElTooltipPosition.bottomEnd, onTap: () { - if (_editTableETKey.currentState != null) { - _editTableETKey.currentState!.showOverlayOnTap(); + if (MediaQuery.of(context).size.width < 480) { + _showEditTableSheet(true, context); + } else { + if (_editTableETKey.currentState != null) { + _editTableETKey.currentState!.showOverlayOnTap(); + } } }, key: _editTableETKey, - content: SizedBox( - width: 180, - child: ListView.builder( - shrinkWrap: true, - itemCount: EditTableEnum.values.length, - itemBuilder: (context, i) { - return _getEditTableItem(EditTableEnum.values.toList()[i]); - }, - ), - ), + content: _editTableListView(false, context), child: SizedBox( width: widget.iconSize, height: widget.iconSize, @@ -85,7 +79,8 @@ class _EditTableDropDownState extends State { ); } - Widget _getEditTableItem(EditTableEnum type) { + Widget _getEditTableItem( + EditTableEnum type, BuildContext context, bool isMobile) { String value = ""; String imagePath = ImageConstant.kiInsertRowBelowPng; @@ -129,6 +124,7 @@ class _EditTableDropDownState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( width: widget.iconSize, @@ -137,14 +133,14 @@ class _EditTableDropDownState extends State { imagePath, color: widget.iconColor, )), + const SizedBox( + width: 8, + ), Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - value, - style: TextStyle( - fontWeight: FontWeight.w500, color: widget.iconColor), - ), + child: Text( + value, + style: TextStyle( + fontWeight: FontWeight.w500, color: widget.iconColor), ), ), ], @@ -152,14 +148,112 @@ class _EditTableDropDownState extends State { ), onTap: () { widget.onOptionSelected(type); - if (_editTableETKey.currentState != null) { - _editTableETKey.currentState!.hideOverlay(); + if (isMobile) { + Navigator.of(context).pop(); + } else { + if (_editTableETKey.currentState != null) { + _editTableETKey.currentState!.hideOverlay(); + } } }, ), ), ); } + + void _showEditTableSheet(bool isMobileView, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return _editTableListView(isMobileView, context); + }); + } + + Widget _editTableListView(bool isMobileView, BuildContext context) { + if (isMobileView) { + return AlertDialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + contentPadding: EdgeInsets.zero, + content: WebViewAware( + child: Builder(builder: (context) { + return SizedBox( + width: 400, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: const [ + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 8.0), + child: Text( + 'Edit Table', + style: TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + ), + CloseButton(), + ], + ), + Flexible( + fit: FlexFit.loose, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: MediaQuery.of(context).size.width < 380 + ? ListView.builder( + shrinkWrap: true, + itemCount: EditTableEnum.values.length, + itemBuilder: (context, i) { + return _getEditTableItem( + EditTableEnum.values.toList()[i], + context, + isMobileView); + }) + : GridView.builder( + shrinkWrap: true, + itemCount: EditTableEnum.values.length, + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: + MediaQuery.of(context).size.width < + 380 + ? 1 + : 2, + childAspectRatio: 1 / .3), + itemBuilder: (context, i) { + return _getEditTableItem( + EditTableEnum.values.toList()[i], + context, + isMobileView); + }), + ), + ), + const SizedBox( + height: 10, + ) + ], + ), + ); + }), + )); + } + + return SizedBox( + width: 200, + height: MediaQuery.of(context).size.width < 450 ? 350 : null, + child: ListView.builder( + reverse: true, + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: EditTableEnum.values.length, + itemBuilder: (context, i) { + return _getEditTableItem( + EditTableEnum.values.toList()[i], context, isMobileView); + }, + ), + ); + } } ///[EditTableEnum] enum options for edit table dropdown diff --git a/lib/src/widgets/input_url_widget.dart b/lib/src/widgets/input_url_widget.dart index 26131cd..6a46138 100644 --- a/lib/src/widgets/input_url_widget.dart +++ b/lib/src/widgets/input_url_widget.dart @@ -85,7 +85,7 @@ class _InputUrlWidgetState extends State { return InkWell( onTap: () async { await widget.controller.getSelectionRange().then((selectionModel) { - showBottomSheet( + showModalBottomSheet( context: context, builder: (context) { return _getTextFieldBytType(false, onDoneLastClicked, diff --git a/lib/src/widgets/table_picker.dart b/lib/src/widgets/table_picker.dart index 8b0b49a..bdfe7dc 100644 --- a/lib/src/widgets/table_picker.dart +++ b/lib/src/widgets/table_picker.dart @@ -6,7 +6,10 @@ import 'package:quill_html_editor/src/widgets/webviewx/src/webviewx_plus.dart'; class TablePicker extends StatefulWidget { ///[TablePicker] a widget to interactively selected the number of rows and columns to insert in editor const TablePicker( - {super.key, this.rowCount = 6, required this.onTablePicked}); + {super.key, + this.rowCount = 6, + required this.onTablePicked, + this.width = 200}); ///[onTablePicked] a callback function that returns the selected row and column index final Function(int row, int column) onTablePicked; @@ -14,6 +17,9 @@ class TablePicker extends StatefulWidget { ///[rowCount] to define the table row*column matrix final int? rowCount; + ///[width] to set the min width of the table picker + final double? width; + @override State createState() => _TablePickerState(); } @@ -69,41 +75,38 @@ class _TablePickerState extends State { @override Widget build(BuildContext context) { - return Listener( - onPointerDown: _detectTapedItem, - onPointerMove: _detectTapedItem, - onPointerUp: _onSelectionDone, - child: WebViewAware( - child: Container( - width: 200, - padding: const EdgeInsets.all(4), - child: GridView.builder( - key: _cellKey, - shrinkWrap: true, - itemCount: widget.rowCount! * widget.rowCount!, - scrollDirection: Axis.horizontal, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: widget.rowCount!), - itemBuilder: (context, index) { - return _CellSelectionWidget( - index: index, - child: Container( - margin: const EdgeInsets.all(2), - decoration: BoxDecoration( + return WebViewAware( + child: Listener( + onPointerDown: _detectTapedItem, + onPointerMove: _detectTapedItem, + onPointerUp: _onSelectionDone, + child: GridView.builder( + key: _cellKey, + shrinkWrap: true, + itemCount: widget.rowCount! * widget.rowCount!, + scrollDirection: Axis.horizontal, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 1, crossAxisCount: widget.rowCount!), + itemBuilder: (context, index) { + return _CellSelectionWidget( + index: index, + child: Container( + // width: widget.width! / widget.rowCount!, + margin: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: _selectedIndexes.contains(index) + ? Colors.lightBlue.shade50 + : Colors.transparent, + border: Border.all( + width: _selectedIndexes.contains(index) ? 2 : 1, color: _selectedIndexes.contains(index) - ? Colors.lightBlue.shade50 - : Colors.transparent, - border: Border.all( - width: _selectedIndexes.contains(index) ? 2 : 1, - color: _selectedIndexes.contains(index) - ? Colors.lightBlue.shade100 - : Colors.black45, - )), - ), - ); - }, - ), + ? Colors.lightBlue.shade100 + : Colors.black45, + )), + ), + ); + }, ), ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 683c724..8f4d334 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: quill_html_editor description: HTML rich text editor for Android, iOS, and Web, it is built with the powerful Quill Js library. Which is an open source WYSIWYG editor built for the modern web. -version: 2.1.8 +version: 2.1.9 homepage: https://github.com/the-airbender/quill_html_editor repository: https://github.com/the-airbender/quill_html_editor maintainer: Pavan Kumar Nagulavancha @@ -23,9 +23,9 @@ dependencies: pointer_interceptor: ^0.9.3+4 uuid: ^3.0.7 webview_flutter: ^4.0.7 - webview_flutter_android: ^3.4.4 + webview_flutter_android: ^3.4.5 webview_flutter_platform_interface: ^2.1.0 - webview_flutter_wkwebview: ^3.2.3 + webview_flutter_wkwebview: ^3.2.4 flutter_keyboard_visibility: ^5.4.0 dev_dependencies: