205 lines
5.2 KiB
Dart
205 lines
5.2 KiB
Dart
import "package:flutter/widgets.dart";
|
|
|
|
// Renders text via TextPainter directly, bypassing any theme/font overrides
|
|
// from shadcn or other inherited themes. Use this when you need a specific
|
|
// font (e.g. google fonts) and the theme keeps clobbering it.
|
|
class AnaText extends StatefulWidget {
|
|
const AnaText(
|
|
this.text, {
|
|
required this.style,
|
|
this.textAlign = TextAlign.left,
|
|
this.maxLines,
|
|
super.key,
|
|
});
|
|
|
|
final String text;
|
|
final TextStyle style;
|
|
final TextAlign textAlign;
|
|
final int? maxLines;
|
|
|
|
@override
|
|
State<AnaText> createState() => _AnaTextState();
|
|
}
|
|
|
|
class _AnaTextState extends State<AnaText> {
|
|
int _fontVersion = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
PaintingBinding.instance.systemFonts.addListener(_onFontChange);
|
|
}
|
|
|
|
void _onFontChange() {
|
|
setState(() => _fontVersion++);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
PaintingBinding.instance.systemFonts.removeListener(_onFontChange);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return CustomPaint(
|
|
painter: _AnaTextPainter(
|
|
text: widget.text,
|
|
style: widget.style,
|
|
textAlign: widget.textAlign,
|
|
maxLines: widget.maxLines,
|
|
textDirection: Directionality.of(context),
|
|
fontVersion: _fontVersion,
|
|
),
|
|
|
|
child: _AnaTextSizer(
|
|
text: widget.text,
|
|
style: widget.style,
|
|
maxLines: widget.maxLines,
|
|
textDirection: Directionality.of(context),
|
|
fontVersion: _fontVersion,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _AnaTextPainter extends CustomPainter {
|
|
_AnaTextPainter({
|
|
required this.text,
|
|
required this.style,
|
|
required this.textAlign,
|
|
required this.textDirection,
|
|
required this.fontVersion,
|
|
this.maxLines,
|
|
});
|
|
|
|
final String text;
|
|
final TextStyle style;
|
|
final TextAlign textAlign;
|
|
final TextDirection textDirection;
|
|
final int? maxLines;
|
|
final int fontVersion;
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final tp = TextPainter(
|
|
text: TextSpan(text: text, style: style),
|
|
textAlign: textAlign,
|
|
textDirection: textDirection,
|
|
maxLines: maxLines,
|
|
)..layout(maxWidth: size.width);
|
|
|
|
final dy = (size.height - tp.height) / 2;
|
|
tp.paint(canvas, Offset(0, dy.clamp(0.0, double.infinity)));
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(_AnaTextPainter old) =>
|
|
old.text != text ||
|
|
old.style != style ||
|
|
old.textAlign != textAlign ||
|
|
old.maxLines != maxLines ||
|
|
old.fontVersion != fontVersion;
|
|
}
|
|
|
|
|
|
// Invisible child that reports the natural text size back to the layout system
|
|
// so CustomPaint gets constrained correctly.
|
|
class _AnaTextSizer extends LeafRenderObjectWidget {
|
|
const _AnaTextSizer({
|
|
required this.text,
|
|
required this.style,
|
|
required this.textDirection,
|
|
required this.fontVersion,
|
|
this.maxLines,
|
|
});
|
|
|
|
final String text;
|
|
final TextStyle style;
|
|
final TextDirection textDirection;
|
|
final int? maxLines;
|
|
final int fontVersion;
|
|
|
|
@override
|
|
RenderObject createRenderObject(BuildContext context) => _AnaTextSizerBox(
|
|
text: text,
|
|
style: style,
|
|
textDirection: textDirection,
|
|
maxLines: maxLines,
|
|
fontVersion: fontVersion,
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, _AnaTextSizerBox renderObject) {
|
|
renderObject
|
|
..text = text
|
|
..style = style
|
|
..textDirection = textDirection
|
|
..maxLines = maxLines
|
|
..fontVersion = fontVersion;
|
|
}
|
|
}
|
|
|
|
class _AnaTextSizerBox extends RenderBox {
|
|
_AnaTextSizerBox({
|
|
required String text,
|
|
required TextStyle style,
|
|
required TextDirection textDirection,
|
|
required int fontVersion,
|
|
int? maxLines,
|
|
}) : _text = text,
|
|
_style = style,
|
|
_textDirection = textDirection,
|
|
_maxLines = maxLines,
|
|
_fontVersion = fontVersion;
|
|
|
|
String _text;
|
|
TextStyle _style;
|
|
TextDirection _textDirection;
|
|
int? _maxLines;
|
|
int _fontVersion;
|
|
|
|
set text(String v) { if (_text == v) return; _text = v; markNeedsLayout(); }
|
|
set style(TextStyle v) { if (_style == v) return; _style = v; markNeedsLayout(); }
|
|
|
|
set textDirection(TextDirection v) {
|
|
if (_textDirection == v) return;
|
|
_textDirection = v;
|
|
markNeedsLayout();
|
|
}
|
|
|
|
set maxLines(int? v) { if (_maxLines == v) return; _maxLines = v; markNeedsLayout(); }
|
|
|
|
set fontVersion(int v) { if (_fontVersion == v) return; _fontVersion = v; markNeedsLayout(); }
|
|
|
|
TextPainter _buildPainter({double maxWidth = double.infinity}) {
|
|
return TextPainter(
|
|
text: TextSpan(text: _text, style: _style),
|
|
textDirection: _textDirection,
|
|
maxLines: _maxLines,
|
|
)..layout(maxWidth: maxWidth);
|
|
}
|
|
|
|
@override
|
|
double computeMinIntrinsicWidth(double height) => _buildPainter().width;
|
|
|
|
@override
|
|
double computeMaxIntrinsicWidth(double height) => _buildPainter().width;
|
|
|
|
@override
|
|
double computeMinIntrinsicHeight(double width) =>
|
|
_buildPainter(maxWidth: width).height;
|
|
|
|
@override
|
|
double computeMaxIntrinsicHeight(double width) =>
|
|
_buildPainter(maxWidth: width).height;
|
|
|
|
@override
|
|
void performLayout() {
|
|
final tp = _buildPainter();
|
|
size = constraints.constrain(Size(tp.width, tp.height));
|
|
}
|
|
|
|
@override
|
|
void paint(PaintingContext context, Offset offset) {}
|
|
}
|