• Flutter TextField 点击时如何定位光标位置


    在使用Flutter 输入框TextField组件时,一直好奇组件是如何定位到光标位置,在学习了解TextField组件源码后,对这个问题有初步了解。

    查看

    TextField 源码 _CupertinoTextFieldState build方法

    1. return Semantics(
    2. enabled: enabled,
    3. onTap: !enabled || widget.readOnly ? null : () {
    4. if (!controller.selection.isValid) {
    5. controller.selection = TextSelection.collapsed(offset: controller.text.length);
    6. }
    7. _requestKeyboard();
    8. },
    9. onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus,
    10. child: IgnorePointer(
    11. ignoring: !enabled,
    12. child: Container(
    13. decoration: effectiveDecoration,
    14. color: !enabled && effectiveDecoration == null ? disabledColor : null,
    15. child: _selectionGestureDetectorBuilder.buildGestureDetector(
    16. behavior: HitTestBehavior.translucent,
    17. child: Align(
    18. alignment: Alignment(-1.0, _textAlignVertical.y),
    19. widthFactor: 1.0,
    20. heightFactor: 1.0,
    21. child: _addTextDependentAttachments(paddedEditable, textStyle, placeholderStyle),
    22. ),
    23. ),
    24. ),
    25. ),
    26. );

    该build方法中使用了_selectionGestureDetectorBuilder.buildGestureDetector方法,这里是一个手势识别器。也就是这个方法里面监听处理鼠标点击(Web中使用)和点击手势。

    1. Widget buildGestureDetector({
    2. Key? key,
    3. HitTestBehavior? behavior,
    4. required Widget child,
    5. }) {
    6. return TextSelectionGestureDetector(
    7. key: key,
    8. onTapDown: onTapDown,
    9. onForcePressStart: delegate.forcePressEnabled ? onForcePressStart : null,
    10. onForcePressEnd: delegate.forcePressEnabled ? onForcePressEnd : null,
    11. onSecondaryTap: onSecondaryTap,
    12. onSecondaryTapDown: onSecondaryTapDown,
    13. onSingleTapUp: onSingleTapUp,
    14. onSingleTapCancel: onSingleTapCancel,
    15. onSingleLongTapStart: onSingleLongTapStart,
    16. onSingleLongTapMoveUpdate: onSingleLongTapMoveUpdate,
    17. onSingleLongTapEnd: onSingleLongTapEnd,
    18. onDoubleTapDown: onDoubleTapDown,
    19. onDragSelectionStart: onDragSelectionStart,
    20. onDragSelectionUpdate: onDragSelectionUpdate,
    21. onDragSelectionEnd: onDragSelectionEnd,
    22. behavior: behavior,
    23. child: child,
    24. );
    25. }

    这里我们重点查看单击onSingleTapUp方法。

    1. void onSingleTapUp(TapUpDetails details) {
    2. if (_isShiftTapping) {
    3. _isShiftTapping = false;
    4. return;
    5. }
    6. if (delegate.selectionEnabled) {
    7. switch (defaultTargetPlatform) {
    8. case TargetPlatform.iOS:
    9. case TargetPlatform.macOS:
    10. switch (details.kind) {
    11. case PointerDeviceKind.mouse:
    12. case PointerDeviceKind.stylus:
    13. case PointerDeviceKind.invertedStylus:
    14. // Precise devices should place the cursor at a precise position.
    15. renderEditable.selectPosition(cause: SelectionChangedCause.tap);
    16. break;
    17. case PointerDeviceKind.touch:
    18. case PointerDeviceKind.unknown:
    19. // On macOS/iOS/iPadOS a touch tap places the cursor at the edge
    20. // of the word.
    21. renderEditable.selectWordEdge(cause: SelectionChangedCause.tap);
    22. break;
    23. }
    24. break;
    25. case TargetPlatform.android:
    26. case TargetPlatform.fuchsia:
    27. case TargetPlatform.linux:
    28. case TargetPlatform.windows:
    29. renderEditable.selectPosition(cause: SelectionChangedCause.tap);
    30. break;
    31. }
    32. }
    33. }

    重点方法   renderEditable.selectPosition(cause: SelectionChangedCause.tap);

    1. void selectPosition({ required SelectionChangedCause cause }) {
    2. selectPositionAt(from: _lastTapDownPosition!, cause: cause);
    3. }
    4. /// Select text between the global positions [from] and [to].
    5. ///
    6. /// [from] corresponds to the [TextSelection.baseOffset], and [to] corresponds
    7. /// to the [TextSelection.extentOffset].
    8. void selectPositionAt({ required Offset from, Offset? to, required SelectionChangedCause cause }) {
    9. assert(cause != null);
    10. assert(from != null);
    11. _layoutText(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
    12. final TextPosition fromPosition = _textPainter.getPositionForOffset(globalToLocal(from - _paintOffset));
    13. final TextPosition? toPosition = to == null
    14. ? null
    15. : _textPainter.getPositionForOffset(globalToLocal(to - _paintOffset));
    16. final int baseOffset = fromPosition.offset;
    17. final int extentOffset = toPosition?.offset ?? fromPosition.offset;
    18. final TextSelection newSelection = TextSelection(
    19. baseOffset: baseOffset,
    20. extentOffset: extentOffset,
    21. affinity: fromPosition.affinity,
    22. );
    23. _setSelection(newSelection, cause);
    24. }
    selectPositionAt 这个方法中,通过点击位置计算判断光标应该显示在文本第几个字符。

    细节:

    final TextPosition fromPosition = _textPainter.getPositionForOffset(globalToLocal(from - _paintOffset));

    这里用到了TextPainer.getPositionForOffset方法

    1. /// Returns the position within the text for the given pixel offset.
    2. TextPosition getPositionForOffset(Offset offset) {
    3. assert(!_debugNeedsLayout);
    4. return _paragraph!.getPositionForOffset(offset);
    5. }

    这个方法就是根据传入的offset计算在字符第几个字符中。

    计算好后生成一个TextSelection对象,传递给 RenderEditable

    1. final TextSelection newSelection = TextSelection(
    2. baseOffset: baseOffset,
    3. extentOffset: extentOffset,
    4. affinity: fromPosition.affinity,
    5. );

    class _Editable extends MultiChildRenderObjectWidget {}

    _Editable 也就是输入框重点的RenderObject,主要负责绘制工作

    1. @override
    2. void updateRenderObject(BuildContext context, RenderEditable renderObject) {
    3. renderObject
    4. ///省略
    5. ..selection = value.selection
    6. ///省略
    7. }

     这里的value.selection 也就是上面的TextSelection对象。

  • 相关阅读:
    python软件许可License文件生成
    2023下半年创业风口项目:实景自动无人直播!揭秘3大好处!
    vue七牛云视频直传
    MIT6.824-lab3A-Key/value service without snapshots(基本的KV服务)
    如何让FileBeat支持http的output插件
    深度详解 Android R(11.0)Service 启动过程
    Allegro DFM Ravel Rule检查工具介绍
    算法设计与分析100例子(C语言版)
    php7.3安装phalcon扩展
    学习-Java类和对象之包的定义
  • 原文地址:https://blog.csdn.net/bawomingtian123/article/details/126940828