GithubHelp home page GithubHelp logo

ronjb / selectable Goto Github PK

View Code? Open in Web Editor NEW
17.0 2.0 11.0 6.03 MB

A Flutter widget that enables text selection over all the text widgets it contains

License: MIT License

Kotlin 0.05% Ruby 1.07% Swift 0.49% Objective-C 0.01% Dart 80.26% HTML 1.57% CSS 0.67% CMake 7.17% C++ 8.17% C 0.55%
flutter widget selection

selectable's Introduction

selectable

Pub

A Flutter widget that enables text selection over all the text widgets it contains.

Try it out at: https://ronjb.github.io/selectable

⭐️ IMPORTANT ⭐️

This library was written over a year before SelectableRegion and related classes were added to Flutter. I will continue to maintain this library because I still use it in a few of my projects, but now that Flutter natively supports selection, I'd recommend using their implementation if it meets your needs.

Getting Started

Add this to your app's pubspec.yaml file:

dependencies:
  selectable: ^0.3.1

Usage

Then in the dart file, import the package with:

import 'package:selectable/selectable.dart';

And use Selectable where appropriate. For example:

Scaffold(
  body: SingleChildScrollView(
    child: Selectable(
      child: Column(
        children: [
          Text('... a lot of text ...'),
          // ... more widgets of any type that might contain text ...
        ],
      )
    )
  )
)

Important Note: As shown in the example above, if a scrollable widget (such as SingleChildScrollView, ListView, CustomScrollView, etc.) is used to wrap the text widgets you want to enable selection for, the Selectable widget must be a descendant of the scrollable widget, and an ancestor of the text widgets.

Selectable by default supports long-pressing on a word to select it, then using the selection handles to adjust the selection. To also enable double-tapping on a word to select it, pass in selectWordOnDoubleTap: true like this:

Selectable(
  selectWordOnDoubleTap: true,
  child: child,
)

Wrap widgets that shouldn't be selectable in IgnoreSelectable

For example:

IgnoreSelectable(
  child: Text(
    'This text is wrapped in an IgnoreSelectable widget, so it is not selectable.',
    style: textStyle2,
  ),
),

Customizable Popup Selection Menu

Selectable provides a default popup selection menu with the menu items Copy, Define, and WebSearch, but it can easily be customized. For example, to continue to show the default Copy menu item, and to add a custom menu item with the title "Foo! :)", which shows the selected text in an AlertDialog, do this:

Selectable(
  child: child,
  popupMenuItems: [
    SelectableMenuItem(type: SelectableMenuItemType.copy),
    SelectableMenuItem(
      title: 'Foo! :)',
      isEnabled: (controller) => controller!.isTextSelected;
      handler: (controller) {
        showDialog<void>(
          context: context,
          barrierDismissible: true,
          builder: (builder) {
            return AlertDialog(
              contentPadding: EdgeInsets.zero,
              content: Container(
                padding: const EdgeInsets.all(16),
                child: Text(controller!.getSelection()!.text!),
              ),
              shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8)),
            );
          },
        );
        return true;
      },
    ),
  ],
)

Add an Issue for Questions or Problems

Look at the code in the example app included with this package for more usage details and example code, and feel free to add an issue to ask questions or report problems you find while using this package: https://github.com/ronjb/selectable/issues

selectable's People

Contributors

ronjb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

selectable's Issues

Support nested horizontal scrollviews?

From the doc it's stated that "the Selectable widget must be a descendant of the scrollable widget" in order to work. So I shall wrap Selectable around MarkdownBody but under a SingleChildScrollView for selectable to work (i.e. SingleChildScrollView -> Selectable -> MarkdownBody), example code.

However, if there are code blocks inside the markdown source, they are rendered as a horizontal scrollview, and selectable will not work well for that area. E.g. if I horizontally scroll then try to select, it selects the text that were in that position before the scroll happens.

Is there any workaround to make it work better for the nested srcollables?

Dependency issue

Because selectable >=0.2.6 depends on characters ^1.2.1 and every version of flutter from sdk depends on characters 1.2.0, selectable >=0.2.6 is incompatible with flutter from sdk.

selection handlers' color

Your package is awesome and very useful!

I'd like to suggest you to set the text selection handlers' color by using Theme.of(context).textSelectionTheme.selectionHandleColor or Theme.of(context).primaryColor . I think it would be just change line 219 in the text.selection.dart (release 0.1.1).

Best regards.

Ability to select all text given a default "Select All" option.

Hi, this is a really helpful package that wraps around any number of text widgets and allows selection.
In my case, a requirement is to select all text given a default "Select All" option. The number of text widgets inside changes dynamically so giving a random index to selectWordsBetweenIndexes(start, end) ends up with different behavior.

Would it be possible for you to provide a Select All option too?

Thanks!

How to make a Text inside Selectable, unselectable and how to not select the white space in between widgets

