fix(flutter): feed 长按加速与刷新转圈

This commit is contained in:
Zhou Weidong committed 2026-10-20 14:19:14 +08:00
1 parent 564c0dbbef
commit ea9ea6e606
5 files changed
+317 -103

No files matched your search

+96 -12
View File
@@ -27,6 +27,9 @@ const double _tagFontSize = 11;
const double _fullBarHeight = 44;
const double _progressHeight = 2;
const double _progressHitHeight = 24;
const List<Shadow> _boostTextShadows = [
Shadow(color: Colors.black54, blurRadius: 5, offset: Offset(0, 1)),
];
const int _maxTags = 4;
final RegExp _tagSplitter = RegExp(r'[,/、·]');
@@ -40,6 +43,8 @@ class HomeFeedOverlay extends StatefulWidget {
required this.controller,
required this.liked,
required this.saved,
required this.visible,
required this.boosting,
required this.onLike,
required this.onSave,
required this.onComments,
@@ -51,6 +56,9 @@ class HomeFeedOverlay extends StatefulWidget {
required this.onDownload,
required this.onClear,
required this.onSearch,
required this.onTogglePlay,
required this.onBoostStart,
required this.onBoostEnd,
this.onScrubStart,
this.onSeek,
this.likeCount,
@@ -70,6 +78,8 @@ class HomeFeedOverlay extends StatefulWidget {
final int? commentCount;
final bool saved;
final String authorNote;
final bool visible;
final bool boosting;
final VoidCallback onLike;
final VoidCallback onSave;
final VoidCallback onComments;
@@ -77,13 +87,16 @@ class HomeFeedOverlay extends StatefulWidget {
final VoidCallback onEpisodes;
final VoidCallback onDetail;
final VoidCallback onOpenFullPlayer;
final VoidCallback onRefreshFeed;
final Future<bool> Function() onRefreshFeed;
final VoidCallback onDownload;
final VoidCallback onClear;
final VoidCallback onSearch;
final VoidCallback onTogglePlay;
final VoidCallback? onPip;
final VoidCallback? onScrubStart;
final ValueChanged<Duration>? onSeek;
final VoidCallback onBoostStart;
final VoidCallback onBoostEnd;
@override
State<HomeFeedOverlay> createState() => _HomeFeedOverlayState();
@@ -91,30 +104,100 @@ class HomeFeedOverlay extends StatefulWidget {
class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
_FeedTab _tab = _FeedTab.recommend;
bool _refreshing = false;
@override
Widget build(BuildContext context) {
return SafeArea(
bottom: false,
child: Column(
child: Stack(
children: [
_header(context),
Expanded(
child: Align(
alignment: const Alignment(1.0, 0.68),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 10, 16),
child: _rail(),
Positioned.fill(
child: LayoutBuilder(
builder: (context, constraints) => Listener(
onPointerUp: (_) => widget.onBoostEnd(),
onPointerCancel: (_) => widget.onBoostEnd(),
child: GestureDetector(
key: const Key('home-feed-gesture'),
behavior: HitTestBehavior.opaque,
onTap: widget.onTogglePlay,
onLongPressStart: (details) =>
_handleLongPressStart(details, constraints.maxWidth),
onLongPressEnd: (_) => widget.onBoostEnd(),
onLongPressCancel: widget.onBoostEnd,
),
),
),
),
_bottomInfo(),
_fullSeriesBar(),
if (widget.boosting)
Column(children: [const Spacer(), _boostHint(), _fullSeriesBar()])
else if (widget.visible)
Column(
children: [
_header(context),
Expanded(
child: Align(
alignment: const Alignment(1.0, 0.80),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 10, 16),
child: _rail(),
),
),
),
_bottomInfo(),
_fullSeriesBar(),
],
),
],
),
);
}
void _handleLongPressStart(LongPressStartDetails details, double width) {
final third = width / 3;
final dx = details.localPosition.dx;
if (dx < third || dx > width - third) {
widget.onBoostStart();
return;
}
_openMore(context);
}
Widget _boostHint() {
return Padding(
key: const Key('home-feed-speed-boost-hint'),
padding: const EdgeInsets.only(bottom: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.play_arrow_rounded,
color: AppColors.blue,
size: 16,
shadows: _boostTextShadows,
),
const SizedBox(width: 4),
Text(
widget.l10n.speedBoosting('2.0'),
style: const TextStyle(
color: AppColors.blue,
fontSize: 13,
fontWeight: FontWeight.w700,
shadows: _boostTextShadows,
),
),
],
),
);
}
Future<void> _refresh() async {
if (_refreshing) return;
setState(() => _refreshing = true);
await widget.onRefreshFeed();
if (mounted) setState(() => _refreshing = false);
}
Widget _header(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(8, 6, 8, 4),
@@ -124,7 +207,8 @@ class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
tooltip: widget.l10n.retry,
dimension: _topIconSize,
glyphSize: _topIconGlyph,
onTap: widget.onRefreshFeed,
refreshing: _refreshing,
onTap: _refresh,
onLongPress: () => _openMore(context),
),
Expanded(
@@ -3,9 +3,9 @@
part of 'home_feed_overlay.dart';
const double _thumbSize = 6;
const double _thumbActiveSize = 10;
const double _trackActiveHeight = 4;
const double _lineCenterFromBottom = 6;
const double _thumbActiveSize = 14;
const double _trackActiveHeight = 6;
const double _lineCenterFromBottom = 8;
/// 顶部频道 tab:漫剧点击提示即将上线。
enum _FeedTab {
@@ -69,6 +69,7 @@ class _RefreshButton extends StatelessWidget {
required this.tooltip,
required this.dimension,
required this.glyphSize,
required this.refreshing,
required this.onTap,
required this.onLongPress,
});
@@ -76,6 +77,7 @@ class _RefreshButton extends StatelessWidget {
final String tooltip;
final double dimension;
final double glyphSize;
final bool refreshing;
final VoidCallback onTap;
final VoidCallback onLongPress;
@@ -90,12 +92,23 @@ class _RefreshButton extends StatelessWidget {
onLongPress: onLongPress,
child: SizedBox.square(
dimension: dimension,
child: Icon(
Icons.refresh_rounded,
size: glyphSize,
color: Colors.white,
shadows: const [Shadow(blurRadius: 6, color: Colors.black)],
),
child: refreshing
? const Center(
child: SizedBox.square(
dimension: 18,
child: CircularProgressIndicator(
key: Key('home-feed-refreshing'),
strokeWidth: 2,
color: Colors.white,
),
),
)
: Icon(
Icons.refresh_rounded,
size: glyphSize,
color: Colors.white,
shadows: const [Shadow(blurRadius: 6, color: Colors.black)],
),
),
),
);