fix(player): 完善进度预览清屏与下拉刷新
This commit is contained in:
1 parent
9b1afbdb36
commit
f8500bbaca
15 files changed
+949
-251
No files matched your search
@@ -34,6 +34,7 @@ import 'player_logic.dart';
|
||||
import 'widgets/home_feed_header.dart';
|
||||
import 'widgets/home_feed_overlay.dart';
|
||||
import 'widgets/exit_clear_screen_button.dart';
|
||||
import 'widgets/feed_pull_refresh.dart';
|
||||
import 'widgets/player_center_toggle.dart';
|
||||
import 'widgets/player_more_sheet.dart';
|
||||
import 'widgets/player_overlay.dart';
|
||||
@@ -49,6 +50,7 @@ part 'player_view_actions.dart';
|
||||
const double _boostPlaybackSpeed = 2.0;
|
||||
const double _playerLoadingSize = 24;
|
||||
const double _playerLoadingStrokeWidth = 2;
|
||||
const double _feedRefreshTopGap = 56;
|
||||
|
||||
class PlayerPage extends StatefulWidget {
|
||||
const PlayerPage({
|
||||
@@ -305,6 +307,16 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
unawaited(logic.setPlaybackSpeed(speed));
|
||||
}
|
||||
|
||||
Future<void> _refreshHomeFeed() async {
|
||||
final refresh = widget.onRefreshFeed;
|
||||
if (refresh == null) return;
|
||||
final ok = await refresh();
|
||||
if (!mounted || ok) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(AppLocalizations.of(context)!.catalogError)),
|
||||
);
|
||||
}
|
||||
|
||||
void _setScrubbing(bool value) {
|
||||
if (!mounted) return;
|
||||
if (_scrubbing == value) return;
|
||||
@@ -404,26 +416,37 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
final interactions = context.watch<InteractionController>();
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final indices = logic.visibleIndices;
|
||||
final pages = logic.pages;
|
||||
final content = AnimatedBuilder(
|
||||
animation: logic.session,
|
||||
builder: (context, _) => PageView.builder(
|
||||
controller: pages,
|
||||
scrollDirection: Axis.vertical,
|
||||
allowImplicitScrolling: true,
|
||||
itemCount: indices.length,
|
||||
onPageChanged: (position) {
|
||||
final index = indices[position];
|
||||
widget.onDramaChanged?.call(logic.queue.entryAt(index).drama);
|
||||
unawaited(logic.selectEpisode(index));
|
||||
if (position >= indices.length - 3) {
|
||||
final loadMore = widget.onLoadMore;
|
||||
if (loadMore != null) unawaited(loadMore());
|
||||
}
|
||||
},
|
||||
itemBuilder: (context, position) =>
|
||||
_buildEpisode(context, indices[position], interactions),
|
||||
),
|
||||
builder: (context, _) {
|
||||
final pages = PageView.builder(
|
||||
controller: logic.pages,
|
||||
scrollDirection: Axis.vertical,
|
||||
allowImplicitScrolling: true,
|
||||
physics: widget.onRefreshFeed == null
|
||||
? null
|
||||
: const FeedRefreshPhysics(),
|
||||
itemCount: indices.length,
|
||||
onPageChanged: (position) {
|
||||
final index = indices[position];
|
||||
widget.onDramaChanged?.call(logic.queue.entryAt(index).drama);
|
||||
unawaited(logic.selectEpisode(index));
|
||||
if (position >= indices.length - 3) {
|
||||
final loadMore = widget.onLoadMore;
|
||||
if (loadMore != null) unawaited(loadMore());
|
||||
}
|
||||
},
|
||||
itemBuilder: (context, position) =>
|
||||
_buildEpisode(context, indices[position], interactions),
|
||||
);
|
||||
if (widget.onRefreshFeed == null) return pages;
|
||||
return FeedPullRefresh(
|
||||
pageController: logic.pages,
|
||||
topInset: MediaQuery.paddingOf(context).top + _feedRefreshTopGap,
|
||||
onRefresh: _refreshHomeFeed,
|
||||
child: pages,
|
||||
);
|
||||
},
|
||||
);
|
||||
final body = Stack(
|
||||
fit: StackFit.expand,
|
||||
@@ -504,9 +527,17 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
final sessionEpisode = index >= 0 && index < logic.sessionEpisodes.length
|
||||
? logic.sessionEpisodes[index]
|
||||
: entry.episode;
|
||||
final episode = widget.homeMode
|
||||
final displayed = widget.homeMode
|
||||
? logic.displayEpisode(drama, sessionEpisode)
|
||||
: sessionEpisode;
|
||||
final episode =
|
||||
(displayed.thumbnailsUrl == null || displayed.thumbnailsUrl!.isEmpty) &&
|
||||
sessionEpisode.thumbnailsUrl != null
|
||||
? displayed.withVideoUrl(
|
||||
displayed.videoUrl,
|
||||
thumbnailsUrl: sessionEpisode.thumbnailsUrl,
|
||||
)
|
||||
: displayed;
|
||||
final selected = index == logic.session.currentIndex;
|
||||
// Selection changes before the prepared slot is promoted. Use its controller
|
||||
// throughout the handoff instead of briefly falling back to a placeholder.
|
||||
@@ -702,6 +733,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
thumbnailService: _thumbnailService,
|
||||
spriteAxisService: _spriteAxisService,
|
||||
),
|
||||
scrubbing: _scrubbing,
|
||||
actions: PlayerOverlayActions(
|
||||
onBack: widget.embedded
|
||||
? null
|
||||
@@ -719,7 +751,13 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
onSave: () => unawaited(_toggleSave(context, drama)),
|
||||
onShare: () => logic.shareDrama(context, drama),
|
||||
onMore: () => _openMore(context, episode),
|
||||
onScrubStart: ready ? () => logic.beginScrub(videoPort!) : null,
|
||||
onScrubStart: ready
|
||||
? () {
|
||||
_setScrubbing(true);
|
||||
logic.beginScrub(videoPort!);
|
||||
}
|
||||
: null,
|
||||
onScrubEnd: () => _setScrubbing(false),
|
||||
onSeek: ready
|
||||
? (position) => logic.seekTo(context, videoPort!, position)
|
||||
: null,
|
||||
|
||||
Reference in new issue
Block a user