The-Agency/lib/ui/widgets/common/ana_text.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) {}
}