feat: dyn show more

Closes #1629

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-10-19 20:12:28 +08:00
parent 287cea4d6c
commit cc00b2cc39
4 changed files with 38 additions and 1 deletions

View File

@@ -78,6 +78,7 @@ class RenderParagraph extends RenderBox
Color? selectionColor, Color? selectionColor,
SelectionRegistrar? registrar, SelectionRegistrar? registrar,
required Color primary, required Color primary,
VoidCallback? onShowMore,
}) : assert(text.debugAssertIsValid()), }) : assert(text.debugAssertIsValid()),
assert( assert(
maxLines == null || maxLines == null ||
@@ -93,6 +94,7 @@ class RenderParagraph extends RenderBox
_softWrap = softWrap, _softWrap = softWrap,
_overflow = overflow, _overflow = overflow,
_selectionColor = selectionColor, _selectionColor = selectionColor,
_onShowMore = onShowMore,
_textPainter = TextPainter( _textPainter = TextPainter(
text: text, text: text,
textAlign: textAlign, textAlign: textAlign,
@@ -294,6 +296,8 @@ class RenderParagraph extends RenderBox
_disposeSelectableFragments(); _disposeSelectableFragments();
_textPainter.dispose(); _textPainter.dispose();
_textIntrinsicsCache?.dispose(); _textIntrinsicsCache?.dispose();
_tapGestureRecognizer?.dispose();
_tapGestureRecognizer = null;
_morePainter?.dispose(); _morePainter?.dispose();
_morePainter = null; _morePainter = null;
super.dispose(); super.dispose();
@@ -553,6 +557,16 @@ class RenderParagraph extends RenderBox
@override @override
@protected @protected
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
if (_morePainter case final textPainter?) {
late final height = _textPainter.height;
if (position.dx < textPainter.width &&
position.dy > height &&
position.dy < height + textPainter.height) {
result.add(HitTestEntry(_moreTextSpan));
return true;
}
}
final GlyphInfo? glyph = _textPainter.getClosestGlyphForOffset(position); final GlyphInfo? glyph = _textPainter.getClosestGlyphForOffset(position);
// The hit-test can't fall through the horizontal gaps between visually // The hit-test can't fall through the horizontal gaps between visually
// adjacent characters on the same line, even with a large letter-spacing or // adjacent characters on the same line, even with a large letter-spacing or
@@ -680,9 +694,20 @@ class RenderParagraph extends RenderBox
} }
} }
VoidCallback? _onShowMore;
set onShowMore(VoidCallback? onShowMore) {
_onShowMore = onShowMore;
_tapGestureRecognizer?.onTap = onShowMore;
}
TapGestureRecognizer? _tapGestureRecognizer;
TapGestureRecognizer get _effectiveTapRecognizer =>
_tapGestureRecognizer ??= TapGestureRecognizer()..onTap = _onShowMore;
TextSpan get _moreTextSpan => TextSpan( TextSpan get _moreTextSpan => TextSpan(
style: text.style!.copyWith(color: _primary), style: text.style!.copyWith(color: _primary),
text: '查看更多', text: '查看更多',
recognizer: _effectiveTapRecognizer,
); );
TextPainter? _morePainter; TextPainter? _morePainter;

View File

@@ -114,6 +114,7 @@ class RichText extends MultiChildRenderObjectWidget {
this.textHeightBehavior, this.textHeightBehavior,
this.selectionRegistrar, this.selectionRegistrar,
this.selectionColor, this.selectionColor,
this.onShowMore,
}) : assert(maxLines == null || maxLines > 0), }) : assert(maxLines == null || maxLines > 0),
assert(selectionRegistrar == null || selectionColor != null), assert(selectionRegistrar == null || selectionColor != null),
assert( assert(
@@ -228,6 +229,8 @@ class RichText extends MultiChildRenderObjectWidget {
/// widgets. /// widgets.
final Color? selectionColor; final Color? selectionColor;
final VoidCallback? onShowMore;
@override @override
RenderParagraph createRenderObject(BuildContext context) { RenderParagraph createRenderObject(BuildContext context) {
assert(textDirection != null || debugCheckHasDirectionality(context)); assert(textDirection != null || debugCheckHasDirectionality(context));
@@ -246,6 +249,7 @@ class RichText extends MultiChildRenderObjectWidget {
registrar: selectionRegistrar, registrar: selectionRegistrar,
selectionColor: selectionColor, selectionColor: selectionColor,
primary: Theme.of(context).colorScheme.primary, primary: Theme.of(context).colorScheme.primary,
onShowMore: onShowMore,
); );
} }
@@ -266,7 +270,8 @@ class RichText extends MultiChildRenderObjectWidget {
..locale = locale ?? Localizations.maybeLocaleOf(context) ..locale = locale ?? Localizations.maybeLocaleOf(context)
..registrar = selectionRegistrar ..registrar = selectionRegistrar
..selectionColor = selectionColor ..selectionColor = selectionColor
..primary = Theme.of(context).colorScheme.primary; ..primary = Theme.of(context).colorScheme.primary
..onShowMore = onShowMore;
} }
@override @override

View File

@@ -174,6 +174,7 @@ class Text extends StatelessWidget {
this.textWidthBasis, this.textWidthBasis,
this.textHeightBehavior, this.textHeightBehavior,
this.selectionColor, this.selectionColor,
this.onShowMore,
}) : textSpan = null, }) : textSpan = null,
assert( assert(
textScaler == null || textScaleFactor == null, textScaler == null || textScaleFactor == null,
@@ -211,6 +212,7 @@ class Text extends StatelessWidget {
this.textWidthBasis, this.textWidthBasis,
this.textHeightBehavior, this.textHeightBehavior,
this.selectionColor, this.selectionColor,
this.onShowMore,
}) : data = null, }) : data = null,
assert( assert(
textScaler == null || textScaleFactor == null, textScaler == null || textScaleFactor == null,
@@ -349,6 +351,8 @@ class Text extends StatelessWidget {
/// (semi-transparent grey). /// (semi-transparent grey).
final Color? selectionColor; final Color? selectionColor;
final VoidCallback? onShowMore;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
@@ -435,6 +439,7 @@ class Text extends StatelessWidget {
text: data, text: data,
children: textSpan != null ? <InlineSpan>[textSpan!] : null, children: textSpan != null ? <InlineSpan>[textSpan!] : null,
), ),
onShowMore: onShowMore,
); );
} }
if (semanticsLabel != null || semanticsIdentifier != null) { if (semanticsLabel != null || semanticsIdentifier != null) {

View File

@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/widgets/image/custom_grid_view.dart';
import 'package:PiliPlus/common/widgets/text/text.dart' as custom_text; import 'package:PiliPlus/common/widgets/text/text.dart' as custom_text;
import 'package:PiliPlus/models/dynamics/result.dart'; import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/dynamics/widgets/rich_node_panel.dart'; import 'package:PiliPlus/pages/dynamics/widgets/rich_node_panel.dart';
import 'package:PiliPlus/utils/page_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -84,6 +85,7 @@ Widget content(
: const TextStyle(fontSize: 14), : const TextStyle(fontSize: 14),
richNodes, richNodes,
maxLines: isSave ? null : 6, maxLines: isSave ? null : 6,
onShowMore: () => PageUtils.pushDynDetail(item, isPush: true),
), ),
if (pics?.isNotEmpty == true) if (pics?.isNotEmpty == true)
CustomGridView( CustomGridView(