aves/lib/widgets/common/basic/outlined_text.dart
2022-07-18 18:15:48 +02:00

82 lines
2.5 KiB
Dart

import 'dart:ui';
import 'package:flutter/material.dart';
class OutlinedText extends StatelessWidget {
final List<TextSpan> textSpans;
final double outlineWidth;
final Color outlineColor;
final double outlineBlurSigma;
final TextAlign? textAlign;
static const widgetSpanAlignment = PlaceholderAlignment.middle;
const OutlinedText({
super.key,
required this.textSpans,
double? outlineWidth,
Color? outlineColor,
double? outlineBlurSigma,
this.textAlign,
}) : outlineWidth = outlineWidth ?? 1,
outlineColor = outlineColor ?? Colors.black,
outlineBlurSigma = outlineBlurSigma ?? 0;
@override
Widget build(BuildContext context) {
// TODO TLAD [subtitles] fix background area for mixed alphabetic-ideographic text
// as of Flutter v2.2.2, the area computed for `backgroundColor` has inconsistent height
// in case of mixed alphabetic-ideographic text. The painted boxes depends on the script.
// Possible workarounds would be to use metrics from:
// - `TextPainter.getBoxesForSelection`
// - `Paragraph.getBoxesForRange`
// and paint the background at the bottom of the `Stack`
final hasOutline = outlineWidth > 0;
return Stack(
children: [
if (hasOutline)
ImageFiltered(
imageFilter: outlineBlurSigma > 0
? ImageFilter.blur(
sigmaX: outlineBlurSigma,
sigmaY: outlineBlurSigma,
)
: ImageFilter.matrix(
Matrix4.identity().storage,
),
child: Text.rich(
TextSpan(
children: textSpans.map(_toStrokeSpan).toList(),
),
textAlign: textAlign,
),
),
Text.rich(
TextSpan(
children: hasOutline ? textSpans.map(_toFillSpan).toList() : textSpans,
),
textAlign: textAlign,
),
],
);
}
TextSpan _toStrokeSpan(TextSpan span) => TextSpan(
text: span.text,
children: span.children,
style: (span.style ?? const TextStyle()).copyWith(
foreground: Paint()
..style = PaintingStyle.stroke
..color = outlineColor
..strokeWidth = outlineWidth,
),
);
TextSpan _toFillSpan(TextSpan span) => TextSpan(
text: span.text,
children: span.children,
style: (span.style ?? const TextStyle()).copyWith(
backgroundColor: Colors.transparent,
),
);
}