|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
|
|
|
import '../models/documents/attribute.dart';
|
|
|
|
import '../utils/media_pick_setting.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/indent_button.dart';
|
|
|
|
import 'toolbar/insert_embed_button.dart';
|
|
|
|
import 'toolbar/link_style_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 '../utils/media_pick_setting.dart';
|
|
|
|
export 'toolbar/clear_format_button.dart';
|
|
|
|
export 'toolbar/color_button.dart';
|
|
|
|
export 'toolbar/history_button.dart';
|
|
|
|
export 'toolbar/image_button.dart';
|
|
|
|
export 'toolbar/indent_button.dart';
|
|
|
|
export 'toolbar/insert_embed_button.dart';
|
|
|
|
export 'toolbar/link_style_button.dart';
|
|
|
|
export 'toolbar/quill_dropdown_button.dart';
|
|
|
|
export 'toolbar/quill_icon_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<String?> Function(File file);
|
|
|
|
typedef OnVideoPickCallback = Future<String?> Function(File file);
|
|
|
|
typedef FilePickImpl = Future<String?> Function(BuildContext context);
|
|
|
|
typedef WebImagePickImpl = Future<String?> Function(
|
|
|
|
OnImagePickCallback onImagePickCallback);
|
|
|
|
typedef WebVideoPickImpl = Future<String?> Function(
|
|
|
|
OnVideoPickCallback onImagePickCallback);
|
|
|
|
typedef MediaPickSettingSelector = Future<MediaPickSetting?> 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.color,
|
|
|
|
this.filePickImpl,
|
|
|
|
this.multiRowsDisplay,
|
|
|
|
Key? key,
|
|
|
|
}) : super(key: key);
|
|
|
|
|
|
|
|
factory QuillToolbar.basic({
|
|
|
|
required QuillController controller,
|
|
|
|
double toolbarIconSize = kDefaultIconSize,
|
|
|
|
bool showBoldButton = true,
|
|
|
|
bool showItalicButton = true,
|
|
|
|
bool showSmallButton = false,
|
|
|
|
bool showUnderLineButton = true,
|
|
|
|
bool showStrikeThrough = true,
|
|
|
|
bool showColorButton = true,
|
|
|
|
bool showBackgroundColorButton = true,
|
|
|
|
bool showClearFormat = 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 showHistory = true,
|
|
|
|
bool showHorizontalRule = false,
|
|
|
|
bool multiRowsDisplay = true,
|
|
|
|
bool showImageButton = true,
|
|
|
|
bool showVideoButton = true,
|
|
|
|
bool showCameraButton = true,
|
|
|
|
OnImagePickCallback? onImagePickCallback,
|
|
|
|
OnVideoPickCallback? onVideoPickCallback,
|
|
|
|
MediaPickSettingSelector? mediaPickSettingSelector,
|
|
|
|
FilePickImpl? filePickImpl,
|
|
|
|
WebImagePickImpl? webImagePickImpl,
|
|
|
|
WebVideoPickImpl? webVideoPickImpl,
|
|
|
|
Key? key,
|
|
|
|
}) {
|
|
|
|
final isButtonGroupShown = [
|
|
|
|
showHistory ||
|
|
|
|
showBoldButton ||
|
|
|
|
showItalicButton ||
|
|
|
|
showSmallButton ||
|
|
|
|
showUnderLineButton ||
|
|
|
|
showStrikeThrough ||
|
|
|
|
showColorButton ||
|
|
|
|
showBackgroundColorButton ||
|
|
|
|
showClearFormat ||
|
|
|
|
onImagePickCallback != null ||
|
|
|
|
onVideoPickCallback != null,
|
|
|
|
showHeaderStyle,
|
|
|
|
showListNumbers || showListBullets || showListCheck || showCodeBlock,
|
|
|
|
showQuote || showIndent,
|
|
|
|
showLink || showHorizontalRule
|
|
|
|
];
|
|
|
|
|
|
|
|
return QuillToolbar(
|
|
|
|
key: key,
|
|
|
|
toolBarHeight: toolbarIconSize * 2,
|
|
|
|
multiRowsDisplay: multiRowsDisplay,
|
|
|
|
children: [
|
|
|
|
if (showHistory)
|
|
|
|
HistoryButton(
|
|
|
|
icon: Icons.undo_outlined,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
undo: true,
|
|
|
|
),
|
|
|
|
if (showHistory)
|
|
|
|
HistoryButton(
|
|
|
|
icon: Icons.redo_outlined,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
undo: false,
|
|
|
|
),
|
|
|
|
if (showBoldButton)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.bold,
|
|
|
|
icon: Icons.format_bold,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
),
|
|
|
|
if (showItalicButton)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.italic,
|
|
|
|
icon: Icons.format_italic,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
),
|
|
|
|
if (showSmallButton)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.small,
|
|
|
|
icon: Icons.format_size,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
),
|
|
|
|
if (showUnderLineButton)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.underline,
|
|
|
|
icon: Icons.format_underline,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
),
|
|
|
|
if (showStrikeThrough)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.strikeThrough,
|
|
|
|
icon: Icons.format_strikethrough,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
),
|
|
|
|
if (showColorButton)
|
|
|
|
ColorButton(
|
|
|
|
icon: Icons.color_lens,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
background: false,
|
|
|
|
),
|
|
|
|
if (showBackgroundColorButton)
|
|
|
|
ColorButton(
|
|
|
|
icon: Icons.format_color_fill,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
background: true,
|
|
|
|
),
|
|
|
|
if (showClearFormat)
|
|
|
|
ClearFormatButton(
|
|
|
|
icon: Icons.format_clear,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
),
|
|
|
|
if (showImageButton)
|
|
|
|
ImageButton(
|
|
|
|
icon: Icons.image,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
onImagePickCallback: onImagePickCallback,
|
|
|
|
filePickImpl: filePickImpl,
|
|
|
|
webImagePickImpl: webImagePickImpl,
|
|
|
|
mediaPickSettingSelector: mediaPickSettingSelector,
|
|
|
|
),
|
|
|
|
if (showVideoButton)
|
|
|
|
VideoButton(
|
|
|
|
icon: Icons.movie_creation,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
onVideoPickCallback: onVideoPickCallback,
|
|
|
|
filePickImpl: filePickImpl,
|
|
|
|
webVideoPickImpl: webImagePickImpl,
|
|
|
|
mediaPickSettingSelector: mediaPickSettingSelector,
|
|
|
|
),
|
|
|
|
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),
|
|
|
|
if (isButtonGroupShown[0] &&
|
|
|
|
(isButtonGroupShown[1] ||
|
|
|
|
isButtonGroupShown[2] ||
|
|
|
|
isButtonGroupShown[3] ||
|
|
|
|
isButtonGroupShown[4]))
|
|
|
|
VerticalDivider(
|
|
|
|
indent: 12,
|
|
|
|
endIndent: 12,
|
|
|
|
color: Colors.grey.shade400,
|
|
|
|
),
|
|
|
|
if (showHeaderStyle)
|
|
|
|
SelectHeaderStyleButton(
|
|
|
|
controller: controller,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
if (isButtonGroupShown[1] &&
|
|
|
|
(isButtonGroupShown[2] ||
|
|
|
|
isButtonGroupShown[3] ||
|
|
|
|
isButtonGroupShown[4]))
|
|
|
|
VerticalDivider(
|
|
|
|
indent: 12,
|
|
|
|
endIndent: 12,
|
|
|
|
color: Colors.grey.shade400,
|
|
|
|
),
|
|
|
|
if (showListNumbers)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.ol,
|
|
|
|
controller: controller,
|
|
|
|
icon: Icons.format_list_numbered,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
if (showListBullets)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.ul,
|
|
|
|
controller: controller,
|
|
|
|
icon: Icons.format_list_bulleted,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
if (showListCheck)
|
|
|
|
ToggleCheckListButton(
|
|
|
|
attribute: Attribute.unchecked,
|
|
|
|
controller: controller,
|
|
|
|
icon: Icons.check_box,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
if (showCodeBlock)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.codeBlock,
|
|
|
|
controller: controller,
|
|
|
|
icon: Icons.code,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
if (isButtonGroupShown[2] &&
|
|
|
|
(isButtonGroupShown[3] || isButtonGroupShown[4]))
|
|
|
|
VerticalDivider(
|
|
|
|
indent: 12,
|
|
|
|
endIndent: 12,
|
|
|
|
color: Colors.grey.shade400,
|
|
|
|
),
|
|
|
|
if (showQuote)
|
|
|
|
ToggleStyleButton(
|
|
|
|
attribute: Attribute.blockQuote,
|
|
|
|
controller: controller,
|
|
|
|
icon: Icons.format_quote,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
if (showIndent)
|
|
|
|
IndentButton(
|
|
|
|
icon: Icons.format_indent_increase,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
isIncrease: true,
|
|
|
|
),
|
|
|
|
if (showIndent)
|
|
|
|
IndentButton(
|
|
|
|
icon: Icons.format_indent_decrease,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
controller: controller,
|
|
|
|
isIncrease: false,
|
|
|
|
),
|
|
|
|
if (isButtonGroupShown[3] && isButtonGroupShown[4])
|
|
|
|
VerticalDivider(
|
|
|
|
indent: 12,
|
|
|
|
endIndent: 12,
|
|
|
|
color: Colors.grey.shade400,
|
|
|
|
),
|
|
|
|
if (showLink)
|
|
|
|
LinkStyleButton(
|
|
|
|
controller: controller,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
if (showHorizontalRule)
|
|
|
|
InsertEmbedButton(
|
|
|
|
controller: controller,
|
|
|
|
icon: Icons.horizontal_rule,
|
|
|
|
iconSize: toolbarIconSize,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
final List<Widget> children;
|
|
|
|
final double toolBarHeight;
|
|
|
|
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;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Size get preferredSize => Size.fromHeight(toolBarHeight);
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
if (multiRowsDisplay ?? true) {
|
|
|
|
return Wrap(
|
|
|
|
alignment: WrapAlignment.center,
|
|
|
|
runSpacing: 4,
|
|
|
|
spacing: 4,
|
|
|
|
children: children,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return Container(
|
|
|
|
constraints: BoxConstraints.tightFor(height: preferredSize.height),
|
|
|
|
color: color ?? Theme.of(context).canvasColor,
|
|
|
|
child: ArrowIndicatedButtonList(buttons: children),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|