parent
f2aac2df29
commit
fbc633f01e
21 changed files with 767 additions and 321 deletions
@ -0,0 +1,21 @@ |
||||
import 'package:flutter/foundation.dart' show immutable; |
||||
|
||||
/// The configurations for the quill editor widget of flutter quill |
||||
@immutable |
||||
class QuillEditorConfigurations { |
||||
const QuillEditorConfigurations({ |
||||
this.placeholder, |
||||
this.readOnly = false, |
||||
}); |
||||
|
||||
/// The text placeholder in the quill editor |
||||
final String? placeholder; |
||||
|
||||
/// Whether the text can be changed. |
||||
/// |
||||
/// When this is set to `true`, the text cannot be modified |
||||
/// by any shortcut or keyboard operation. The text is still selectable. |
||||
/// |
||||
/// Defaults to `false`. Must not be `null`. |
||||
final bool readOnly; |
||||
} |
@ -0,0 +1,21 @@ |
||||
import 'package:flutter/material.dart' show Color, Colors, Locale; |
||||
import './editor/configurations.dart' show QuillEditorConfigurations; |
||||
import './toolbar/configurations.dart' show QuillToolbarConfigurations; |
||||
|
||||
/// The shared configurations between [QuillEditorConfigurations] and |
||||
/// [QuillToolbarConfigurations] so we don't duplicate things |
||||
class QuillSharedConfigurations { |
||||
const QuillSharedConfigurations({ |
||||
this.dialogBarrierColor = Colors.black54, |
||||
this.locale, |
||||
}); |
||||
|
||||
// This is just example or showcase of this major update to make the library |
||||
// more maintanable, flexible, and customizable |
||||
/// The barrier color of the shown dialogs |
||||
final Color dialogBarrierColor; |
||||
|
||||
/// The locale to use for the editor and toolbar, defaults to system locale |
||||
/// More https://github.com/singerdmx/flutter-quill#translation |
||||
final Locale? locale; |
||||
} |
@ -0,0 +1,48 @@ |
||||
import 'package:flutter/foundation.dart' show VoidCallback, immutable; |
||||
import 'package:flutter/widgets.dart' show IconData, Widget; |
||||
|
||||
import '../../../../../flutter_quill.dart' show QuillController, QuillProvider; |
||||
import '../../../themes/quill_icon_theme.dart' show QuillIconTheme; |
||||
import '../../quill_configurations.dart' show kDefaultIconSize; |
||||
|
||||
/// The [T] is the options for the button, usually should refresnce itself |
||||
/// it's used in [childBuilder] so the developer can custmize this when using it |
||||
/// The [I] is extra options for the button, usually for it's state |
||||
@immutable |
||||
class QuillToolbarBaseButtonOptions<T, I> { |
||||
const QuillToolbarBaseButtonOptions({ |
||||
this.iconData, |
||||
this.globalIconSize = kDefaultIconSize, |
||||
this.afterButtonPressed, |
||||
this.tooltip, |
||||
this.iconTheme, |
||||
this.childBuilder, |
||||
this.controller, |
||||
}); |
||||
|
||||
/// By default it will use a Icon data from Icons which comes from material |
||||
/// library, to change this, please pass a different value |
||||
/// If there is no Icon in this button then pass null in the child class |
||||
final IconData? iconData; |
||||
|
||||
/// To change the the icon size pass a different value, by default will be |
||||
/// [kDefaultIconSize] |
||||
/// this will be used for all the buttons but you can override this |
||||
final double globalIconSize; |
||||
|
||||
/// To do extra logic after pressing the button |
||||
final VoidCallback? afterButtonPressed; |
||||
|
||||
/// By default it will use the default tooltip which already localized |
||||
final String? tooltip; |
||||
|
||||
/// Use custom theme |
||||
final QuillIconTheme? iconTheme; |
||||
|
||||
/// If you want to dispaly a differnet widget based using a builder |
||||
final Widget Function(T options, I extraOptions)? childBuilder; |
||||
|
||||
/// By default it will be from the one in [QuillProvider] |
||||
/// To override it you must pass not null controller |
||||
final QuillController? controller; |
||||
} |
@ -0,0 +1,79 @@ |
||||
import 'package:flutter/foundation.dart' show immutable; |
||||
import 'package:flutter/material.dart' show Colors, PopupMenuEntry; |
||||
import 'package:flutter/widgets.dart' |
||||
show |
||||
Color, |
||||
ValueChanged, |
||||
EdgeInsetsGeometry, |
||||
TextStyle, |
||||
EdgeInsets, |
||||
TextOverflow; |
||||
|
||||
import '../../../../../flutter_quill.dart'; |
||||
|
||||
@immutable |
||||
class QuillToolbarFontFamilyButtonExtraOptions { |
||||
const QuillToolbarFontFamilyButtonExtraOptions({ |
||||
required this.defaultDisplayText, |
||||
required this.currentValue, |
||||
}); |
||||
final String defaultDisplayText; |
||||
final String currentValue; |
||||
} |
||||
|
||||
class QuillToolbarFontFamilyButtonOptions extends QuillToolbarBaseButtonOptions< |
||||
QuillToolbarFontFamilyButtonOptions, |
||||
QuillToolbarFontFamilyButtonExtraOptions> { |
||||
const QuillToolbarFontFamilyButtonOptions({ |
||||
required this.attribute, |
||||
this.rawItemsMap, |
||||
super.controller, |
||||
super.iconData, |
||||
super.afterButtonPressed, |
||||
super.tooltip, |
||||
super.iconTheme, |
||||
super.childBuilder, |
||||
this.onSelected, |
||||
this.padding, |
||||
this.style, |
||||
this.width, |
||||
this.initialValue, |
||||
this.labelOverflow = TextOverflow.visible, |
||||
this.overrideTooltipByFontFamily = false, |
||||
this.itemHeight, |
||||
this.itemPadding, |
||||
this.defaultItemColor = Colors.red, |
||||
this.renderFontFamilies = true, |
||||
@Deprecated('It is not required because of `rawItemsMap`') this.items, |
||||
this.highlightElevation = 1, |
||||
this.hoverElevation = 1, |
||||
this.fillColor, |
||||
this.iconSize, |
||||
}); |
||||
|
||||
final Color? fillColor; |
||||
final double hoverElevation; |
||||
final double highlightElevation; |
||||
@Deprecated('It is not required because of `rawItemsMap`') |
||||
final List<PopupMenuEntry<String>>? items; |
||||
|
||||
/// By default it will be [fontFamilyValues] from [QuillToolbarConfigurations] |
||||
/// You can override this if you want |
||||
final Map<String, String>? rawItemsMap; |
||||
final ValueChanged<String>? onSelected; |
||||
final Attribute attribute; |
||||
|
||||
final EdgeInsetsGeometry? padding; |
||||
final TextStyle? style; |
||||
final double? width; |
||||
final bool renderFontFamilies; |
||||
final String? initialValue; |
||||
final TextOverflow labelOverflow; |
||||
final bool overrideTooltipByFontFamily; |
||||
final double? itemHeight; |
||||
final EdgeInsets? itemPadding; |
||||
final Color? defaultItemColor; |
||||
|
||||
/// By default will use [globalIconSize] |
||||
final double? iconSize; |
||||
} |
@ -0,0 +1,39 @@ |
||||
import 'package:flutter/foundation.dart' show VoidCallback, immutable; |
||||
|
||||
import '../../../../../flutter_quill.dart'; |
||||
|
||||
@immutable |
||||
class HistoryButtonExtraOptions { |
||||
const HistoryButtonExtraOptions({ |
||||
required this.onPressed, |
||||
required this.canPressed, |
||||
}); |
||||
|
||||
/// When the button pressed |
||||
final VoidCallback onPressed; |
||||
|
||||
/// If it can redo or undo |
||||
final bool canPressed; |
||||
} |
||||
|
||||
@immutable |
||||
class QuillToolbarHistoryButtonOptions extends QuillToolbarBaseButtonOptions< |
||||
QuillToolbarHistoryButtonOptions, HistoryButtonExtraOptions> { |
||||
const QuillToolbarHistoryButtonOptions({ |
||||
required this.isUndo, |
||||
super.iconData, |
||||
super.controller, |
||||
super.iconTheme, |
||||
super.afterButtonPressed, |
||||
super.tooltip, |
||||
super.childBuilder, |
||||
this.iconSize, |
||||
}); |
||||
|
||||
/// If this true then it will be the undo button |
||||
/// otherwise it will be redo |
||||
final bool isUndo; |
||||
|
||||
/// By default will use [globalIconSize] |
||||
final double? iconSize; |
||||
} |
@ -0,0 +1,15 @@ |
||||
import 'package:flutter/foundation.dart' show immutable; |
||||
import 'package:flutter/widgets.dart' show IconData; |
||||
|
||||
import 'base.dart'; |
||||
|
||||
@immutable |
||||
class QuillToolbarToggleStyleButtonOptions |
||||
extends QuillToolbarBaseButtonOptions { |
||||
const QuillToolbarToggleStyleButtonOptions({ |
||||
required this.iconData, |
||||
}); |
||||
|
||||
@override |
||||
final IconData iconData; |
||||
} |
@ -0,0 +1,94 @@ |
||||
import 'package:flutter/foundation.dart' show immutable; |
||||
import 'package:flutter/material.dart' show Icons; |
||||
|
||||
import '../../documents/attribute.dart'; |
||||
import 'buttons/base.dart'; |
||||
import 'buttons/font_family.dart'; |
||||
import 'buttons/history.dart'; |
||||
|
||||
export './buttons/base.dart'; |
||||
export './buttons/history.dart'; |
||||
export './buttons/toggle_style.dart'; |
||||
|
||||
/// The default size of the icon of a button. |
||||
const double kDefaultIconSize = 18; |
||||
|
||||
/// The default size for the toolbar (width, height) |
||||
const double defaultToolbarSize = kDefaultIconSize * 2; |
||||
|
||||
/// The factor of how much larger the button is in relation to the icon. |
||||
const double kIconButtonFactor = 1.77; |
||||
|
||||
/// The horizontal margin between the contents of each toolbar section. |
||||
const double kToolbarSectionSpacing = 4; |
||||
|
||||
/// The configurations for the toolbar widget of flutter quill |
||||
@immutable |
||||
class QuillToolbarConfigurations { |
||||
const QuillToolbarConfigurations({ |
||||
this.buttonOptions = const QuillToolbarButtonOptions(), |
||||
this.multiRowsDisplay = true, |
||||
this.fontFamilyValues, |
||||
|
||||
/// By default it will calculated based on the [baseOptions] iconSize |
||||
/// You can change it but the the change only apply if |
||||
/// the [multiRowsDisplay] is false, if [multiRowsDisplay] then the value |
||||
/// will be [kDefaultIconSize] * 2 |
||||
double? toolbarSize, |
||||
}) : _toolbarSize = toolbarSize; |
||||
|
||||
final double? _toolbarSize; |
||||
|
||||
/// The toolbar size, by default it will be `baseButtonOptions.iconSize * 2` |
||||
double get toolbarSize { |
||||
final alternativeToolbarSize = _toolbarSize; |
||||
if (alternativeToolbarSize != null) { |
||||
return alternativeToolbarSize; |
||||
} |
||||
return buttonOptions.baseButtonOptions.globalIconSize * 2; |
||||
} |
||||
|
||||
/// If you want change spesefic buttons or all of them |
||||
/// then you came to the right place |
||||
final QuillToolbarButtonOptions buttonOptions; |
||||
final bool multiRowsDisplay; |
||||
|
||||
/// By default will be final |
||||
/// ``` |
||||
/// { |
||||
/// 'Sans Serif': 'sans-serif', |
||||
/// 'Serif': 'serif', |
||||
/// 'Monospace': 'monospace', |
||||
/// 'Ibarra Real Nova': 'ibarra-real-nova', |
||||
/// 'SquarePeg': 'square-peg', |
||||
/// 'Nunito': 'nunito', |
||||
/// 'Pacifico': 'pacifico', |
||||
/// 'Roboto Mono': 'roboto-mono', |
||||
/// 'Clear'.i18n: 'Clear' |
||||
/// }; |
||||
/// ``` |
||||
final Map<String, String>? fontFamilyValues; |
||||
} |
||||
|
||||
/// The configurations for the buttons of the toolbar widget of flutter quill |
||||
@immutable |
||||
class QuillToolbarButtonOptions { |
||||
const QuillToolbarButtonOptions({ |
||||
this.baseButtonOptions = const QuillToolbarBaseButtonOptions(), |
||||
this.undoHistoryButtonOptions = const QuillToolbarHistoryButtonOptions( |
||||
isUndo: true, |
||||
), |
||||
this.redoHistoryButtonOptions = const QuillToolbarHistoryButtonOptions( |
||||
isUndo: false, |
||||
), |
||||
this.fontFamilyButtonOptions = const QuillToolbarFontFamilyButtonOptions( |
||||
attribute: Attribute.font, |
||||
), |
||||
}); |
||||
|
||||
/// The base configurations for all the buttons |
||||
final QuillToolbarBaseButtonOptions baseButtonOptions; |
||||
final QuillToolbarHistoryButtonOptions undoHistoryButtonOptions; |
||||
final QuillToolbarHistoryButtonOptions redoHistoryButtonOptions; |
||||
final QuillToolbarFontFamilyButtonOptions fontFamilyButtonOptions; |
||||
} |
@ -0,0 +1,17 @@ |
||||
import 'package:flutter/widgets.dart' show BuildContext; |
||||
|
||||
import '../../../flutter_quill.dart' show QuillController, QuillProvider; |
||||
import 'build_context.dart'; |
||||
|
||||
extension QuillControllerExt on QuillController? { |
||||
/// Simple logic to use the current passed controller if not null |
||||
/// if null then we will have to use the default one from [QuillProvider] |
||||
/// using the [context] |
||||
QuillController notNull(BuildContext context) { |
||||
final controller = this; |
||||
if (controller != null) { |
||||
return controller; |
||||
} |
||||
return context.requireQuillController; |
||||
} |
||||
} |
@ -1,89 +1,138 @@ |
||||
import 'package:flutter/material.dart'; |
||||
|
||||
import '../../models/themes/quill_icon_theme.dart'; |
||||
import '../../../translations.dart'; |
||||
import '../../utils/extensions/build_context.dart'; |
||||
import '../../utils/extensions/quill_controller.dart'; |
||||
import '../controller.dart'; |
||||
import '../toolbar.dart'; |
||||
|
||||
class HistoryButton extends StatefulWidget { |
||||
const HistoryButton({ |
||||
required this.icon, |
||||
required this.controller, |
||||
required this.undo, |
||||
this.iconSize = kDefaultIconSize, |
||||
this.iconTheme, |
||||
this.afterButtonPressed, |
||||
this.tooltip, |
||||
Key? key, |
||||
}) : super(key: key); |
||||
|
||||
final IconData icon; |
||||
final double iconSize; |
||||
final bool undo; |
||||
final QuillController controller; |
||||
final QuillIconTheme? iconTheme; |
||||
final VoidCallback? afterButtonPressed; |
||||
final String? tooltip; |
||||
class QuillToolbarHistoryButton extends StatefulWidget { |
||||
const QuillToolbarHistoryButton({ |
||||
required this.options, |
||||
super.key, |
||||
}); |
||||
|
||||
final QuillToolbarHistoryButtonOptions options; |
||||
|
||||
@override |
||||
_HistoryButtonState createState() => _HistoryButtonState(); |
||||
_QuillToolbarHistoryButtonState createState() => |
||||
_QuillToolbarHistoryButtonState(); |
||||
} |
||||
|
||||
class _HistoryButtonState extends State<HistoryButton> { |
||||
Color? _iconColor; |
||||
class _QuillToolbarHistoryButtonState extends State<QuillToolbarHistoryButton> { |
||||
late ThemeData theme; |
||||
var _canPressed = false; |
||||
|
||||
QuillToolbarHistoryButtonOptions get options { |
||||
return widget.options; |
||||
} |
||||
|
||||
QuillController get controller { |
||||
return options.controller.notNull(context); |
||||
} |
||||
|
||||
@override |
||||
void initState() { |
||||
super.initState(); |
||||
_listenForChanges(); // Listen for changes and change it |
||||
} |
||||
|
||||
Future<void> _listenForChanges() async { |
||||
await Future.delayed(Duration.zero); // Wait for the widget to built |
||||
_updateCanPressed(); // Set the init state |
||||
|
||||
// Listen for changes and change it |
||||
controller.changes.listen((event) async { |
||||
_updateCanPressed(); |
||||
}); |
||||
} |
||||
|
||||
@override |
||||
Widget build(BuildContext context) { |
||||
theme = Theme.of(context); |
||||
_setIconColor(); |
||||
|
||||
final fillColor = |
||||
widget.iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; |
||||
widget.controller.changes.listen((event) async { |
||||
_setIconColor(); |
||||
}); |
||||
final baseButtonConfigurations = |
||||
context.requireQuillToolbarBaseButtonOptions; |
||||
final tooltip = options.tooltip ?? |
||||
baseButtonConfigurations.tooltip ?? |
||||
(options.isUndo ? 'Undo'.i18n : 'Redo'.i18n); |
||||
final iconData = options.iconData ?? |
||||
baseButtonConfigurations.iconData ?? |
||||
(options.isUndo ? Icons.undo_outlined : Icons.redo_outlined); |
||||
final childBuilder = |
||||
options.childBuilder ?? baseButtonConfigurations.childBuilder; |
||||
final iconSize = options.iconSize ?? |
||||
context.requireQuillToolbarBaseButtonOptions.globalIconSize; |
||||
final iconTheme = options.iconTheme ?? baseButtonConfigurations.iconTheme; |
||||
|
||||
final fillColor = iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; |
||||
|
||||
final afterButtonPressed = options.afterButtonPressed ?? |
||||
baseButtonConfigurations.afterButtonPressed; |
||||
|
||||
if (childBuilder != null) { |
||||
return childBuilder( |
||||
QuillToolbarHistoryButtonOptions( |
||||
isUndo: options.isUndo, |
||||
afterButtonPressed: afterButtonPressed, |
||||
controller: controller, |
||||
iconData: iconData, |
||||
iconSize: iconSize, |
||||
iconTheme: iconTheme, |
||||
tooltip: tooltip, |
||||
), |
||||
HistoryButtonExtraOptions( |
||||
onPressed: () { |
||||
_updateHistory(); |
||||
afterButtonPressed?.call(); |
||||
}, |
||||
canPressed: _canPressed, |
||||
), |
||||
); |
||||
} |
||||
return QuillIconButton( |
||||
tooltip: widget.tooltip, |
||||
tooltip: tooltip, |
||||
highlightElevation: 0, |
||||
hoverElevation: 0, |
||||
size: widget.iconSize * kIconButtonFactor, |
||||
icon: Icon(widget.icon, size: widget.iconSize, color: _iconColor), |
||||
size: iconSize * kIconButtonFactor, |
||||
icon: Icon( |
||||
iconData, |
||||
size: iconSize, |
||||
color: _canPressed |
||||
? iconTheme?.iconUnselectedColor ?? theme.iconTheme.color |
||||
: iconTheme?.disabledIconColor ?? theme.disabledColor, |
||||
), |
||||
fillColor: fillColor, |
||||
borderRadius: widget.iconTheme?.borderRadius ?? 2, |
||||
onPressed: _changeHistory, |
||||
afterPressed: widget.afterButtonPressed, |
||||
borderRadius: iconTheme?.borderRadius ?? 2, |
||||
onPressed: _updateHistory, |
||||
afterPressed: afterButtonPressed, |
||||
); |
||||
} |
||||
|
||||
void _setIconColor() { |
||||
void _updateCanPressed() { |
||||
if (!mounted) return; |
||||
|
||||
if (widget.undo) { |
||||
setState(() { |
||||
_iconColor = widget.controller.hasUndo |
||||
? widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color |
||||
: widget.iconTheme?.disabledIconColor ?? theme.disabledColor; |
||||
}); |
||||
} else { |
||||
setState(() { |
||||
_iconColor = widget.controller.hasRedo |
||||
? widget.iconTheme?.iconUnselectedColor ?? theme.iconTheme.color |
||||
: widget.iconTheme?.disabledIconColor ?? theme.disabledColor; |
||||
}); |
||||
} |
||||
setState(() { |
||||
if (options.isUndo) { |
||||
_canPressed = controller.hasUndo; |
||||
return; |
||||
} |
||||
_canPressed = controller.hasRedo; |
||||
}); |
||||
} |
||||
|
||||
void _changeHistory() { |
||||
if (widget.undo) { |
||||
if (widget.controller.hasUndo) { |
||||
widget.controller.undo(); |
||||
} |
||||
} else { |
||||
if (widget.controller.hasRedo) { |
||||
widget.controller.redo(); |
||||
void _updateHistory() { |
||||
if (options.isUndo) { |
||||
if (controller.hasUndo) { |
||||
controller.undo(); |
||||
} |
||||
// _updateCanPressed(); // We are already listeneting for the changes |
||||
return; |
||||
} |
||||
|
||||
_setIconColor(); |
||||
if (controller.hasRedo) { |
||||
controller.redo(); |
||||
// _updateCanPressed(); // We are already listeneting for the changes |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue