import 'dart:io'; import 'package:flutter/material.dart'; import 'package:i18n_extension/i18n_widget.dart'; import '../models/documents/attribute.dart'; import '../models/themes/quill_custom_icon.dart'; import '../models/themes/quill_dialog_theme.dart'; import '../models/themes/quill_icon_theme.dart'; import '../utils/font.dart'; import 'controller.dart'; import 'toolbar/arrow_indicated_button_list.dart'; import 'toolbar/camera_button.dart'; import 'toolbar/clear_format_button.dart'; import 'toolbar/color_button.dart'; import 'toolbar/history_button.dart'; import 'toolbar/image_button.dart'; import 'toolbar/image_video_utils.dart'; import 'toolbar/indent_button.dart'; import 'toolbar/link_style_button.dart'; import 'toolbar/quill_dropdown_button.dart'; import 'toolbar/quill_icon_button.dart'; import 'toolbar/select_alignment_button.dart'; import 'toolbar/select_header_style_button.dart'; import 'toolbar/toggle_check_list_button.dart'; import 'toolbar/toggle_style_button.dart'; import 'toolbar/video_button.dart'; export 'toolbar/clear_format_button.dart'; export 'toolbar/color_button.dart'; export 'toolbar/history_button.dart'; export 'toolbar/image_button.dart'; export 'toolbar/image_video_utils.dart'; export 'toolbar/indent_button.dart'; export 'toolbar/link_style_button.dart'; export 'toolbar/quill_dropdown_button.dart'; export 'toolbar/quill_icon_button.dart'; export 'toolbar/select_alignment_button.dart'; export 'toolbar/select_header_style_button.dart'; export 'toolbar/toggle_check_list_button.dart'; export 'toolbar/toggle_style_button.dart'; export 'toolbar/video_button.dart'; typedef OnImagePickCallback = Future Function(File file); typedef OnVideoPickCallback = Future Function(File file); typedef FilePickImpl = Future Function(BuildContext context); typedef WebImagePickImpl = Future Function( OnImagePickCallback onImagePickCallback); typedef WebVideoPickImpl = Future Function( OnVideoPickCallback onImagePickCallback); typedef MediaPickSettingSelector = Future Function( BuildContext context); // The default size of the icon of a button. const double kDefaultIconSize = 18; // The factor of how much larger the button is in relation to the icon. const double kIconButtonFactor = 1.77; class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { const QuillToolbar({ required this.children, this.toolbarHeight = 36, this.toolbarIconAlignment = WrapAlignment.center, this.toolbarSectionSpacing = 4, this.multiRowsDisplay = true, this.color, this.filePickImpl, this.customIcons = const [], this.locale, Key? key, }) : super(key: key); factory QuillToolbar.basic({ required QuillController controller, double toolbarIconSize = kDefaultIconSize, double toolbarSectionSpacing = 4, WrapAlignment toolbarIconAlignment = WrapAlignment.center, bool showDividers = true, bool showFontSize = true, bool showBoldButton = true, bool showItalicButton = true, bool showSmallButton = false, bool showUnderLineButton = true, bool showStrikeThrough = true, bool showInlineCode = true, bool showColorButton = true, bool showBackgroundColorButton = true, bool showClearFormat = true, bool showAlignmentButtons = false, bool showLeftAlignment = true, bool showCenterAlignment = true, bool showRightAlignment = true, bool showJustifyAlignment = true, bool showHeaderStyle = true, bool showListNumbers = true, bool showListBullets = true, bool showListCheck = true, bool showCodeBlock = true, bool showQuote = true, bool showIndent = true, bool showLink = true, bool showUndo = true, bool showRedo = true, bool multiRowsDisplay = true, bool showImageButton = true, bool showVideoButton = true, bool showCameraButton = true, bool showDirection = false, OnImagePickCallback? onImagePickCallback, OnVideoPickCallback? onVideoPickCallback, MediaPickSettingSelector? mediaPickSettingSelector, FilePickImpl? filePickImpl, WebImagePickImpl? webImagePickImpl, WebVideoPickImpl? webVideoPickImpl, List customIcons = const [], ///Map of font sizes in string Map? fontSizeValues, ///The theme to use for the icons in the toolbar, uses type [QuillIconTheme] QuillIconTheme? iconTheme, ///The theme to use for the theming of the [LinkDialog()], ///shown when embedding an image, for example QuillDialogTheme? dialogTheme, /// The locale to use for the editor toolbar, defaults to system locale /// More at https://github.com/singerdmx/flutter-quill#translation Locale? locale, Key? key, }) { final isButtonGroupShown = [ showFontSize || showBoldButton || showItalicButton || showSmallButton || showUnderLineButton || showStrikeThrough || showInlineCode || showColorButton || showBackgroundColorButton || showClearFormat || onImagePickCallback != null || onVideoPickCallback != null, showAlignmentButtons || showDirection, showLeftAlignment, showCenterAlignment, showRightAlignment, showJustifyAlignment, showHeaderStyle, showListNumbers || showListBullets || showListCheck || showCodeBlock, showQuote || showIndent, showLink ]; //default font size values final fontSizes = fontSizeValues ?? {'Small': 'small', 'Large': 'large', 'Huge': 'huge'}; return QuillToolbar( key: key, toolbarHeight: toolbarIconSize * 2, toolbarSectionSpacing: toolbarSectionSpacing, toolbarIconAlignment: toolbarIconAlignment, multiRowsDisplay: multiRowsDisplay, customIcons: customIcons, locale: locale, children: [ if (showUndo) HistoryButton( icon: Icons.undo_outlined, iconSize: toolbarIconSize, controller: controller, undo: true, iconTheme: iconTheme, ), if (showRedo) HistoryButton( icon: Icons.redo_outlined, iconSize: toolbarIconSize, controller: controller, undo: false, iconTheme: iconTheme, ), if (showFontSize) QuillDropdownButton( iconTheme: iconTheme, iconSize: toolbarIconSize, attribute: Attribute.size, controller: controller, items: [ for (MapEntry fontSize in fontSizes.entries) PopupMenuItem( key: ValueKey(fontSize.key), value: fontSize.value, child: Text(fontSize.key.toString()), ), ], onSelected: (newSize) { if (newSize != null) { controller.formatSelection( Attribute.fromKeyValue('size', getFontSize(newSize))); } }, rawItemsMap: fontSizes, ), if (showBoldButton) ToggleStyleButton( attribute: Attribute.bold, icon: Icons.format_bold, iconSize: toolbarIconSize, controller: controller, iconTheme: iconTheme, ), if (showItalicButton) ToggleStyleButton( attribute: Attribute.italic, icon: Icons.format_italic, iconSize: toolbarIconSize, controller: controller, iconTheme: iconTheme, ), if (showSmallButton) ToggleStyleButton( attribute: Attribute.small, icon: Icons.format_size, iconSize: toolbarIconSize, controller: controller, iconTheme: iconTheme, ), if (showUnderLineButton) ToggleStyleButton( attribute: Attribute.underline, icon: Icons.format_underline, iconSize: toolbarIconSize, controller: controller, iconTheme: iconTheme, ), if (showStrikeThrough) ToggleStyleButton( attribute: Attribute.strikeThrough, icon: Icons.format_strikethrough, iconSize: toolbarIconSize, controller: controller, iconTheme: iconTheme, ), if (showInlineCode) ToggleStyleButton( attribute: Attribute.inlineCode, icon: Icons.code, iconSize: toolbarIconSize, controller: controller, iconTheme: iconTheme, ), if (showColorButton) ColorButton( icon: Icons.color_lens, iconSize: toolbarIconSize, controller: controller, background: false, iconTheme: iconTheme, ), if (showBackgroundColorButton) ColorButton( icon: Icons.format_color_fill, iconSize: toolbarIconSize, controller: controller, background: true, iconTheme: iconTheme, ), if (showClearFormat) ClearFormatButton( icon: Icons.format_clear, iconSize: toolbarIconSize, controller: controller, iconTheme: iconTheme, ), if (showImageButton) ImageButton( icon: Icons.image, iconSize: toolbarIconSize, controller: controller, onImagePickCallback: onImagePickCallback, filePickImpl: filePickImpl, webImagePickImpl: webImagePickImpl, mediaPickSettingSelector: mediaPickSettingSelector, iconTheme: iconTheme, dialogTheme: dialogTheme, ), if (showVideoButton) VideoButton( icon: Icons.movie_creation, iconSize: toolbarIconSize, controller: controller, onVideoPickCallback: onVideoPickCallback, filePickImpl: filePickImpl, webVideoPickImpl: webImagePickImpl, mediaPickSettingSelector: mediaPickSettingSelector, iconTheme: iconTheme, dialogTheme: dialogTheme, ), if ((onImagePickCallback != null || onVideoPickCallback != null) && showCameraButton) CameraButton( icon: Icons.photo_camera, iconSize: toolbarIconSize, controller: controller, onImagePickCallback: onImagePickCallback, onVideoPickCallback: onVideoPickCallback, filePickImpl: filePickImpl, webImagePickImpl: webImagePickImpl, webVideoPickImpl: webVideoPickImpl, iconTheme: iconTheme, ), if (showDividers && isButtonGroupShown[0] && (isButtonGroupShown[1] || isButtonGroupShown[2] || isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) VerticalDivider( indent: 12, endIndent: 12, color: Colors.grey.shade400, ), if (showAlignmentButtons) SelectAlignmentButton( controller: controller, iconSize: toolbarIconSize, iconTheme: iconTheme, showLeftAlignment: showLeftAlignment, showCenterAlignment: showCenterAlignment, showRightAlignment: showRightAlignment, showJustifyAlignment: showJustifyAlignment, ), if (showDirection) ToggleStyleButton( attribute: Attribute.rtl, controller: controller, icon: Icons.format_textdirection_r_to_l, iconSize: toolbarIconSize, iconTheme: iconTheme, ), if (showDividers && isButtonGroupShown[1] && (isButtonGroupShown[2] || isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) VerticalDivider( indent: 12, endIndent: 12, color: Colors.grey.shade400, ), if (showHeaderStyle) SelectHeaderStyleButton( controller: controller, iconSize: toolbarIconSize, iconTheme: iconTheme, ), if (showDividers && showHeaderStyle && isButtonGroupShown[2] && (isButtonGroupShown[3] || isButtonGroupShown[4] || isButtonGroupShown[5])) VerticalDivider( indent: 12, endIndent: 12, color: Colors.grey.shade400, ), if (showListNumbers) ToggleStyleButton( attribute: Attribute.ol, controller: controller, icon: Icons.format_list_numbered, iconSize: toolbarIconSize, iconTheme: iconTheme, ), if (showListBullets) ToggleStyleButton( attribute: Attribute.ul, controller: controller, icon: Icons.format_list_bulleted, iconSize: toolbarIconSize, iconTheme: iconTheme, ), if (showListCheck) ToggleCheckListButton( attribute: Attribute.unchecked, controller: controller, icon: Icons.check_box, iconSize: toolbarIconSize, iconTheme: iconTheme, ), if (showCodeBlock) ToggleStyleButton( attribute: Attribute.codeBlock, controller: controller, icon: Icons.code, iconSize: toolbarIconSize, iconTheme: iconTheme, ), if (showDividers && isButtonGroupShown[3] && (isButtonGroupShown[4] || isButtonGroupShown[5])) VerticalDivider( indent: 12, endIndent: 12, color: Colors.grey.shade400, ), if (showQuote) ToggleStyleButton( attribute: Attribute.blockQuote, controller: controller, icon: Icons.format_quote, iconSize: toolbarIconSize, iconTheme: iconTheme, ), if (showIndent) IndentButton( icon: Icons.format_indent_increase, iconSize: toolbarIconSize, controller: controller, isIncrease: true, iconTheme: iconTheme, ), if (showIndent) IndentButton( icon: Icons.format_indent_decrease, iconSize: toolbarIconSize, controller: controller, isIncrease: false, iconTheme: iconTheme, ), if (showDividers && isButtonGroupShown[4] && isButtonGroupShown[5]) VerticalDivider( indent: 12, endIndent: 12, color: Colors.grey.shade400, ), if (showLink) LinkStyleButton( controller: controller, iconSize: toolbarIconSize, iconTheme: iconTheme, dialogTheme: dialogTheme, ), if (customIcons.isNotEmpty) if (showDividers) VerticalDivider( indent: 12, endIndent: 12, color: Colors.grey.shade400, ), for (var customIcon in customIcons) QuillIconButton( highlightElevation: 0, hoverElevation: 0, size: toolbarIconSize * kIconButtonFactor, icon: Icon(customIcon.icon, size: toolbarIconSize), borderRadius: iconTheme?.borderRadius ?? 2, onPressed: customIcon.onTap), ], ); } final List children; final double toolbarHeight; final double toolbarSectionSpacing; final WrapAlignment toolbarIconAlignment; final bool multiRowsDisplay; /// The color of the toolbar. /// /// Defaults to [ThemeData.canvasColor] of the current [Theme] if no color /// is given. final Color? color; final FilePickImpl? filePickImpl; /// The locale to use for the editor toolbar, defaults to system locale /// More https://github.com/singerdmx/flutter-quill#translation final Locale? locale; /// List of custom icons final List customIcons; @override Size get preferredSize => Size.fromHeight(toolbarHeight); @override Widget build(BuildContext context) { return I18n( initialLocale: locale, child: multiRowsDisplay ? Wrap( alignment: toolbarIconAlignment, runSpacing: 4, spacing: toolbarSectionSpacing, children: children, ) : Container( constraints: BoxConstraints.tightFor(height: preferredSize.height), color: color ?? Theme.of(context).canvasColor, child: ArrowIndicatedButtonList(buttons: children), ), ); } }