【问题标题】:Flutter - How to get the coordinates of the cursor in a TextField?Flutter - 如何在 TextField 中获取光标的坐标?
【发布时间】:2020-04-02 05:55:06
【问题描述】:

需要知道TextField中当前光标位置的dx和dy坐标。这是实现提及/标签功能所必需的,其中需要在 TextField 的光标下方几个像素处显示一个弹出窗口。

【问题讨论】:

    标签: flutter flutter-layout flutter-dependencies flutter-web


    【解决方案1】:

    您可以使用FocusNode 来获取文本字段本身的偏移量。然后使用TextPainter 类计算布局宽度,如post 所示,并使用它来定位您的标签。然后也许使用一些覆盖逻辑来显示标签,如here所示。

    1. 创建一个FocusNode 对象并将其附加到文本字段。
    2. 然后在onChanged 回调或其TextEditingController 的回调中继续使用FocusNode.offset.dxFocusNode.offset.dy 定位您的标签的逻辑。
    3. FocusNode 仅提供边界矩形偏移。所以你需要一个TextPainter 实例来计算新输入文本的宽度。为此,您需要提前定义 TextStyle
    4. 同时使用 2 和 3 中的值来计算标签的位置,并为视觉美学增加一些额外的偏移量。

    以下代码是使用上述技术的示例。此解决方案的实时版本可在此dartpad 中获得。

    // Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
    // for details. All rights reserved. Use of this source code is governed by a
    // BSD-style license that can be found in the LICENSE file.
    
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter  Show Text Tag Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(title: 'Flutter Show Text Tag demo'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
    
      FocusNode _focusNode = FocusNode();
      GlobalKey _textFieldKey = GlobalKey();
      TextStyle _textFieldStyle = TextStyle(fontSize: 20);
    
      @override
      void initState() {
        super.initState();
      }
    
      // Code reference for overlay logic from MTECHVIRAL's video
      // https://www.youtube.com/watch?v=KuXKwjv2gTY
    
      showOverlaidTag(BuildContext context, String newText) async {
    
        TextPainter painter = TextPainter(
          textDirection: TextDirection.ltr,
          text: TextSpan(
            style: _textFieldStyle,
            text: newText,
          ),
        );
        painter.layout();
    
    
        OverlayState overlayState = Overlay.of(context);
        OverlayEntry suggestionTagoverlayEntry = OverlayEntry(builder: (context) {
          return Positioned(
    
            // Decides where to place the tag on the screen.
            top: _focusNode.offset.dy + painter.height + 3,
            left: _focusNode.offset.dx + painter.width + 10,
    
            // Tag code.
            child: Material(
                elevation: 4.0,
                color: Colors.lightBlueAccent,          
                child: Text(
                  'Show tag here',
                  style: TextStyle(
                    fontSize: 20.0,
                  ),
                )),
          );
        });
        overlayState.insert(suggestionTagoverlayEntry);
    
        // Removes the over lay entry from the Overly after 500 milliseconds 
        await Future.delayed(Duration(milliseconds: 500));
        suggestionTagoverlayEntry.remove();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Container(
              child: TextField(
                focusNode: _focusNode,
                key: _textFieldKey,
                style: _textFieldStyle,
                onChanged: (String nextText) {
                  showOverlaidTag(context, nextText);
                },
              ),
              width: 400.0,
            ),
          ),
        );
      }
    }
    
    

    如下所示的屏幕截图。您必须调整位置以满足您的需要,如果您要使用它,还必须调整叠加层的持续时间/可见性逻辑。

    【讨论】:

    • 您能否扩展您的答案以使用多行文本字段?
    【解决方案2】:

    获取当前光标的坐标(也称为caret)在颤振的文本字段中,我认为您可以使用返回TextPainter > getOffsetForCaret方法>offset 绘制插入符号的位置。然后,从偏移量你可以得到插入符号的 x 和 y 组件

    观察下面代码中的xCarretyCarret,它们对应屏幕上光标的左上角坐标。 您可以通过将preferredLineHeight 添加到yCarret 来推断yCarretBottom 的位置。

    方法getOffsetForCaret需要一个caretPrototype,我们用Rect.fromLTWHTextField的属性cursorWidth给出的光标宽度。

    
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Get cursor (caret) position',
          debugShowCheckedModeBanner: false,
          home: MyHomePage(title: 'Get cursor (caret) position'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key? key, this.title}) : super(key: key);
    
      final String? title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      GlobalKey _textFieldKey = GlobalKey();
      TextStyle _textFieldStyle = TextStyle(fontSize: 20);
      TextEditingController _textFieldController = TextEditingController();
      late TextField _textField;
      double xCaret = 0.0;
      double yCaret = 0.0;
      double painterWidth = 0.0;
      double painterHeight = 0.0;
      double preferredLineHeight = 0.0;
    
      @override
      void initState() {
        super.initState();
    
        /// Listen changes on your text field controller
        _textFieldController.addListener(() {
          _updateCaretOffset(_textFieldController.text);
        });
      }
    
      void _updateCaretOffset(String text) {
        TextPainter painter = TextPainter(
          textDirection: TextDirection.ltr,
          text: TextSpan(
            style: _textFieldStyle,
            text: text,
          ),
        );
        painter.layout();
    
        TextPosition cursorTextPosition = _textFieldController.selection.base;
        Rect caretPrototype = Rect.fromLTWH(
            0.0, 0.0, _textField.cursorWidth, _textField.cursorHeight ?? 0);
        Offset caretOffset =
            painter.getOffsetForCaret(cursorTextPosition, caretPrototype);
        setState(() {
          xCaret = caretOffset.dx;
          yCaret = caretOffset.dy;
          painterWidth = painter.width;
          painterHeight = painter.height;
          preferredLineHeight = painter.preferredLineHeight;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        String text = '''
    xCaret: $xCaret
    yCaret: $yCaret
    yCaretBottom: ${yCaret + preferredLineHeight}
    ''';
    
        _textField = TextField(
          controller: _textFieldController,
          keyboardType: TextInputType.multiline,
          key: _textFieldKey,
          style: _textFieldStyle,
          minLines: 1,
          maxLines: 2,
        );
    
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title!),
          ),
          body: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Text(text),
                Padding(
                  child: _textField,
                  padding: EdgeInsets.all(40),
                ),
              ]),
        );
      }
    }
    

    【讨论】:

    • 我实现了这一点,并将Icon 放在PositionedStack 内,以在光标位置显示某些内容。尽管您的答案是迄今为止表现最好的答案,但它有点缺陷。插入更多文本后,Icon 将比光标向右移动。
    【解决方案3】:

    获取输入字段光标或插入符号当前位置的简单方法

    TextField(
      controller: _textController,
      onChanged: (value) {
        int cursorPos = _textController.selection.base.offset;
        print(cursorPos); // returns value current position where you just typed
      }
    )
    

    【讨论】:

    • 这不提供任何坐标。这只给出了 TextField 内插入符号的离散位置
    • 是的,这就是我所说的它将提供位置。
    • 但发帖者是在寻找坐标,而不是位置。
    猜你喜欢
    • 1970-01-01
    • 2022-08-15
    • 2010-12-30
    • 1970-01-01
    • 2021-05-22
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多