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

Flutter实现TextField文本宽度自动压缩功能的技术咨询

实现单行自适应宽度压缩的TextField(Flutter)

要实现输入文本到达右侧边界时自动压缩字符宽度(保持高度不变)的TextField,核心思路是实时计算文本宽度与容器宽度的比例,动态压缩文本宽度,以下是具体实现方案:

核心实现步骤

  1. 测量文本宽度:使用TextPainter计算当前输入文本在默认样式下的实际宽度。
  2. 动态计算缩放比例:对比文本宽度与TextField的可用宽度,当文本超出边界时,按可用宽度/文本宽度计算宽度压缩比例,否则保持1.0不缩放。
  3. 单独压缩宽度:通过Transform.scale仅缩放文本的宽度维度(scaleX),保持高度(scaleY=1.0)不变,同时设置左对齐保证布局符合需求。

完整代码示例

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CompressibleTextField extends StatefulWidget {
  final TextStyle? style;
  final TextEditingController? controller;
  final InputDecoration? decoration;
  final TextInputType? keyboardType;
  final List<TextInputFormatter>? inputFormatters;

  const CompressibleTextField({
    super.key,
    this.style,
    this.controller,
    this.decoration,
    this.keyboardType,
    this.inputFormatters,
  });

  @override
  State<CompressibleTextField> createState() => _CompressibleTextFieldState();
}

class _CompressibleTextFieldState extends State<CompressibleTextField> {
  late TextEditingController _controller;
  double _scaleX = 1.0;
  late TextStyle _baseStyle;

  @override
  void initState() {
    super.initState();
    _controller = widget.controller ?? TextEditingController();
    _baseStyle = widget.style ?? const TextStyle(fontSize: 16);
    _controller.addListener(_updateScale);
  }

  @override
  void didUpdateWidget(covariant CompressibleTextField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.controller != oldWidget.controller) {
      _controller.removeListener(_updateScale);
      _controller = widget.controller ?? TextEditingController();
      _controller.addListener(_updateScale);
    }
    if (widget.style != oldWidget.style) {
      _baseStyle = widget.style ?? const TextStyle(fontSize: 16);
      _updateScale();
    }
  }

  @override
  void dispose() {
    _controller.removeListener(_updateScale);
    if (widget.controller == null) {
      _controller.dispose();
    }
    super.dispose();
  }

  void _updateScale() {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // 计算TextField的可用宽度(减去装饰内边距)
        final contentPadding = widget.decoration?.contentPadding ?? const EdgeInsets.symmetric(horizontal: 16);
        final availableWidth = constraints.maxWidth - contentPadding.horizontal;

        // 测量当前文本的实际宽度
        final textPainter = TextPainter(
          text: TextSpan(text: _controller.text, style: _baseStyle),
          textDirection: TextDirection.ltr,
        )..layout(maxWidth: double.infinity);

        // 计算宽度缩放比例
        if (textPainter.width > availableWidth && availableWidth > 0) {
          _scaleX = availableWidth / textPainter.width;
        } else {
          _scaleX = 1.0;
        }

        return Transform.scale(
          scaleX: _scaleX,
          scaleY: 1.0, // 固定高度不缩放
          alignment: Alignment.centerLeft, // 保持文本左对齐
          child: TextField(
            controller: _controller,
            style: _baseStyle.copyWith(height: 1.0), // 固定文本高度比例
            decoration: widget.decoration ?? const InputDecoration(
              contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
              border: OutlineInputBorder(),
            ),
            keyboardType: widget.keyboardType,
            inputFormatters: widget.inputFormatters,
            textAlign: TextAlign.left,
            maxLines: 1, // 强制单行输入
            expands: false,
          ),
        );
      },
    );
  }
}

// 使用示例
class CompressibleTextFieldDemo extends StatelessWidget {
  const CompressibleTextFieldDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("宽度自适应压缩TextField")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: CompressibleTextField(
          style: const TextStyle(fontSize: 20, color: Colors.black87),
          decoration: const InputDecoration(
            border: OutlineInputBorder(),
            hintText: "输入文本测试压缩效果",
          ),
        ),
      ),
    );
  }
}

关键说明

  • 可用宽度计算:必须减去TextField装饰的内边距,否则压缩比例会出现偏差。
  • 高度保持:通过TextStyle.height=1.0固定文本高度比例,配合Transform.scaleY=1.0确保文本高度不受宽度压缩影响。
  • 单行限制:设置maxLines=1强制文本单行显示,避免自动换行干扰压缩逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 04:45:36