Search code examples
fluttertextfieldmultiline

Why the multi-line TextField in Flutter has additional vertical padding?


I'm implementing the multi-line TextField with dynamic height in Flutter. The TextField resizes based on the text’s height. Here’s my implementation:

class MyTextField extends StatefulWidget {
  const MyTextField({
    super.key,
    this.controller,
    this.focusNode,
    this.text,
    this.placeholder,
    this.textColor,
    this.fontSize,
    this.isCompleted = false,
  });

  final TextEditingController? controller;
  final FocusNode? focusNode;
  final String? text;
  final String? placeholder;
  final Color? textColor;
  final double? fontSize;
  final bool isCompleted;

  @override
  State<MyTextField> createState() => _MyTextFieldState();
}

class _MyTextFieldState extends State<MyTextField> {
  void _onTextChanged(String text) {
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final textStyle = TextStyle(
          color: widget.textColor,
          fontSize: widget.fontSize,
          height: 24 / 16,
        );

        final painter = TextPainter(
          text: TextSpan(
            text: widget.controller?.text, 
            style: textStyle
          ),
          textDirection: Directionality.of(context),
        )..layout(maxWidth: constraints.maxWidth);

        return SizedBox(
          height: painter.height,
          child: TextField(
            controller: widget.controller,
            focusNode: widget.focusNode,
            onChanged: _onTextChanged,
            style: textStyle.copyWith(
              decoration: widget.isCompleted
                  ? TextDecoration.lineThrough
                  : TextDecoration.none,
            ),
            maxLines: null,
            expands: true,
            cursorColor: Theme.of(context).primaryColor,
            cursorHeight: 16,
            decoration: InputDecoration(
              contentPadding: EdgeInsets.zero,
              border: InputBorder.none,
              isDense: true,
              hintText: widget.placeholder,
              hintStyle: textStyle.copyWith(
                color: Colors.grey.shade400,
              ),
            ),
          ),
        );
      },
    );
  }
}

The issue is that the height of the TextField increases dynamically as expected, but when I press “Enter” to add a newline, the text shifts upward, and the TextField becomes scrollable.

It seems like there’s some extra vertical padding, even though I set contentPadding to EdgeInsets.zero.

Why does this happen, and how can I remove the extra padding?


Solution

  • TextField includes some padding and margin around the text area, which may result in unexpected shifts when you add new lines. Set height to style of TextField, this controls the line height. The default is 1.0. You can set it to a lower value (like 1.2) to decrease the space between lines.

    style: TextStyle(
            height: 1.2,
          ),