Can a single TextField in flutter have variable line height?

180

I've found a solution. All I needed to do was to play around with the strutStyle property of the TextField.

As the documentation states:

The strut style used for the vertical layout.

StrutStyle is used to establish a predictable vertical layout. Since fonts may vary depending on user input and due to font fallback, StrutStyle.forceStrutHeight is enabled by default to lock all lines to the height of the base TextStyle, provided by style. This ensures the typed text fits within the allotted space.

Share:
180
Author by

dev_mush

I work as a full stack software developer (mainly focused on development and deployment of mobile products) at ufirst, an Italian startup which aim is to solve one of the most tedious problems of the world: Queues! Now my focus is oriented in building great apps with Flutter. In my free time I try to be in the present. Constantly seeking for new stuff to discover, mildly interested in generative arts obsessed by free climbing, piano playing and pizza🍕.

Updated on December 30, 2022

Comments

  • dev_mush less than a minute

    I'm implementing a simple rich text editor that renders text with a text editing controller that recognises basic markdown syntax, I'll link some code down below.

    Everything works fine, the only problem I'm having is when a text style requires a bigger line height, for instance an # h1 that should be rendered as a title and therefore require a bigger line height overlaps over the previous line, as you can see in the screenshot below.

    I've not been able so far to make the line height in a TextView variable based on the style of the text that is being displayed, is such thing even achievable in a Flutter TextView?

    Here's a snippet of my text editing controller and a screenshot detailing my problem.

    import 'dart:ui';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    class AddNotePage extends StatelessWidget {
      final TextEditingController _controller = MarkdownTextEditingController();
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Add Note'),
          ),
          body: GestureDetector(
            onVerticalDragDown: (_) {
              FocusScopeNode currentFocus = FocusScope.of(context);
              if (!currentFocus.hasPrimaryFocus) {
                currentFocus.unfocus();
              }
            },
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Expanded(
                  child: TextField(
                    style: defaultTextStyle,
                    controller: _controller,
                    decoration: InputDecoration(
                      hintText: "Insert your message",
                      border: UnderlineInputBorder(
                        borderSide: BorderSide.none,
                      ),
                    ),
                    scrollPadding: EdgeInsets.all(20.0),
                    keyboardType: TextInputType.multiline,
                    maxLines: null,
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    const Map<String, TextStyle> defaultMarkdownStyleMap = {
      r'^# .*?$': TextStyle(
        fontWeight: FontWeight.bold,
        fontSize: 50,
      ),
      r'^## .*?$': TextStyle(
        fontWeight: FontWeight.bold,
        fontSize: 40,
      ),
      r'^### .*?$': TextStyle(
        fontWeight: FontWeight.bold,
        fontSize: 30,
      ),
      r'__(.*?)\__': TextStyle(fontStyle: FontStyle.italic, fontSize: 20),
      r'~~(.*?)~~': TextStyle(decoration: TextDecoration.lineThrough, fontSize: 20),
      r'\*\*(.*?)\*\*': TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
    };
    const TextStyle defaultTextStyle = TextStyle(fontSize: 20);
    class MarkdownTextEditingController extends TextEditingController {
      final Map<String, TextStyle> styleMap;
      final Pattern pattern;
      MarkdownTextEditingController({this.styleMap = defaultMarkdownStyleMap})
          : pattern = RegExp(
                styleMap.keys.map((key) {
                  return key;
                }).join('|'),
                multiLine: true);
      @override
      TextSpan buildTextSpan(
          {required BuildContext context,
          TextStyle? style,
          required bool withComposing}) {
        final List<InlineSpan> children = [];
        text.splitMapJoin(
          pattern,
          onMatch: (Match match) {
            TextStyle? markdownStyle = styleMap[styleMap.keys.firstWhere(
              (e) {
                return RegExp(e).hasMatch(match[0]!);
              },
            )];
            children.add(TextSpan(
              text: match[0],
              style: style!.merge(markdownStyle),
            ));
            return "";
          },
          onNonMatch: (String text) {
            children
                .add(TextSpan(text: text, style: style!.merge(defaultTextStyle)));
            return "";
          },
        );
        return TextSpan(style: style, children: children);
      }
    }