As you can see, this image is with the use of the SelectionArea, so the Text JohnDoe is not selected (because I used a SelectionContainer.disabled on top of it) and the space in between widgets is not selected as well:

Screenshot_1666339628

This is with the use of Selectable, and the white space and also the JohnDoe Text are selected:
Screenshot_1666339799

How can I get the same result as the SelectionArea with Selectable?

I appreciate your answer to this question.

Couple of Questions

1. Interacting with Selected Spans
image

When a selection is active and the menu is displayed, what is the best approach to interact with the underlying Text / TextSpan objects? For example, if the 'Foo' menu action was intended to make the selected text bold how does the callback identify the spans that need to be affected?

In case it makes a difference, in my usecase every word in the text will always be its own TextSpan. Would you happen to have a simple example illustrating something like that?

2. Support for RtL Languages?
I didn’t see anything in the readme regarding limitations in support for top-to-bottom / right-to-left languages (Japanese, for example) but when looking through the comments in the source code I noted in common.dart that the doc comments for both extension SelectableExtOnListOfRect on List<Rect> and extension SelectableExtOnIterableOfRect that it assumes the rectangles are in ‘reading order’.

I don’t understand the code well enough yet to know how these extensions are used within the package so I am unclear if this means the package itself is strictly limited to left-to-right or if this has a less significant overall impact.

If it does mean only left-to-right is supported by the package, is there something inherent to the package’s architecture that would make it difficult to add support for top-to-bottom / right-to-left or is this just something that you haven’t implemented yet since it isn’t needed for your own usecase? If the latter, what areas of the code would I need to address if I decided to try to tackle this myself? It isn’t critical for my usecase but would be extremely helpful to have. I’m just curious what level of challenge would likely be involved if I were to try to refactor the code to support it.

Thanks in advance for any guidance on the above!

Ability to select text in code?

Is there anything planned for making text selectable in code? I'm not sure how this would work—perhaps passing keys from the Text widgets to be selected?

Error: The non-abstract class 'SelectableFragment' is missing

/C:/Users/sameer/AppData/Local/Pub/Cache/hosted/pub.dev/float_column-2.1.1/lib/src/selectable_fragment.dart:19:7: Error: The non-abstract class 'SelectableFragment' is missing implementations for these members:

  • Selectable.boundingBoxes
    Try to either
  • provide an implementation,
  • inherit an implementation from a superclass or mixin,
  • mark the class as abstract, or
  • provide a 'noSuchMethod' implementation.

class SelectableFragment
^^^^^^^^^^^^^^^^^^
/C:/src/flutter/packages/flutter/lib/src/rendering/selection.dart:147:18: Context: 'Selectable.boundingBoxes' is defined here.
List get boundingBoxes;
^^^^^^^^^^^^^
Target kernel_snapshot failed: Exception

FAILURE: Build failed with an exception.

  • Where:
    Script 'C:\src\flutter\packages\flutter_tools\gradle\src\main\groovy\flutter.groovy' line: 1549

  • What went wrong:
    Execution failed for task ':app:compileFlutterBuildDebug'.

Process 'command 'C:\src\flutter\bin\flutter.bat'' finished with non-zero exit value 1

  • Try:

Run with --stacktrace option to get the stack trace.
Run with --info or --debug option to get more log output.
Run with --scan to get full insights.

BUILD FAILED in 31s
Error: Gradle task assembleDebug failed with exit code 1

Crash when adjusting selection handles

Thanks for this very interesting package! I’m learning a lot from it and it seems to closely match my usecase.

While testing the Example app, I’ve encountered a crash on both MacOS and iOS.

ENVIRONMENT:

  • Platforms: both MacOS 12.3.1 and iOS 15.5 (simulated iPhone 5E)
  • Flutter v3.3.4
  • Selectable 0.2.4

REPRODUCE:

  • Build and launch the Example app.
  • Scroll to the bottom of the screen.
  • Double-click / double-tap on the word ‘laborum’ at the end of the text.
  • Grab the ‘end’ selection handle and move it to the right so that the ‘.’ is included in the selection. Now move it back to the left so that the ‘.’ is removed from the selection (you can hold the handle the entire time or you can release the handle after each movement and re-grab it; it doesn’t seem to make any difference). Repeat this process until an exception occurs (which happens seemingly randomly, though typically within 1-7 attempts).

NOTES:

  • The crash happens in many circumstances other than described in the steps above. However, those steps represent the fastest, most consistent way I’ve been able to trigger the exception.
  • It may be coincidental, but in all of the times I’ve triggered the exception I don’t recall it ever happening while moving the ‘start’ selection handle – only when moving the ‘end’ selection handle.
  • Also possibly coincidental, but attempting to drag the selection handle past the end of the text (pulling it downwards) and then doing the ‘repeat’ step above seems to increase the likelihood of a crash. However, the crash definitely can happen without attempting to select beyond the available text.
  • Attempting to trigger the exception doing the same steps as above but using ‘laborum’ in the middle or first paragraph either doesn’t trigger the exception or doesn’t do so within the number of repeats I’ve attempted (quite a few!). However, I've frequently been able to trigger the exception without involving the end of the text (just randomly making selections) though it can sometimes take much longer.

Here’s the exception and the stack track from one of the crashes:

Exception:
FlutterError (A RenderViewport was mutated in _RenderLayoutBuilder.performLayout.
The RenderObject was mutated when none of its ancestors is actively performing layout.
The RenderObject being mutated was:
RenderViewport#20990
The RenderObject that was mutating the said RenderViewport was:
_RenderLayoutBuilder#130e4 NEEDS-LAYOUT)

Stack Trace:
RenderObject._debugCanPerformMutations. (/flutter/packages/flutter/lib/src/rendering/object.dart:1630)
RenderObject._debugCanPerformMutations (/flutter/packages/flutter/lib/src/rendering/object.dart:1691)
RenderObject.markNeedsLayout (/flutter/packages/flutter/lib/src/rendering/object.dart:1840)
RenderBox.markNeedsLayout (/flutter/packages/flutter/lib/src/rendering/box.dart:2408)
ChangeNotifier.notifyListeners (/flutter/packages/flutter/lib/src/foundation/change_notifier.dart:351)
ScrollPosition.notifyListeners (/flutter/packages/flutter/lib/src/widgets/scroll_position.dart:985)
ScrollPosition.forcePixels (/flutter/packages/flutter/lib/src/widgets/scroll_position.dart:384)
ScrollPositionWithSingleContext.jumpTo (/flutter/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart:197)
ScrollPositionWithSingleContext.animateTo (/flutter/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart:176)
ScrollController.animateTo (/flutter/packages/flutter/lib/src/widgets/scroll_controller.dart:153)
SelectableBuildHelper._autoscroll (/selectable-main/lib/src/selectable_build_helper.dart:78)
SelectableBuildHelper.maybeAutoscroll (/selectable-main/lib/src/selectable_build_helper.dart:33)
_SelectableState.build. (/selectable-main/lib/src/selectable.dart:333)
_LayoutBuilderElement._layout.layoutCallback (/flutter/packages/flutter/lib/src/widgets/layout_builder.dart:119)
BuildOwner.buildScope (/flutter/packages/flutter/lib/src/widgets/framework.dart:2605)
_LayoutBuilderElement._layout (/flutter/packages/flutter/lib/src/widgets/layout_builder.dart:153)
RenderObject.invokeLayoutCallback. (/flutter/packages/flutter/lib/src/rendering/object.dart:2246)
PipelineOwner._enableMutationsToDirtySubtrees (/flutter/packages/flutter/lib/src/rendering/object.dart:1035)
RenderObject.invokeLayoutCallback (/flutter/packages/flutter/lib/src/rendering/object.dart:2246)
RenderConstrainedLayoutBuilder.rebuildIfNecessary (/flutter/packages/flutter/lib/src/widgets/layout_builder.dart:228)

tap to get index of text.

I love the package.
is that any way to get an index when I tap on any text?

for ex, I highlighted one Word.

then when I single tap on that highlighted word. need to open the information of that word.
so if you guys can help me to get the index of that text I can do that feature.

Title Overflow on long texts

When the title has a lot of text it throws the overflow error, I tried wrapping it in every way possible and tricking it to feed the title with text ellipsis instead of string, but with no success
Can you help please?

SelectableMenuItem(
      type: SelectableMenuItemType.copy,
      title: '........ a lot of text ......... ',
      isEnabled: (controller) => controller!.isTextSelected,
      handler: (controller) {
        return true;
      },
    ),

How to use ListView.builder?

I tried wrapping it around the outer layer of the ListView and found that if the ListView scrolls a distance before selecting, the selection will fail.
Thanks.

selectionChangedListener() not getting called

_selectionChangedListener() not getting called despite changing the selected text?

class _MyHomePageState extends State<MyHomePage> {
  final _selectionController = SelectableController();

  void _selectionChangedListener() {
    print('Changed 1');  //Not called
    setState(() {
      print('Changed 2'); //Not called
    });
  }

  @override
  void initState() {
    super.initState();
    if (kIsWeb) {
      // On web, disable the browser's context menu since this example uses a custom context menu
      BrowserContextMenu.disableContextMenu();
    }
    _selectionController.addListener(_selectionChangedListener);
  }

  @override
  void dispose() {
    if (kIsWeb) {
      BrowserContextMenu.enableContextMenu();
    }
    _selectionController
      ..removeListener(_selectionChangedListener)
      ..dispose();
    super.dispose();
  }
  
  // The rest of the code...
}

How to solve this error?

How to use this function in Customscrollview.
if i use this function in Customscrollview i Got This Error
36 pos 16: 'vp != null': is not true.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.