You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter的CustomPainter中实现可编辑文本框?

在CustomPainter中实现可编辑文本的可行方案

CustomPainter仅负责绘制内容,无法直接处理文本编辑的交互逻辑,你之前尝试直接调用TextField的渲染方法走不通——TextField是完整的UI组件,其渲染依赖Flutter的布局与交互框架,不能在paint方法里直接提取使用。下面给出两种实用方案:

方案一:Stack叠加CustomPaint与TextField

最直观的方式是用Stack把CustomPaint(负责绘制矩形)和TextField(负责文本编辑)叠加,通过定位让TextField正好落在你原本要绘制文本的位置,用TextEditingController统一控制内容。

示例代码:

class EditableTextPainterPage extends StatefulWidget {
  @override
  State<EditableTextPainterPage> createState() => _EditableTextPainterPageState();
}

class _EditableTextPainterPageState extends State<EditableTextPainterPage> {
  final TextEditingController _textController = TextEditingController(text: "可编辑文本");
  // 文本位置与尺寸,根据需求调整
  final Rect textRect = const Rect.fromLTWH(50, 50, 200, 40);

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 自定义绘制矩形
          CustomPaint(
            painter: RectanglePainter(textRect),
            size: MediaQuery.of(context).size,
          ),
          // 定位TextField到文本位置
          Positioned(
            left: textRect.left,
            top: textRect.top,
            width: textRect.width,
            height: textRect.height,
            child: TextField(
              controller: _textController,
              decoration: const InputDecoration(
                border: InputBorder.none, // 去掉默认边框,和绘制的矩形融合
                contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
              ),
              style: const TextStyle(fontSize: 16, color: Colors.black),
            ),
          ),
        ],
      ),
    );
  }
}

class RectanglePainter extends CustomPainter {
  final Rect textRect;
  RectanglePainter(this.textRect);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.grey.shade200
      ..style = PaintingStyle.fill;
    // 绘制背景矩形
    canvas.drawRect(textRect, paint);
    // 若需要在TextField未聚焦时显示静态文本,可在此绘制,但需和TextField内容同步
    // final textPainter = TextPainter(
    //   text: TextSpan(text: _textController.text, style: TextStyle(fontSize:16)),
    //   textDirection: TextDirection.ltr,
    // );
    // textPainter.layout();
    // textPainter.paint(canvas, Offset(textRect.left+8, textRect.top+8));
  }

  @override
  bool shouldRepaint(covariant RectanglePainter oldDelegate) {
    return oldDelegate.textRect != textRect;
  }
}

方案二:使用EditableText(更灵活的底层组件)

如果需要更精细的控制(比如完全在绘制流程里管理文本位置),可以用EditableText——它是TextField的底层组件,能直接和CustomPainter配合,你可以在CustomPainter绘制完矩形后,让EditableText的渲染对象在指定位置绘制可编辑文本。

示例代码核心部分:

class EditableCustomPainter extends StatefulWidget {
  @override
  State<EditableCustomPainter> createState() => _EditableCustomPainterState();
}

class _EditableCustomPainterState extends State<EditableCustomPainter> {
  final TextEditingController _controller = TextEditingController(text: "自定义位置的可编辑文本");
  final FocusNode _focusNode = FocusNode();
  final Rect _textArea = const Rect.fromLTWH(100, 100, 250, 50);

  @override
  void dispose() {
    _controller.dispose();
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          CustomPaint(
            painter: _BackgroundPainter(_textArea),
            size: MediaQuery.of(context).size,
          ),
          // 用EditableText实现自定义位置的编辑
          Positioned.fromRect(
            rect: _textArea,
            child: EditableText(
              controller: _controller,
              focusNode: _focusNode,
              style: const TextStyle(fontSize: 18, color: Colors.black),
              cursorColor: Colors.blue,
              backgroundCursorColor: Colors.grey,
              textDirection: TextDirection.ltr,
              onSubmitted: (value) => FocusScope.of(context).unfocus(),
            ),
          ),
        ],
      ),
    );
  }
}

class _BackgroundPainter extends CustomPainter {
  final Rect textArea;
  _BackgroundPainter(this.textArea);

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制矩形背景
    canvas.drawRect(
      textArea,
      Paint()..color = Colors.grey.shade300,
    );
  }

  @override
  bool shouldRepaint(covariant _BackgroundPainter oldDelegate) {
    return oldDelegate.textArea != textArea;
  }
}

关键说明

  • 不要试图在CustomPainter的paint方法里直接创建或渲染TextField/EditableText的渲染对象,因为渲染对象的创建依赖上下文和布局流程,paint方法只是绘制阶段,没有处理这些的能力。
  • TextSpan本身是用于静态富文本展示的,没有编辑功能,所以无法绑定TextEditingController,必须用支持编辑的组件(TextField/EditableText)来实现。

内容的提问来源于stack exchange,提问作者aCas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 03:12:18