Skip to content

SelectionArea with a multi-column layout does not work properly #153004

Open

Description

@anggaaryas

Steps to reproduce

  1. Use SelectionArea in Top of the widget
  2. Try to use some Scroll Widget or List View

Expected results

all text that can be selected should only need tap + drag gesture to select the text

Actual results

some Text Widget need to double tap before dragging to select the text .

I found some solution, like use SelectableText each Text. but the cons is the UI can do multiple selection in different widget / location. Any Selection scope for this?

Code sample

Code sample
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class Test2 extends StatefulWidget {
  @override
  State<Test2> createState() => _Test2State();
}

class _Test2State extends State<Test2> {
  @override
  Widget build(BuildContext context) {
    return SelectionArea(
        child: Material(
      child: Row(
        children: [
          SizedBox(
            width: 16,
          ),
          Container(
            padding: EdgeInsets.all(8),
            color: Colors.blue,
            child: Center(child: Text("Text 0")),
          ),
          SizedBox(
            width: 16,
          ),
          Expanded(
              child: Container(
                color: Colors.red,
                padding: EdgeInsets.all(8),
                height: 300,
                child: ListView(
                  children: [
                    SizedBox(
                      height: 40,
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [Text("Text 1"), Text("Text 2")],
                      ),
                    ),
                    Divider(
                      height: 16,
                    ),
                    Text("Text 3"),
                    Text("Text 4"),
                    Text("Text 5"),
                    Text("Text 6"),
                    Text("Text 7"),
                    Text("Text 8"),
                  ],
                ),
              )),
          SizedBox(
            width: 16,
          ),
          Container(
            padding: EdgeInsets.all(8),
            color: Colors.blue,
            child: Center(child: Text("Text 9")),
          ),
          SizedBox(
            width: 16,
          ),
          Container(
            padding: EdgeInsets.all(8),
            color: Colors.green,
            width: 500,
            child: SingleChildScrollView(
              child: SizedBox(
                height: 300,
                child: Column(
                  children: [
                    Text("Text 10"),
                    SizedBox(
                      height: 8,
                    ),
                    Text("Text 11"),
                    Text("Text 12"),
                    Text("Text 13"),
                    Text("Text 13"),
                    Text("Text 14"),
                    Text("Text 15"),
                    Text("Text 16"),
                    Text("Text 17"),
                  ],
                ),
              ),
            ),
          )
        ],
      ),
    ));
  }
}

Screenshots or Video

Screenshots / Video demonstration
Screen.Recording.2024-08-07.at.15.28.40.mov

Logs

Logs

Flutter Doctor:

Launching lib/main.dart on Chrome in debug mode...
Waiting for connection from debug service on Chrome...
Warning: In index.html:37: Local variable for "serviceWorkerVersion" is deprecated. Use "{{flutter_service_worker_version}}" template token instead.
Warning: In index.html:46: "FlutterLoader.loadEntrypoint" is deprecated. Use "FlutterLoader.load" instead.
This app is linked to the debug service: ws://127.0.0.1:49384/FNVrXC7RIGY=/ws
Debug service listening on ws://127.0.0.1:49384/FNVrXC7RIGY=/ws
Debug service listening on ws://127.0.0.1:49384/FNVrXC7RIGY=/ws

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[!] Flutter (Channel stable, 3.22.1, on macOS 14.5 23F79 darwin-arm64, locale en-ID)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.1)
[✓] VS Code (version 1.91.1)
[✓] Connected device (3 available)
[✓] Network resources

