How to change text selection option in flutter?

Viewed 5473

I tried to add text editing format option like in the gmail app. But when highlight the text there' is not a format option. Is it possible to handle selecting alert? (Copy/cut/paste). Or is there a way to add format bar like gmail?

       TextField(
          controller: _categoryController,
          decoration: InputDecoration(
            border: InputBorder.none,
            hintText: "Enter Category Name",
          ),
        ),

I added screenshot and gif files to better understanding my question.

Selecting option on my Flutter application

selecting option on my Flutter application

Selecting option on Gmail App

enter image description here

3 Answers

Output:

output

You can check the code below:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _controller = new TextEditingController();
  final _textfieldFocusNode = new FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(20.0),
              child: GestureDetector(
                // intercept all pointer calls
                behavior: HitTestBehavior.opaque,
                onTap: () {
                  FocusScope.of(context).requestFocus(_textfieldFocusNode);
                },
                onLongPress: () {
                  showMenu(
                    context: context,
                    // TODO: Position dynamically based on cursor or textfield
                    position: RelativeRect.fromLTRB(0.0, 300.0, 300.0, 0.0),
                    items: [
                      PopupMenuItem(
                        child: Row(
                          children: <Widget>[
                            // TODO: Dynamic items / handle click
                            PopupMenuItem(
                              child: Text(
                                "Paste",
                                style: Theme.of(context)
                                    .textTheme
                                    .body2
                                    .copyWith(color: Colors.red),
                              ),
                            ),
                            PopupMenuItem(
                              child: Text("Select All"),
                            ),
                          ],
                        ),
                      ),
                    ],
                  );
                },
                child: IgnorePointer(
                  // ensures textfield doesn't overrule GestureDetector
                  child: TextField(
                    focusNode: _textfieldFocusNode,
                    controller: _controller,
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

You can use selectionControls parameter to customize text selection menu:

TextField(selectionControls: MyMaterialTextSelectionControls()),

and implement delegate class like here:

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;
}

Or checkout this one text_selection_controls

Related