Flutter: how to build a custom toolbar for Selectable Text~

2.6k views Asked by At

I'm building a ChatApp, and I need a feature that can allow user to select text from the widget, and also show some of my custom actions, such as Deleting that message, Share etc.

And I found that the SelectableText widget would be helpful, but the only downside of it is that I have to create a custom TextSelectionControls class and pass it to the SelectableText widget selectionControls property in order to add my own actions, but I really don't know how to do it, so does anyone have idea about how to create this kind of class?

I already visited this link: Github: Custom text selection menu example, but after I copy and paste his code, it shows me this error message at compile time:

Missing concrete implementations of 'TextSelectionControls.buildHandle', 'TextSelectionControls.getHandleAnchor', and 'TextSelectionControls.getHandleSize'.

Try implementing the missing methods, or make the class abstract.dart(non_abstract_class_inherits_abstract_member)

But I don't think I missed something, since the people on Github doesn't, so is it because the issue of the Flutter framework itself?

2

There are 2 answers

0
Yes Dev On BEST ANSWER

With the release of Flutter 3.7, you can easily add custom context actions to any widget, including Text widgets.

SelectableText(
              'My Text',
              contextMenuBuilder: (context, editableTextState) {
              return AdaptiveTextSelectionToolbar(
                  anchors: editableTextState.contextMenuAnchors,
                  children: [
                    InkWell(
                      onTap: (){},
                      child: SizedBox(
                        width: 200.0,
                        child: Text('Note'),
                      ),
                    )
                  ]);
            },
          ),

More examples can be found here.

0
igokom On

The code from the link you've provided works. Are you sure you are using

class MyMaterialTextSelectionControls extends MaterialTextSelectionControls

Here is refactored working example

class MyMaterialTextSelectionControls extends MaterialTextSelectionControls {
  // Padding between the toolbar and the anchor.
  static const double _kToolbarContentDistanceBelow = 10.0;
  static const double _kToolbarContentDistance = 8.0;

  /// Builder for material-style copy/paste text selection toolbar.
  @override
  Widget buildToolbar(
    BuildContext context,
    Rect globalEditableRegion,
    double textLineHeight,
    Offset selectionMidpoint,
    List<TextSelectionPoint> endpoints,
    TextSelectionDelegate delegate,
    ClipboardStatusNotifier clipboardStatus,
    Offset? lastSecondaryTapDownPosition,
  ) {
    final TextSelectionPoint startTextSelectionPoint = endpoints[0];
    final TextSelectionPoint endTextSelectionPoint =
        endpoints.length > 1 ? endpoints[1] : endpoints[0];
    final Offset anchorAbove = Offset(
      globalEditableRegion.left + selectionMidpoint.dx,
      globalEditableRegion.top +
          startTextSelectionPoint.point.dy -
          textLineHeight -
          _kToolbarContentDistance,
    );
    final Offset anchorBelow = Offset(
      globalEditableRegion.left + selectionMidpoint.dx,
      globalEditableRegion.top +
          endTextSelectionPoint.point.dy +
          _kToolbarContentDistanceBelow,
    );
    final value = delegate.textEditingValue;
    return MyTextSelectionToolbar(
      anchorAbove: anchorAbove,
      anchorBelow: anchorBelow,
      clipboardStatus: clipboardStatus,
      handleCustomButton: () {
        print(value.selection.textInside(value.text));
        delegate.hideToolbar();
      },
    );
  }
}

class MyTextSelectionToolbar extends StatelessWidget {
  const MyTextSelectionToolbar({
    Key? key,
    required this.anchorAbove,
    required this.anchorBelow,
    required this.clipboardStatus,
    required this.handleCustomButton,
  }) : super(key: key);

  final Offset anchorAbove;
  final Offset anchorBelow;
  final ClipboardStatusNotifier clipboardStatus;
  final VoidCallback? handleCustomButton;

  @override
  Widget build(BuildContext context) {
    assert(debugCheckHasMaterialLocalizations(context));

    final List<_TextSelectionToolbarItemData> items =
        <_TextSelectionToolbarItemData>[
      _TextSelectionToolbarItemData(
        onPressed: handleCustomButton ?? () {},
        label: 'Custom button',
      ),
    ];

    int childIndex = 0;
    return TextSelectionToolbar(
      anchorAbove: anchorAbove,
      anchorBelow: anchorBelow,
      toolbarBuilder: (BuildContext context, Widget child) =>
          Container(color: Colors.pink, child: child),
      children: items
          .map((_TextSelectionToolbarItemData itemData) =>
              TextSelectionToolbarTextButton(
                padding: TextSelectionToolbarTextButton.getPadding(
                    childIndex++, items.length),
                onPressed: itemData.onPressed,
                child: Text(itemData.label),
              ))
          .toList(),
    );
  }
}

class _TextSelectionToolbarItemData {
  const _TextSelectionToolbarItemData({
    required this.label,
    required this.onPressed,
  });

  final String label;
  final VoidCallback onPressed;
}

anyway you can check this package text_selection_controls