Activity

  1. darshankawar commented on Aug 7, 2024

    @darshankawar
    Member

    @anggaaryas
    Maybe you are hitting #150897 that you can check and confirm, which has been fixed recently #151138.
    It doesn't seem to be available in latest stable so you can switch to latest master and re-run your scenario to confirm.

  2. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Aug 7, 2024
  3. anggaaryas commented on Aug 7, 2024

    @anggaaryas
    Author

    @darshankawar still not fixed in master channel.

    But i found new behaviour in all flutter version. If i change the Row widget into Column, there are Ok, no strange behaviour

    Screen.Recording.2024-08-07.at.17.54.45.mov
  4. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Aug 7, 2024
  5. added
    f: selectionSelectableRegion, SelectionArea, SelectionContainer, Selectable, and related APIs
    on Aug 7, 2024
  6. darshankawar commented on Aug 8, 2024

    @darshankawar
    Member

    Thanks for the update. I tried on latest stable and master and observed same behavior. It doesn't select the text at first attempt, we need to double tap and then it lets us select the text. Verified on macOS desktp.

    stable, master flutter doctor -v
    [!] Flutter (Channel stable, 3.24.0, on macOS 12.2.1 21D62 darwin-x64, locale
        en-GB)
        • Flutter version 3.24.0 on channel stable at
          /Users/dhs/documents/fluttersdk/flutter
        ! Warning: `flutter` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
          your current Flutter SDK checkout at
          /Users/dhs/documents/fluttersdk/flutter. Consider adding
          /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
        ! Warning: `dart` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
          current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
          Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
          of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 80c2e84975 (9 days ago), 2024-07-30 23:06:49 +0700
        • Engine revision b8800d88be
        • Dart version 3.5.0
        • DevTools version 2.37.2
        • If those were intentional, you can disregard the above warnings; however
          it is recommended to use "git" directly to perform update checks and
          upgrades.
    
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    [!] Flutter (Channel master, 3.24.0-1.0.pre.509, on macOS 12.2.1 21D62
        darwin-x64, locale en-GB)
        • Flutter version 3.24.0-1.0.pre.509 on channel master at
          /Users/dhs/documents/fluttersdk/flutter
        ! Warning: `flutter` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
          your current Flutter SDK checkout at
          /Users/dhs/documents/fluttersdk/flutter. Consider adding
          /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
        ! Warning: `dart` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
          current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
          Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
          of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 925ad4e6fa (44 minutes ago), 2024-08-08 00:38:29 -0400
        • Engine revision 94cf8c8fad
        • Dart version 3.6.0 (build 3.6.0-127.0.dev)
        • DevTools version 2.38.0
        • If those were intentional, you can disregard the above warnings; however
          it is recommended to use "git" directly to perform update checks and
          upgrades.
    
    [!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
        • Android SDK at /Users/dhs/Library/Android/sdk
        ✗ cmdline-tools component is missing
          Run `path/to/sdkmanager --install "cmdline-tools;latest"`
          See https://developer.android.com/studio/command-line for more details.
        ✗ Android license status unknown.
          Run `flutter doctor --android-licenses` to accept the SDK licenses.
          See https://flutter.dev/docs/get-started/install/macos#android-setup for
          more details.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Build 13C100
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] IntelliJ IDEA Ultimate Edition (version 2021.3.2)
        • IntelliJ at /Applications/IntelliJ IDEA.app
        • Flutter plugin version 65.1.4
        • Dart plugin version 213.7228
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.29.0
    
    [✓] Connected device (3 available)
        • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios
          • iOS 15.3.1 19D52
        • macOS (desktop)           • macos                                    •
          darwin-x64     • macOS 12.2.1 21D62 darwin-x64
        • Chrome (web)              • chrome                                   •
          web-javascript • Google Chrome 109.0.5414.119
    
    [✓] Network resources
        • All expected network resources are available.
    
    ! Doctor found issues in 1 category.
          
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    
    
    
  7. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    in triagePresently being triaged by the triage team
    on Aug 8, 2024
  8. added
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    and removed on Aug 20, 2024
  9. added
    P2Important issues not at the top of the work list
    and removed
    team-accessibilityOwned by Framework Accessibility team (i.e. responsible for accessibility code in flutter/flutter)
    on Aug 22, 2024
  10. chunhtai commented on Aug 22, 2024

    @chunhtai
    Contributor

    seems like maybe a focus issue. cc @Renzo-Olivares

  11. anggaaryas commented on Sep 26, 2024

    @anggaaryas
    Author

    Hello, i found another temporary solution. So, we can add SelectionArea each Row children and assign the focusNode. and then, each focus node has listener. if a focus node has focus, unfocus all focus node. So maybe this is really focus issue. especially when using Row() widget.

      @override
      void initState() {
        super.initState();
    
        focusNodeLeft.addListener((){
          if(focusNodeLeft.hasFocus){
            focusNodeRight.unfocus();
            focusNodeCenter.unfocus();
          }
        });
    
        focusNodeCenter.addListener((){
          if(focusNodeCenter.hasFocus){
            focusNodeRight.unfocus();
            focusNodeLeft.unfocus();
          }
        });
    
        focusNodeRight.addListener((){
          if(focusNodeRight.hasFocus){
            focusNodeLeft.unfocus();
            focusNodeCenter.unfocus();
          }
        });
      }
    
    Screen.Recording.2024-09-26.at.11.47.32.mov
  12. Renzo-Olivares commented on Sep 27, 2024

    @Renzo-Olivares
    Contributor

    Hi @anggaaryas, thanks for reporting this issue. I've started debugging this, and I think part of the issue is with the screen ordering algorithm used by MultiSelectableSelectionContainerDelegate.

    I made a smaller minimal reproducible example, the text in the SingleChildScrollView is not selectable with tap + drag:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(
        const MaterialApp(
          home: Scaffold(
            body: Test2(),
          ),
        ),
      );
    }
    
    class Test2 extends StatefulWidget {
      const Test2({super.key});
    
      @override
      State<Test2> createState() => _Test2State();
    }
    
    class _Test2State extends State<Test2> {
      @override
      Widget build(BuildContext context) {
        return const SelectionArea(
          child: Row(
            children: [
              Text("Text 0"),
              SingleChildScrollView(
                child: SizedBox(
                  height: 50.0,
                  child: Column(
                    children: <Widget>[
                      Text("Text 1"),
                    ],
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    • With SingleChildScrollView:
      First text widget selectable rect: Rect.fromLTRB(0.0, 15.0, 41.9, 35.0)
      Scrollable selectable rect: Rect.fromLTRB(41.9, 0.0, 81.5, 50.0)

    In this case the algorithm orders the selectables like: [First text widget, Scrollable], so when we tap and drag on the scrollable, the event goes to the first text widget first, since the tapped position will be somewhere above the first text widget, it will return SelectionResult.prev and the event dispatch will stop there, never reaching the Scrollable.

    • Without SingleChildScrollView:
      First text widget rect: Rect.fromLTRB(0.0, 15.0, 41.9, 35.0)
      Scrollable rect: Rect.fromLTRB(41.9, 0.0, 81.5, 20.0)

    In this case the algorithm orders the selectables like: [Scrollable, First text widget], so when we tap and drag on the scrollable, the event goes to it first and we are able to select as expected.

    The main difference between the rects in both situations is that with the Scrollable, the bottom of the Scrollable selectable rect is lower than the first text widgets rect, which means they overlap vertically, so it defers to a horizontal comparison and gets ordered after the first text widget. I think this is because when wrapping with a Scrollable it tries to take up the maximum height of its children, in this case 50.0.

    The reason double tap + drag works is because a double tap, first looks for the selectable with a rect containing the tapped position, rather than starting at the first selectable in order and iterating from there.

  13. anggaaryas commented on Sep 30, 2024

    @anggaaryas
    Author

    Thank you for the response. Btw is this related to _startNewMouseSelectionGesture method too? because if we use double tap and triple tap, it will success to select the selected widget. Maybe related to this issue too #142040 . And maybe related to which Selectable candidates that need to add in List<Selectable> selectables (method _flushAdditions) ?

  14. kevmoo commented on Nov 12, 2024

    @kevmoo
  15. omar-selo commented on May 26, 2025

    @omar-selo
  16. changed the title [-]Selection Area sometimes need to double click on certain widget[/-] [+]SelectionArea with a multi-column layout does not work properly[/+] on Feb 4, 2026
  17. Renzo-Olivares commented on Feb 4, 2026

    @Renzo-Olivares
    Contributor

    Adding more data points from #179552

    Code sample
    SelectionArea(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Container(
                    width: _width,
                    height: _height,
                    color: Color(0xFFEAEAEA),
                    child: Text.rich(
                      textAlign: TextAlign.justify,
                      TextSpan(
                        children: [
                          WidgetSpan(
                            child: Container(
                              padding: EdgeInsetsGeometry.all(5),
                              child: Text.rich(
                                textAlign: TextAlign.justify,
                                TextSpan(
                                  children: [
                                    WidgetSpan(child: SizedBox(width: 30)),
                                    TextSpan(
                                      text:
                                          "我这么说,可能会让不少人不以为然,或者让他们大吃一惊。他们会问,根据我们以前读过的教科书,中国境内不是有生活在170万年前的元谋人、70万年前的蓝田人、50万年前的北京人等古人类吗?你怎么能说我们的祖先人群进入中国最多只有四五万年的时间呢?这不是要把中华民族的历史一下子砍掉一百五六十万年吗?",
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          ),
                          WidgetSpan(
                            child: Container(
                              padding: EdgeInsetsGeometry.all(5),
                              child: Text.rich(
                                textAlign: TextAlign.justify,
                                TextSpan(
                                  children: [
                                    WidgetSpan(child: SizedBox(width: 30)),
                                    TextSpan(
                                      text:
                                          "当然,如果说中国的历史要从中国这片大地上出现人类讲起,那么中国史确实应当从元谋人、蓝田人开篇。但是元谋人、蓝田人、北京人等的祖先,与全球现代人共同的祖先人群之间,早在大约180万年前,就在物种演化的道路上分道扬镳了。所以元谋人、蓝田人、北京人,并不是现代中国人的直系祖先。如果我们想追溯的是一部现代中国人的历史,那么它就真的只能有四五万年可说。当然四五万年也已经足够漫长了!",
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                  Container(width: 20),
                  Container(
                    width: _width,
                    height: _height,
                    color: Color(0xFFEAEAEA),
                    child: Text.rich(
                      textAlign: TextAlign.justify,
                      TextSpan(
                        children: [
                          WidgetSpan(
                            child: Container(
                              padding: EdgeInsetsGeometry.all(5),
                              child: Text.rich(
                                textAlign: TextAlign.justify,
                                TextSpan(
                                  children: [
                                    WidgetSpan(child: SizedBox(width: 30)),
                                    TextSpan(
                                      text:
                                          "现在就从最早也最为漫长的那个时段讲起。所谓自南向北,是指绝大部分现代中国人的祖先,分成不多的几批人群,先后从东南亚进入今天中国境内,并且在寻找生存空间的艰苦探索中,朝着各个可能的方向挺进,逐渐自南向北扩散到中国全境的那个过程。它发生在从公元前四五万年直到公元前两千年这样一个极其漫长的历史时期之中。",
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          ),
                          WidgetSpan(
                            child: Container(
                              padding: EdgeInsetsGeometry.all(5),
                              child: Text.rich(
                                textAlign: TextAlign.justify,
                                TextSpan(
                                  children: [
                                    WidgetSpan(child: SizedBox(width: 30)),
                                    TextSpan(
                                      text:
                                          "这也就是说,四五万年之前,中国大地上旧石器时代中期以前的全部史前文化以及部分晚期石器文化的创造者们,其实是直立人和早期智人。他们比现代中国人的祖先更早生活在这里。所以中国历史上最早的一次重大变局,实际上正应该是现代中国人的祖先如何取代直立人和早期智人的那段神秘的故事",
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            )

    From my investigation the root cause is as follows:

    SelectionUtils.adjustDragOffset +
    _SelectableFragment._updateSelectionEdge

    Sequence of events:

    Start a drag on some text in the first line of the second column. At this point SelectionArea will dispatch a edge update event for the first RenderParagraph corresponding to the first Text widget in the first column. _SelectableFragment._updateSelectionEdge uses SelectionUtils.adjustDragOffset and calculates an adjusted position set to the bottom right of the selectable rect. This position is then used to look in the next selectable for the selection with SelectionResult.next or the previous with SelectionResult.previous. In our case since the adjusted position corresponds with the end of the content in the selectable SelectionResult.next is returned.

    We then move onto the second RenderParagraph corresponding to the second text widget in the first column. _SelectableFragment._updateSelectionEdge uses SelectionUtils.adjustDragOffset and calculates an adjusted position set to the top left of the selectable rect. This is important since this adjusted position is what prevents us from setting a correct selection. Because the adjusted position is set to the top left, this corresponds to the beginning of the selectable content and SelectionResult.previous is returned. This puts us at the first text widget in the first column again, so we can never reach the selectable in the second column which is where we expect our selection to start.

    Selectable order is correct here in this scenario but we still can't reach the selectable we want.

  18. moved this from Incoming Issues to Prioritized in SelectableRegion Improvementson Mar 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listf: selectionSelectableRegion, SelectionArea, SelectionContainer, Selectable, and related APIsfound in release: 3.24Found to occur in 3.24frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions