| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929 |
- import 'package:flutter/foundation.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_colorpicker/flutter_colorpicker.dart';
- import '../model/document/style.dart';
- import '../model/document/attribute.dart';
- import '../model/document/node/embed.dart';
- import '../util/color.dart';
- import '../service/controller.dart';
- import 'toolbar.dart';
- /* -------------------------------- Constant -------------------------------- */
- const double kToolbarIconHeight = 18.0;
- const double kToolbarButtonHeight = kToolbarIconHeight * 1.77;
- class FlowyToolbar extends EditorToolbar {
- const FlowyToolbar({
- required List<Widget> children,
- Key? key,
- }) : super(
- children: children,
- customButtonHeight: kToolbarButtonHeight,
- key: key,
- );
- factory FlowyToolbar.basic({
- required EditorController controller,
- double? toolbarIconSize,
- bool showHistory = true,
- bool showBold = true,
- bool showItalic = true,
- bool showUnderline = true,
- bool showStrikethrough = true,
- bool showColor = true,
- bool showBackgroundColor = true,
- bool showClearFormat = true,
- bool showHeader = true,
- bool showBulletList = true,
- bool showOrderedList = true,
- bool showCheckList = true,
- bool showCodeblock = true,
- bool showQuoteblock = true,
- bool showIndent = true,
- bool showLink = true,
- bool showHorizontalLine = true,
- OnImageSelectCallback? onImageSelectCallback,
- Key? key,
- }) {
- return FlowyToolbar(children: [
- Visibility(
- visible: showHistory,
- child: HistoryButton(
- icon: Icons.undo_outlined,
- controller: controller,
- isUndo: true,
- ),
- ),
- Visibility(
- visible: showHistory,
- child: HistoryButton(
- icon: Icons.redo_outlined,
- controller: controller,
- isUndo: false,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showBold,
- child: ToggleStyleButton(
- attribute: Attribute.bold,
- icon: Icons.format_bold,
- controller: controller,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showItalic,
- child: ToggleStyleButton(
- attribute: Attribute.italic,
- icon: Icons.format_italic,
- controller: controller,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showUnderline,
- child: ToggleStyleButton(
- attribute: Attribute.underline,
- icon: Icons.format_underline,
- controller: controller,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showStrikethrough,
- child: ToggleStyleButton(
- attribute: Attribute.strikeThrough,
- icon: Icons.format_strikethrough,
- controller: controller,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showColor,
- child: ColorButton(
- icon: Icons.color_lens,
- controller: controller,
- isBackground: false,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showColor,
- child: ColorButton(
- icon: Icons.format_color_fill,
- controller: controller,
- isBackground: true,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showColor,
- child: ClearFormatButton(
- icon: Icons.format_clear,
- controller: controller,
- ),
- ),
- const SizedBox(width: 0.6),
- Visibility(
- visible: showOrderedList,
- child: ToggleStyleButton(
- attribute: Attribute.ordered,
- icon: Icons.format_list_numbered,
- controller: controller,
- ),
- ),
- Visibility(
- visible: showBulletList,
- child: ToggleStyleButton(
- attribute: Attribute.bullet,
- icon: Icons.format_list_bulleted,
- controller: controller,
- ),
- ),
- Visibility(
- visible: showCheckList,
- child: ToggleStyleButton(
- attribute: Attribute.unchecked,
- icon: Icons.check_box,
- controller: controller,
- ),
- ),
- Visibility(
- visible: showCodeblock,
- child: ToggleStyleButton(
- attribute: Attribute.codeBlock,
- icon: Icons.code,
- controller: controller,
- ),
- ),
- Visibility(
- visible: showHeader,
- child: VerticalDivider(
- indent: 12, endIndent: 12, color: Colors.grey.shade400),
- ),
- Visibility(
- visible: showHeader,
- child: HeaderStyleButton(controller: controller),
- ),
- VerticalDivider(indent: 12, endIndent: 12, color: Colors.grey.shade400),
- Visibility(
- visible: !showOrderedList &&
- !showBulletList &&
- !showCheckList &&
- !showCodeblock,
- child: VerticalDivider(
- indent: 12, endIndent: 12, color: Colors.grey.shade400),
- ),
- Visibility(
- visible: showQuoteblock,
- child: ToggleStyleButton(
- attribute: Attribute.quoteBlock,
- controller: controller,
- icon: Icons.format_quote,
- ),
- ),
- Visibility(
- visible: showIndent,
- child: IndentButton(
- icon: Icons.format_indent_increase,
- controller: controller,
- isIncrease: true,
- ),
- ),
- Visibility(
- visible: showIndent,
- child: IndentButton(
- icon: Icons.format_indent_decrease,
- controller: controller,
- isIncrease: false,
- ),
- ),
- Visibility(
- visible: showQuoteblock,
- child: VerticalDivider(
- indent: 12, endIndent: 12, color: Colors.grey.shade400),
- ),
- Visibility(
- visible: showLink,
- child: LinkStyleButton(
- controller: controller,
- icon: Icons.link,
- ),
- ),
- ]);
- }
- }
- /* ---------------------------------- Util ---------------------------------- */
- class ToolbarIconButton extends StatelessWidget {
- const ToolbarIconButton({
- this.onPressed,
- this.icon,
- this.size = 40,
- this.fillColor,
- this.hoverElevation = 1,
- this.highlightElevation = 1,
- Key? key,
- }) : super(key: key);
- final VoidCallback? onPressed;
- final Widget? icon;
- final double size;
- final Color? fillColor;
- final double hoverElevation;
- final double highlightElevation;
- @override
- Widget build(BuildContext context) {
- return ConstrainedBox(
- constraints: BoxConstraints.tightFor(width: size, height: size),
- child: RawMaterialButton(
- visualDensity: VisualDensity.compact,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(2)),
- fillColor: fillColor,
- elevation: 0,
- hoverElevation: hoverElevation,
- highlightElevation: hoverElevation,
- onPressed: onPressed,
- child: icon,
- ),
- );
- }
- }
- /* ------------------------------- Button Impl ------------------------------ */
- // History (Redo, Undo)
- class HistoryButton extends StatefulWidget {
- const HistoryButton({
- required this.icon,
- required this.isUndo,
- required this.controller,
- Key? key,
- }) : super(key: key);
- final IconData icon;
- final bool isUndo;
- final EditorController controller;
- @override
- _HistoryButtonState createState() => _HistoryButtonState();
- }
- class _HistoryButtonState extends State<HistoryButton> {
- Color? _iconColor;
- late ThemeData theme;
- @override
- Widget build(BuildContext context) {
- theme = Theme.of(context);
- _setIconColor();
- final fillColor = theme.canvasColor;
- widget.controller.changes.listen((event) async {
- _setIconColor();
- });
- return ToolbarIconButton(
- size: kToolbarButtonHeight,
- fillColor: fillColor,
- hoverElevation: 0,
- highlightElevation: 0,
- icon: Icon(widget.icon, size: kToolbarIconHeight, color: _iconColor),
- onPressed: _applyHistory,
- );
- }
- void _applyHistory() {
- if (widget.isUndo) {
- if (widget.controller.hasUndo) {
- widget.controller.undo();
- }
- } else {
- if (widget.controller.hasRedo) {
- widget.controller.redo();
- }
- }
- _setIconColor();
- }
- void _setIconColor() {
- if (!mounted) {
- return;
- }
- if (widget.isUndo) {
- setState(() {
- _iconColor = widget.controller.hasUndo
- ? theme.iconTheme.color
- : theme.disabledColor;
- });
- } else {
- setState(() {
- _iconColor = widget.controller.hasRedo
- ? theme.iconTheme.color
- : theme.disabledColor;
- });
- }
- }
- }
- // Toggle Style (Bold, Italic, Underline, etc..)
- typedef ToggleStyleButtonBuilder = Widget Function(
- BuildContext context,
- Attribute attribute,
- IconData icon,
- Color? fillColor,
- bool? isToggled,
- VoidCallback? onPressed,
- );
- Widget defaultToggleStyleButtonBuilder(
- BuildContext context,
- Attribute attribute,
- IconData icon,
- Color? fillColor,
- bool? isToggled,
- VoidCallback? onPressed,
- ) {
- final theme = Theme.of(context);
- final isEnabled = onPressed != null;
- final iconColor = isEnabled
- ? isToggled == true
- ? theme.primaryIconTheme.color
- : theme.iconTheme.color
- : theme.disabledColor;
- final theFillColor = isToggled == true
- ? theme.toggleableActiveColor
- : fillColor ?? theme.canvasColor;
- return ToolbarIconButton(
- onPressed: onPressed,
- icon: Icon(icon, size: kToolbarIconHeight, color: iconColor),
- size: kToolbarButtonHeight,
- fillColor: theFillColor,
- hoverElevation: 0,
- highlightElevation: 0,
- );
- }
- class ToggleStyleButton extends StatefulWidget {
- const ToggleStyleButton({
- required this.attribute,
- required this.icon,
- required this.controller,
- this.fillColor,
- this.childBuilder = defaultToggleStyleButtonBuilder,
- Key? key,
- }) : super(key: key);
- final Attribute attribute;
- final IconData icon;
- final Color? fillColor;
- final EditorController controller;
- final ToggleStyleButtonBuilder childBuilder;
- @override
- _ToggleStyleButtonState createState() => _ToggleStyleButtonState();
- }
- class _ToggleStyleButtonState extends State<ToggleStyleButton> {
- bool? _isToggled;
- Style get _selectionStyle => widget.controller.getSelectionStyle();
- @override
- void didUpdateWidget(covariant ToggleStyleButton oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (oldWidget.controller != widget.controller) {
- oldWidget.controller.removeListener(_didChangeEditingValue);
- widget.controller.addListener(_didChangeEditingValue);
- _isToggled = _checkIfAttrToggled(_selectionStyle.attributes);
- }
- }
- @override
- void initState() {
- super.initState();
- _isToggled = _checkIfAttrToggled(_selectionStyle.attributes);
- widget.controller.addListener(_didChangeEditingValue);
- }
- @override
- void dispose() {
- widget.controller.removeListener(_didChangeEditingValue);
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- final isInCodeBlock =
- _selectionStyle.attributes.containsKey(Attribute.codeBlock.key);
- final isEnabled =
- !isInCodeBlock || widget.attribute.key == Attribute.codeBlock.key;
- return widget.childBuilder(
- context,
- widget.attribute,
- widget.icon,
- widget.fillColor,
- _isToggled,
- isEnabled ? _toggleAttribute : null,
- );
- }
- bool _checkIfAttrToggled(Map<String, Attribute> attrs) {
- if (widget.attribute.key == Attribute.list.key) {
- final attribute = attrs[widget.attribute.key];
- if (attribute == null) {
- return false;
- }
- return attribute.value == widget.attribute.value;
- }
- return attrs.containsKey(widget.attribute.key);
- }
- void _toggleAttribute() {
- widget.controller.formatSelection(
- _isToggled! ? Attribute.clone(widget.attribute, null) : widget.attribute,
- );
- }
- void _didChangeEditingValue() {
- setState(() {
- _isToggled =
- _checkIfAttrToggled(widget.controller.getSelectionStyle().attributes);
- });
- }
- }
- // Header Style
- class HeaderStyleButton extends StatefulWidget {
- const HeaderStyleButton({
- required this.controller,
- Key? key,
- }) : super(key: key);
- final EditorController controller;
- @override
- _HeaderStyleButtonState createState() => _HeaderStyleButtonState();
- }
- class _HeaderStyleButtonState extends State<HeaderStyleButton> {
- Attribute? _value;
- Style get _selectionStyle => widget.controller.getSelectionStyle();
- @override
- void initState() {
- super.initState();
- setState(() {
- _value =
- _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header;
- });
- widget.controller.addListener(_didChangeEditingValue);
- }
- @override
- void didUpdateWidget(covariant HeaderStyleButton oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (oldWidget.controller != widget.controller) {
- oldWidget.controller.removeListener(_didChangeEditingValue);
- widget.controller.addListener(_didChangeEditingValue);
- _value =
- _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header;
- }
- }
- @override
- void dispose() {
- widget.controller.removeListener(_didChangeEditingValue);
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- return _selectHeaderStyleButtonBuilder(context, _value);
- }
- void _didChangeEditingValue() {
- setState(() {
- _value =
- _selectionStyle.attributes[Attribute.header.key] ?? Attribute.header;
- });
- }
- Widget _selectHeaderStyleButtonBuilder(
- BuildContext context, Attribute? value) {
- final theme = Theme.of(context);
- final style = TextStyle(
- fontWeight: FontWeight.w600,
- fontSize: kToolbarIconHeight * 0.7,
- );
- final headerTextMapping = <Attribute, String>{
- Attribute.header: 'N',
- Attribute.h1: 'H1',
- Attribute.h2: 'H2',
- Attribute.h3: 'H3',
- Attribute.h4: 'H4',
- Attribute.h5: 'H5',
- Attribute.h6: 'H6',
- };
- final headerStyles = headerTextMapping.keys.toList(growable: false);
- final headerTexts = headerTextMapping.values.toList(growable: false);
- return Row(
- mainAxisSize: MainAxisSize.min,
- children: List.generate(headerStyles.length, (index) {
- return Padding(
- padding: const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0),
- child: ConstrainedBox(
- constraints: BoxConstraints.tightFor(
- width: kToolbarButtonHeight, height: kToolbarButtonHeight),
- child: RawMaterialButton(
- fillColor: headerTextMapping[value] == headerTexts[index]
- ? theme.toggleableActiveColor
- : theme.canvasColor,
- hoverElevation: 0,
- highlightElevation: 0,
- visualDensity: VisualDensity.compact,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(2)),
- onPressed: () {
- widget.controller.formatSelection(headerStyles[index]);
- },
- child: Text(
- headerTexts[index],
- style: style.copyWith(
- color: headerTextMapping[value] == headerTexts[index]
- ? theme.primaryIconTheme.color
- : theme.iconTheme.color,
- ),
- ),
- ),
- ),
- );
- }),
- );
- }
- }
- // Color (TextColor, BackgroundColor)
- class ColorButton extends StatefulWidget {
- const ColorButton({
- required this.icon,
- required this.isBackground,
- required this.controller,
- Key? key,
- }) : super(key: key);
- final IconData icon;
- final bool isBackground;
- final EditorController controller;
- @override
- _ColorButtonState createState() => _ColorButtonState();
- }
- class _ColorButtonState extends State<ColorButton> {
- late bool _isToggledColor;
- late bool _isToggledBackground;
- late bool _isWhite;
- late bool _isWhiteBackground;
- Style get _selectionStyle => widget.controller.getSelectionStyle();
- @override
- void didUpdateWidget(covariant ColorButton oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (oldWidget.controller != widget.controller) {
- oldWidget.controller.removeListener(_didChangeEditingValue);
- widget.controller.addListener(_didChangeEditingValue);
- _updateToggledState();
- }
- }
- @override
- void initState() {
- super.initState();
- _updateToggledState();
- }
- @override
- void dispose() {
- widget.controller.removeListener(_didChangeEditingValue);
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final iconColor = _isToggledColor && !widget.isBackground && !_isWhite
- ? stringToColor(_selectionStyle.attributes['color']!.value)
- : theme.iconTheme.color;
- final iconColorBackground =
- _isToggledBackground && widget.isBackground && !_isWhiteBackground
- ? stringToColor(_selectionStyle.attributes['background']!.value)
- : theme.iconTheme.color;
- final fillColor = _isToggledColor && !widget.isBackground && _isWhite
- ? stringToColor('#ffffff')
- : theme.canvasColor;
- final fillColorBackground =
- _isToggledBackground && widget.isBackground && _isWhiteBackground
- ? stringToColor('#ffffff')
- : theme.canvasColor;
- return ToolbarIconButton(
- size: kToolbarButtonHeight,
- fillColor: widget.isBackground ? fillColorBackground : fillColor,
- hoverElevation: 0,
- highlightElevation: 0,
- icon: Icon(
- widget.icon,
- size: kToolbarIconHeight,
- color: widget.isBackground ? iconColorBackground : iconColor,
- ),
- onPressed: _showColorPicker,
- );
- }
- void _didChangeEditingValue() {
- setState(() {
- _updateToggledState();
- widget.controller.addListener(_didChangeEditingValue);
- });
- }
- void _updateToggledState() {
- _isToggledColor = _checkIfToggledColor(_selectionStyle.attributes);
- _isToggledBackground =
- _checkIfToggledBackground(_selectionStyle.attributes);
- _isWhite = _isToggledColor &&
- _selectionStyle.attributes['color']!.value == '#ffffff';
- _isWhiteBackground = _isToggledBackground &&
- _selectionStyle.attributes['background']!.value == '#ffffff';
- }
- bool _checkIfToggledColor(Map<String, Attribute> attrs) {
- return attrs.containsKey(Attribute.color.key);
- }
- bool _checkIfToggledBackground(Map<String, Attribute> attrs) {
- return attrs.containsKey(Attribute.background.key);
- }
- void _applyColor(Color color) {
- var hex = color.value.toRadixString(16);
- if (hex.startsWith('ff')) {
- hex = hex.substring(2);
- }
- hex = '#$hex';
- widget.controller.formatSelection(
- widget.isBackground ? BackgroundAttribute(hex) : ColorAttribute(hex));
- Navigator.of(context).pop();
- }
- void _showColorPicker() {
- showDialog(
- context: context,
- builder: (_) {
- return AlertDialog(
- title: const Text('Select Color'),
- backgroundColor: Theme.of(context).canvasColor,
- content: SingleChildScrollView(
- child: MaterialPicker(
- pickerColor: const Color(0x00000000),
- onColorChanged: _applyColor,
- enableLabel: true,
- ),
- ),
- );
- });
- }
- }
- // Clear Format
- class ClearFormatButton extends StatefulWidget {
- const ClearFormatButton({
- required this.icon,
- required this.controller,
- Key? key,
- }) : super(key: key);
- final IconData icon;
- final EditorController controller;
- @override
- _ClearFormatButtonState createState() => _ClearFormatButtonState();
- }
- class _ClearFormatButtonState extends State<ClearFormatButton> {
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final iconColor = theme.iconTheme.color;
- final fillColor = theme.canvasColor;
- return ToolbarIconButton(
- size: kToolbarButtonHeight,
- fillColor: fillColor,
- hoverElevation: 0,
- highlightElevation: 0,
- icon: Icon(widget.icon, size: kToolbarIconHeight, color: iconColor),
- onPressed: () {
- widget.controller.getSelectionStyle().values.forEach((style) {
- widget.controller.formatSelection(Attribute.clone(style, null));
- });
- },
- );
- }
- }
- // Indent
- class IndentButton extends StatefulWidget {
- const IndentButton({
- required this.icon,
- required this.controller,
- required this.isIncrease,
- Key? key,
- }) : super(key: key);
- final IconData icon;
- final EditorController controller;
- final bool isIncrease;
- @override
- _IndentButtonState createState() => _IndentButtonState();
- }
- class _IndentButtonState extends State<IndentButton> {
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final iconColor = theme.iconTheme.color;
- final fillColor = theme.canvasColor;
- return ToolbarIconButton(
- size: kToolbarButtonHeight,
- fillColor: fillColor,
- hoverElevation: 0,
- highlightElevation: 0,
- icon: Icon(widget.icon, size: kToolbarIconHeight, color: iconColor),
- onPressed: () {
- final indent = widget.controller
- .getSelectionStyle()
- .attributes[Attribute.indent.key];
- if (indent == null) {
- if (widget.isIncrease) {
- widget.controller.formatSelection(Attribute.indentL1);
- }
- return;
- }
- if (indent.value == 1 && !widget.isIncrease) {
- widget.controller
- .formatSelection(Attribute.clone(Attribute.indentL1, null));
- return;
- }
- if (widget.isIncrease) {
- // Next indent value
- widget.controller
- .formatSelection(Attribute.getIndentLevel(indent.value + 1));
- return;
- }
- // Prev indent value
- widget.controller
- .formatSelection(Attribute.getIndentLevel(indent.value - 1));
- },
- );
- }
- }
- // Link
- class LinkStyleButton extends StatefulWidget {
- const LinkStyleButton({
- required this.controller,
- required this.icon,
- Key? key,
- }) : super(key: key);
- final IconData? icon;
- final EditorController controller;
- @override
- _LinkStyleButtonState createState() => _LinkStyleButtonState();
- }
- class _LinkStyleButtonState extends State<LinkStyleButton> {
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final canAddLink = !widget.controller.selection.isCollapsed;
- return ToolbarIconButton(
- highlightElevation: 0,
- hoverElevation: 0,
- size: kToolbarButtonHeight,
- icon: Icon(
- widget.icon,
- size: kToolbarIconHeight,
- color: canAddLink ? theme.iconTheme.color : theme.disabledColor,
- ),
- fillColor: theme.canvasColor,
- onPressed: canAddLink ? () => _openLinkDialog(context) : null,
- );
- }
- void _openLinkDialog(BuildContext context) {
- showDialog<String>(
- context: context,
- builder: (context) {
- return const LinkEditDialog();
- }).then(_applyLinkAttribute);
- }
- void _applyLinkAttribute(String? value) {
- if (value == null || value.isEmpty) {
- return;
- }
- widget.controller.formatSelection(LinkAttribute(value));
- }
- }
- class LinkEditDialog extends StatefulWidget {
- const LinkEditDialog({Key? key}) : super(key: key);
- @override
- _LinkEditDialogState createState() => _LinkEditDialogState();
- }
- class _LinkEditDialogState extends State<LinkEditDialog> {
- String _value = '';
- @override
- Widget build(BuildContext context) {
- return AlertDialog(
- content: TextField(
- decoration: const InputDecoration(labelText: 'Input Link'),
- autofocus: true,
- onChanged: _handleLinkChanged,
- ),
- actions: [
- TextButton(
- onPressed: _value.isNotEmpty ? _handleFinishEditing : null,
- child: const Text('Finish'),
- ),
- ],
- );
- }
- void _handleLinkChanged(String value) {
- setState(() => _value = value);
- }
- void _handleFinishEditing() {
- Navigator.pop(context, _value);
- }
- }
- // Embed
- class AddEmbedButton extends StatelessWidget {
- const AddEmbedButton({
- required this.controller,
- required this.icon,
- this.fillColor,
- Key? key,
- }) : super(key: key);
- final IconData icon;
- final EditorController controller;
- final Color? fillColor;
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- return ToolbarIconButton(
- highlightElevation: 0,
- hoverElevation: 0,
- size: kToolbarButtonHeight,
- icon: Icon(icon, size: kToolbarIconHeight, color: theme.iconTheme.color),
- fillColor: fillColor ?? theme.canvasColor,
- onPressed: () {
- final index = controller.selection.baseOffset;
- final length = controller.selection.extentOffset - index;
- controller.replaceText(
- index,
- length,
- BlockEmbed.horizontalRule,
- null,
- );
- },
- );
- }
- }
|