'How to decorate text stroke in Flutter?

How to decorate text stroke in Flutter? It's like -webkit-text-stroke - CSS



Solution 1:[1]

Stroke has been possible without workarounds since the addition of foreground paints in TextStyle. An explicit example of stroke under fill bordered text has been added in the TextStyle documentation: https://master-api.flutter.dev/flutter/painting/TextStyle-class.html#painting.TextStyle.6

This example is reproduced here:

enter image description here

Stack(
  children: <Widget>[
    // Stroked text as border.
    Text(
      'Greetings, planet!',
      style: TextStyle(
        fontSize: 40,
        foreground: Paint()
          ..style = PaintingStyle.stroke
          ..strokeWidth = 6
          ..color = Colors.blue[700],
      ),
    ),
    // Solid text as fill.
    Text(
      'Greetings, planet!',
      style: TextStyle(
        fontSize: 40,
        color: Colors.grey[300],
      ),
    ),
  ],
)

Stroke by itself is possible by removing the Stack and just using the first stroke Text widget by itself. The stroke/fill order can also be adjusted by swapping the first and second Text widget.

Solution 2:[2]

I was also looking for this, wasn't able to find it. But I did find a workaround using 4 shadows in the TextStyle:

Text("Border test",
    style: TextStyle(
      inherit: true,
      fontSize: 48.0,
      color: Colors.pink,
      shadows: [
        Shadow( // bottomLeft
          offset: Offset(-1.5, -1.5),
          color: Colors.white
        ),
        Shadow( // bottomRight
          offset: Offset(1.5, -1.5),
          color: Colors.white
        ),
        Shadow( // topRight
          offset: Offset(1.5, 1.5),
          color: Colors.white
        ),
        Shadow( // topLeft
          offset: Offset(-1.5, 1.5),
          color: Colors.white
        ),
      ]
    ),
);

I also opened an Issue on GitHub: https://github.com/flutter/flutter/issues/24108

Solution 3:[3]

Inspired by this article, to achieve the effect, I prefer to use a technique that mixes two Text widgets and TextStype.foreground property with custom Paint():

class StrokeText extends StatelessWidget {
  final String text;
  final double fontSize;
  final FontWeight fontWeight;
  final Color color;
  final Color strokeColor;
  final double strokeWidth;

  const StrokeText(
    this.text, {
    Key key,
    this.fontSize,
    this.fontWeight,
    this.color,
    this.strokeColor,
    this.strokeWidth,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Text(
          text,
          style: TextStyle(
            fontSize: fontSize,
            fontWeight: fontWeight,
            foreground: Paint()..color = color,
          ),
        ),
        Text(
          text,
          style: TextStyle(
            fontSize: fontSize,
            fontWeight: fontWeight,
            foreground: Paint()
              ..strokeWidth = strokeWidth
              ..color = strokeColor
              ..style = PaintingStyle.stroke,
          ),
        ),
      ],
    );
  }
}

Solution 4:[4]

If you prefer the shadows method, you can configure the stroke width using :

/// Outlines a text using shadows.
static List<Shadow> outlinedText({double strokeWidth = 2, Color strokeColor = Colors.black, int precision = 5}) {
  Set<Shadow> result = HashSet();
  for (int x = 1; x < strokeWidth + precision; x++) {
    for(int y = 1; y < strokeWidth + precision; y++) {
      double offsetX = x.toDouble();
      double offsetY = y.toDouble();
      result.add(Shadow(offset: Offset(-strokeWidth / offsetX, -strokeWidth / offsetY), color: strokeColor));
      result.add(Shadow(offset: Offset(-strokeWidth / offsetX, strokeWidth / offsetY), color: strokeColor));
      result.add(Shadow(offset: Offset(strokeWidth / offsetX, -strokeWidth / offsetY), color: strokeColor));
      result.add(Shadow(offset: Offset(strokeWidth / offsetX, strokeWidth / offsetY), color: strokeColor));
    }
  }
  return result.toList();
}

Use it like this :

Text(
  'My text',
  style: TextStyle(shadows: outlinedText(strokeColor: Colors.blue)),
);

Solution 5:[5]

Inspired by @Gary Qian's answer

Widget textWithStroke({String text, String fontFamily, double fontSize: 12, double strokeWidth: 1, Color textColor: Colors.white, Color strokeColor: Colors.black}) {
        return Stack(
          children: <Widget>[
            Text(
              text,
              style: TextStyle(
                fontSize: fontSize,
                fontFamily: fontFamily,
                foreground: Paint()
                  ..style = PaintingStyle.stroke
                  ..strokeWidth = strokeWidth
                  ..color = strokeColor,
              ),
            ),
            Text(text, style: TextStyle(fontFamily: fontFamily, fontSize: fontSize, color: textColor)),
          ],
        );
      }

Solution 6:[6]

I created a package using the same logic shared here.

I also make it possible to add multiple strokes at once.

package: https://pub.dev/packages/outlined_text

DEMO

enter image description here

Solution 7:[7]

This is @Aleh's answer migrated to null-safety and with some more flexibility. Simply paste this inside a new file, and use freely.

import 'package:flutter/widgets.dart';

/// Places a stroke around text to make it appear outlined
///
/// Adapted from https://stackoverflow.com/a/55559435/11846040
class OutlinedText extends StatelessWidget {
  /// Text to display
  final String text;

  /// Original text style (if you weren't outlining)
  ///
  /// Do not specify `color` inside this: use [textColor] instead.
  final TextStyle style;

  /// Text color
  final Color textColor;

  /// Outline stroke color
  final Color strokeColor;

  /// Outline stroke width
  final double strokeWidth;

  /// Places a stroke around text to make it appear outlined
  ///
  /// Adapted from https://stackoverflow.com/a/55559435/11846040
  const OutlinedText(
    this.text, {
    Key? key,
    this.style = const TextStyle(),
    required this.textColor,
    required this.strokeColor,
    required this.strokeWidth,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Text(
          text,
          style: style.copyWith(foreground: Paint()..color = textColor),
        ),
        Text(
          text,
          style: style.copyWith(
            foreground: Paint()
              ..strokeWidth = strokeWidth
              ..color = strokeColor
              ..style = PaintingStyle.stroke,
          ),
        ),
      ],
    );
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Gary Qian
Solution 2 PieterAelse
Solution 3 Aleh
Solution 4
Solution 5 New Dev
Solution 6 Jorge Wander Santana UreƱa
Solution 7 JaffaKetchup