opt scroll physics

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-06-20 13:31:28 +08:00
parent 452b54d124
commit 480bdffdaa
59 changed files with 210 additions and 195 deletions

View File

@@ -1,8 +1,14 @@
import 'package:PiliPlus/utils/global_data.dart';
import 'package:flutter/material.dart';
class DynamicCardSkeleton extends StatelessWidget {
const DynamicCardSkeleton({super.key});
const _ = _DynamicCardSkeleton();
const dynSkeleton_ = SliverList(delegate: dynDelegate);
const dynDelegate = SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
);
class _DynamicCardSkeleton extends StatelessWidget {
const _DynamicCardSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,8 +1,13 @@
import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart';
class FavPgcItemSkeleton extends StatelessWidget {
const FavPgcItemSkeleton({super.key});
const _ = _FavPgcItemSkeleton();
const favPgcDelegate = SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
);
class _FavPgcItemSkeleton extends StatelessWidget {
const _FavPgcItemSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,6 +1,11 @@
import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart';
const _ = MediaPgcSkeleton();
const pgcDelegate = SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
);
class MediaPgcSkeleton extends StatefulWidget {
const MediaPgcSkeleton({super.key});

View File

@@ -1,7 +1,14 @@
import 'package:flutter/material.dart';
class MsgFeedSysMsgSkeleton extends StatelessWidget {
const MsgFeedSysMsgSkeleton({super.key});
const _ = _MsgFeedSysMsgSkeleton();
const sysMsgSkeleton = SliverList(
delegate: SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
),
);
class _MsgFeedSysMsgSkeleton extends StatelessWidget {
const _MsgFeedSysMsgSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,7 +1,13 @@
import 'package:flutter/material.dart';
class MsgFeedTopSkeleton extends StatelessWidget {
const MsgFeedTopSkeleton({super.key});
const _ = _MsgFeedTopSkeleton();
const sysFeedSkeleton = SliverList(delegate: sysFeedDelegate);
const sysFeedDelegate = SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
);
class _MsgFeedTopSkeleton extends StatelessWidget {
const _MsgFeedTopSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,8 +1,13 @@
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
class SpaceOpusSkeleton extends StatelessWidget {
const SpaceOpusSkeleton({super.key});
const _ = _SpaceOpusSkeleton();
const opusDelegate = SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
);
class _SpaceOpusSkeleton extends StatelessWidget {
const _SpaceOpusSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,8 +1,13 @@
import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart';
class VideoCardHSkeleton extends StatelessWidget {
const VideoCardHSkeleton({super.key});
const _ = _VideoCardHSkeleton();
const videoHDelegate = SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
);
class _VideoCardHSkeleton extends StatelessWidget {
const _VideoCardHSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,8 +1,13 @@
import 'package:PiliPlus/common/style.dart';
import 'package:flutter/material.dart';
class VideoCardVSkeleton extends StatelessWidget {
const VideoCardVSkeleton({super.key});
const _ = _VideoCardVSkeleton();
const videoVDelegate = SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
);
class _VideoCardVSkeleton extends StatelessWidget {
const _VideoCardVSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,7 +1,12 @@
import 'package:flutter/material.dart';
class VideoReplySkeleton extends StatelessWidget {
const VideoReplySkeleton({super.key});
const _ = _VideoReplySkeleton();
const replySkeleton = SliverList(
delegate: SliverChildListDelegate.fixed([_, _, _, _, _]),
);
class _VideoReplySkeleton extends StatelessWidget {
const _VideoReplySkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -1,7 +1,14 @@
import 'package:flutter/material.dart';
class WhisperItemSkeleton extends StatelessWidget {
const WhisperItemSkeleton({super.key});
const _ = _WhisperItemSkeleton();
const whisperSkeleton = SliverList(
delegate: SliverChildListDelegate.fixed(
[_, _, _, _, _, _, _, _, _, _],
),
);
class _WhisperItemSkeleton extends StatelessWidget {
const _WhisperItemSkeleton();
@override
Widget build(BuildContext context) {

View File

@@ -551,7 +551,7 @@ class RefreshIndicatorState extends State<RefreshIndicator>
return ScrollConfiguration(
behavior: RefreshScrollBehavior(
scrollPhysics: RefreshScrollPhysics(
parent: const RangeMaintainingScrollPhysics(),
// parent: const RangeMaintainingScrollPhysics(),
onDrag: _onDrag,
),
),

View File

@@ -3,6 +3,10 @@ import 'dart:io' show Platform;
import 'package:flutter/gestures.dart' show PointerDeviceKind;
import 'package:flutter/material.dart';
const _clampingPhysics = ClampingScrollPhysics();
const _bouncingPhysics = BouncingScrollPhysics();
const _bouncingDesktopPhysics = BouncingScrollPhysics(decelerationRate: .fast);
class CustomScrollBehavior extends MaterialScrollBehavior {
const CustomScrollBehavior();
@@ -29,6 +33,17 @@ class CustomScrollBehavior extends MaterialScrollBehavior {
return child;
}
@override
ScrollPhysics getScrollPhysics(BuildContext context) {
if (Platform.isIOS) {
return _bouncingPhysics;
}
if (Platform.isMacOS) {
return _bouncingDesktopPhysics;
}
return _clampingPhysics;
}
@override
Set<PointerDeviceKind> get dragDevices => desktopDragDevices;
}

View File

@@ -65,10 +65,7 @@ class _BlackListPageState extends State<BlackListPage> {
Widget _buildBody(LoadingState<List<BlackListItem>?> loadingState) {
late final style = TextStyle(color: Theme.of(context).colorScheme.outline);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
),
Loading() => sysFeedSkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.builder(

View File

@@ -1,3 +1,4 @@
import 'package:PiliPlus/common/widgets/scroll_physics.dart' show ReloadMixin;
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
import 'package:flutter/widgets.dart' show ScrollController;
@@ -50,3 +51,14 @@ abstract class CommonController<R, T> extends GetxController
super.onClose();
}
}
mixin CommonReloadMixin<R, T> on CommonController<R, T> implements ReloadMixin {
@override
late bool reload = false;
@override
Future<void> onReload() {
reload = true;
return super.onReload();
}
}

View File

@@ -121,10 +121,7 @@ mixin CommonDynPageMixin<T extends StatefulWidget>
Widget replyList(LoadingState<List<ReplyInfo>?> loadingState) {
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const VideoReplySkeleton(),
),
Loading() => replySkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.builder(

View File

@@ -1,13 +1,15 @@
import 'package:PiliPlus/common/widgets/scroll_physics.dart' show ReloadMixin;
import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/reply.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/dyn/common_dyn_controller.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class DynamicDetailController extends CommonDynController with ReloadMixin {
class DynamicDetailController extends CommonDynController
with CommonReloadMixin {
@override
late int oid;
@override
@@ -70,10 +72,4 @@ class DynamicDetailController extends CommonDynController with ReloadMixin {
});
}
}
@override
Future<void> onReload() {
reload = true;
return super.onReload();
}
}

View File

@@ -1,11 +1,12 @@
import 'dart:async' show StreamSubscription;
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/http/dynamics.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/msg.dart';
import 'package:PiliPlus/models/common/dynamic/dynamics_type.dart';
import 'package:PiliPlus/models/dynamics/result.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/main/controller.dart';
@@ -15,11 +16,13 @@ import 'package:get/get.dart';
class DynamicsTabController
extends CommonListController<DynamicsDataModel, DynamicItemModel>
with AccountMixin, ReloadMixin {
with AccountMixin, CommonReloadMixin {
DynamicsTabController({required this.dynamicsType});
final DynamicsTabType dynamicsType;
String offset = '';
int? mid;
late int flag = 0;
late final MainController mainController = Get.find<MainController>();
final dynamicsController = Get.find<DynamicsController>();
StreamSubscription? _listener;
@@ -31,6 +34,7 @@ class DynamicsTabController
if (dynamicsType == .up) {
_listener = dynamicsController.mid.listen((mid) {
if (mid != -1) {
flag++;
this.mid = mid;
onReload();
}
@@ -74,12 +78,6 @@ class DynamicsTabController
}
}
@override
Future<void> onReload() {
reload = true;
return super.onReload();
}
void onBlock(int index) {
if (dynamicsType != .up) {
loadingState

View File

@@ -52,9 +52,11 @@ class _DynamicsTabPageState extends State<DynamicsTabPage>
return controller.onRefresh();
},
child: CustomScrollView(
key: widget.dynamicsType == .all
? null
: PageStorageKey(widget.dynamicsType),
key: switch (widget.dynamicsType) {
.all => null,
.up => PageStorageKey('${widget.dynamicsType}${controller.flag}'),
_ => PageStorageKey(widget.dynamicsType),
},
physics: ReloadScrollPhysics(controller: controller),
controller: controller.scrollController,
slivers: [

View File

@@ -170,10 +170,9 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
Widget _buildBody(LoadingState<List<FavPgcItemModel>?> loadingState) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const FavPgcItemSkeleton(),
itemCount: 10,
delegate: favPgcDelegate,
),
Success(:final response) =>
response != null && response.isNotEmpty

View File

@@ -157,10 +157,7 @@ class _FollowChildPageState extends State<FollowChildPage>
Widget _buildBody(LoadingState<List<FollowItemModel>?> loadingState) {
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
),
Loading() => sysFeedSkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.builder(

View File

@@ -49,10 +49,9 @@ abstract class FollowTypePageState<T extends StatefulWidget> extends State<T> {
LoadingState<List<FollowItemModel>?> loadingState,
) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
itemCount: 16,
delegate: sysFeedDelegate,
),
Success(:final response) =>
response != null && response.isNotEmpty

View File

@@ -171,10 +171,9 @@ class _LivePageState extends State<LivePage>
Widget _buildBody(ThemeData theme, LoadingState<List?> loadingState) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
itemCount: 10,
delegate: videoVDelegate,
),
Success(:final response) => SliverMainAxisGroup(
slivers: [

View File

@@ -80,10 +80,9 @@ class _LiveAreaChildPageState extends State<LiveAreaChildPage>
LoadingState<List<CardLiveItem>?> loadingState,
) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
itemCount: 10,
delegate: videoVDelegate,
),
Success(:final response) => SliverMainAxisGroup(
slivers: [

View File

@@ -62,10 +62,9 @@ class _LiveFollowPageState extends State<LiveFollowPage> {
Widget _buildBody(LoadingState<List<LiveFollowItem>?> loadingState) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
itemCount: 10,
delegate: videoVDelegate,
),
Success(:final response) =>
response != null && response.isNotEmpty

View File

@@ -60,15 +60,13 @@ class _LiveSearchChildPageState extends State<LiveSearchChildPage>
Widget get _buildLoading {
return switch (widget.searchType) {
.room => SliverGrid.builder(
.room => SliverGrid(
gridDelegate: roomDelegate,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
itemCount: 10,
delegate: videoVDelegate,
),
.user => SliverGrid.builder(
.user => SliverGrid(
gridDelegate: userDelegate,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
itemCount: 12,
delegate: sysFeedDelegate,
),
};
}

View File

@@ -2,10 +2,12 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show MainListReply, ReplyInfo;
import 'package:PiliPlus/grpc/reply.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/common/reply_controller.dart';
import 'package:get/get.dart';
class MainReplyController extends ReplyController<MainListReply> {
class MainReplyController extends ReplyController<MainListReply>
with CommonReloadMixin {
late final int oid;
late final int replyType;

View File

@@ -3,6 +3,7 @@ import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/scaffold.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
@@ -80,7 +81,7 @@ class _MainReplyPageState extends State<MainReplyPage>
right: padding.right,
),
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
physics: ReloadScrollPhysics(controller: _controller),
slivers: [
buildReplyHeader(colorScheme),
Obx(
@@ -131,11 +132,7 @@ class _MainReplyPageState extends State<MainReplyPage>
LoadingState<List<ReplyInfo>?> loadingState,
) {
return switch (loadingState) {
Loading() => SliverPrototypeExtentList.builder(
itemCount: 10,
itemBuilder: (_, _) => const VideoReplySkeleton(),
prototypeItem: const VideoReplySkeleton(),
),
Loading() => replySkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.builder(

View File

@@ -77,10 +77,9 @@ class _MemberCoinArcPageState extends State<MemberCoinArcPage> {
Widget _buildBody(LoadingState<List<CoinLikeArcItem>?> loadingState) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemCount: 16,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
delegate: videoVDelegate,
),
Success(:final response) =>
response != null && response.isNotEmpty

View File

@@ -79,10 +79,9 @@ class _MemberLikeArcPageState extends State<MemberLikeArcPage> {
Widget _buildBody(LoadingState<List<CoinLikeArcItem>?> loadingState) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemCount: 16,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
delegate: videoVDelegate,
),
Success(:final response) =>
response != null && response.isNotEmpty

View File

@@ -156,10 +156,7 @@ class _MemberOpusState extends State<MemberOpus>
return switch (loadingState) {
Loading() => SliverWaterfallFlow(
gridDelegate: gridDelegate,
delegate: SliverChildBuilderDelegate(
(context, index) => const SpaceOpusSkeleton(),
childCount: 10,
),
delegate: opusDelegate,
),
Success(:final response) =>
response != null && response.isNotEmpty

View File

@@ -77,10 +77,7 @@ class _MemberShopState extends State<MemberShop>
case Loading():
return SliverWaterfallFlow(
gridDelegate: gridDelegate,
delegate: SliverChildBuilderDelegate(
(context, index) => const SpaceOpusSkeleton(),
childCount: 10,
),
delegate: opusDelegate,
);
case Success(:final response):
if (response == null || response.isEmpty) {

View File

@@ -1,4 +1,3 @@
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/member.dart';
import 'package:PiliPlus/http/search.dart';
@@ -9,6 +8,8 @@ import 'package:PiliPlus/models/common/video/source_type.dart';
import 'package:PiliPlus/models_new/space/space_archive/data.dart';
import 'package:PiliPlus/models_new/space/space_archive/episodic_button.dart';
import 'package:PiliPlus/models_new/space/space_archive/item.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/utils/extension/dimension_ext.dart';
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
@@ -18,7 +19,7 @@ import 'package:get/get.dart';
class MemberVideoCtr
extends CommonListController<SpaceArchiveData, SpaceArchiveItem>
with ReloadMixin {
with CommonReloadMixin {
MemberVideoCtr({
required this.type,
required this.mid,
@@ -226,7 +227,6 @@ class MemberVideoCtr
@override
Future<void> onReload() {
reload = true;
isLocating.value = false;
return super.onReload();
}

View File

@@ -1,12 +1,13 @@
import 'package:PiliPlus/common/widgets/scroll_physics.dart' show ReloadMixin;
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:get/get.dart';
const int ps = 30;
abstract class BaseVideoWebCtr<R, T, V> extends CommonListController<R, T>
with ReloadMixin {
with CommonReloadMixin {
final Object mid = Get.arguments['mid'];
int? totalPage;
@@ -41,10 +42,4 @@ abstract class BaseVideoWebCtr<R, T, V> extends CommonListController<R, T>
loadingState.value = LoadingState.loading();
queryData();
}
@override
Future<void> onReload() {
reload = true;
return super.onReload();
}
}

View File

@@ -79,10 +79,7 @@ class _AtMePageState extends State<AtMePage> {
color: Colors.grey.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
),
Loading() => sysFeedSkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.separated(

View File

@@ -61,10 +61,7 @@ class _LikeDetailPageState extends State<LikeDetailPage> {
color: Colors.grey.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
),
Loading() => sysFeedSkeleton,
Success(:final response) => SliverMainAxisGroup(
slivers: [
if (_controller.card != null) ...[

View File

@@ -82,10 +82,7 @@ class _LikeMePageState extends State<LikeMePage> {
color: Colors.grey.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
),
Loading() => sysFeedSkeleton,
Success(:final response) => Builder(
builder: (context) {
Pair<List<MsgLikeItem>, List<MsgLikeItem>> pair = response;

View File

@@ -79,10 +79,7 @@ class _ReplyMePageState extends State<ReplyMePage> {
color: Colors.grey.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
),
Loading() => sysFeedSkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.separated(

View File

@@ -66,12 +66,7 @@ class _SysMsgPageState extends State<SysMsgPage> {
color: Colors.grey.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => ViewSliverSafeArea(
sliver: SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const MsgFeedSysMsgSkeleton(),
),
),
Loading() => const ViewSliverSafeArea(sliver: sysMsgSkeleton),
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.separated(

View File

@@ -3,12 +3,15 @@ import 'package:PiliPlus/http/pgc.dart';
import 'package:PiliPlus/models/common/pgc_review_type.dart';
import 'package:PiliPlus/models_new/pgc/pgc_review/data.dart';
import 'package:PiliPlus/models_new/pgc/pgc_review/list.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class PgcReviewController
extends CommonListController<PgcReviewData, PgcReviewItemModel> {
extends CommonListController<PgcReviewData, PgcReviewItemModel>
with CommonReloadMixin {
PgcReviewController({required this.type, required this.mediaId});
final PgcReviewType type;

View File

@@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/icon/bili_icons.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/pgc_review_type.dart';
@@ -66,7 +67,7 @@ class _PgcReviewChildPageState extends State<PgcReviewChildPage>
onRefresh: _controller.onRefresh,
child: CustomScrollView(
controller: _controller.scrollController,
physics: const AlwaysScrollableScrollPhysics(),
physics: ReloadScrollPhysics(controller: _controller),
slivers: [
_buildHeader(theme),
SliverPadding(
@@ -91,11 +92,7 @@ class _PgcReviewChildPageState extends State<PgcReviewChildPage>
color: theme.colorScheme.outline.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverPrototypeExtentList.builder(
prototypeItem: const VideoReplySkeleton(),
itemBuilder: (_, _) => const VideoReplySkeleton(),
itemCount: 8,
),
Loading() => replySkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.separated(

View File

@@ -1,17 +1,18 @@
import 'package:PiliPlus/common/widgets/scroll_physics.dart' show ReloadMixin;
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/video.dart';
import 'package:PiliPlus/models/model_hot_video_item.dart';
import 'package:PiliPlus/models_new/popular/popular_series_list/list.dart';
import 'package:PiliPlus/models_new/popular/popular_series_one/config.dart';
import 'package:PiliPlus/models_new/popular/popular_series_one/data.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/common_list_controller.dart';
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
import 'package:get/get.dart';
class PopularSeriesController
extends CommonListController<PopularSeriesOneData, HotVideoItemModel>
with ReloadMixin {
with CommonReloadMixin {
late int number;
final config = Rxn<PopularSeriesConfig>();
@@ -55,7 +56,6 @@ class PopularSeriesController
if (seriesList.isNullOrEmpty) {
return _getSeriesList();
}
reload = true;
return super.onReload();
}
}

View File

@@ -28,10 +28,9 @@ class ZonePage extends StatelessWidget {
Widget buildBody(LoadingState<List<dynamic>?> loadingState) {
return switch (loadingState) {
Loading() => SliverGrid.builder(
Loading() => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (_, _) => const VideoCardHSkeleton(),
itemCount: 10,
delegate: videoHDelegate,
),
Success(:final response) =>
response != null && response.isNotEmpty

View File

@@ -131,9 +131,8 @@ class _RcmdPageState extends State<RcmdPage>
};
}
Widget get _buildSkeleton => SliverGrid.builder(
Widget get _buildSkeleton => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
itemCount: 10,
delegate: videoVDelegate,
);
}

View File

@@ -101,9 +101,8 @@ class _SearchAllPanelState
}
@override
Widget get buildLoading => SliverGrid.builder(
Widget get buildLoading => SliverGrid(
gridDelegate: Grid.videoCardHDelegate(),
itemBuilder: (context, index) => const VideoCardHSkeleton(),
itemCount: 10,
delegate: videoHDelegate,
);
}

View File

@@ -73,9 +73,8 @@ class _SearchLivePanelState
}
@override
Widget get buildLoading => SliverGrid.builder(
Widget get buildLoading => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const VideoCardVSkeleton(),
itemCount: 10,
delegate: videoVDelegate,
);
}

View File

@@ -65,13 +65,12 @@ class _SearchPgcPanelState
}
@override
Widget get buildLoading => SliverGrid.builder(
Widget get buildLoading => SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio(
mainAxisSpacing: 2,
maxCrossAxisExtent: Grid.smallCardWidth * 2,
childAspectRatio: Style.aspectRatio * 1.5,
),
itemBuilder: (context, index) => const MediaPgcSkeleton(),
itemCount: 10,
delegate: pgcDelegate,
);
}

View File

@@ -112,9 +112,8 @@ class _SearchUserPanelState
}
@override
Widget get buildLoading => SliverGrid.builder(
Widget get buildLoading => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => const MsgFeedTopSkeleton(),
itemCount: 10,
delegate: sysFeedDelegate,
);
}

View File

@@ -173,9 +173,8 @@ class _HorizontalMemberPageState extends State<HorizontalMemberPage> {
LoadingState<List<SpaceArchiveItem>?> loadingState,
) {
return switch (loadingState) {
Loading() => SliverFixedExtentList.builder(
itemCount: 10,
itemBuilder: (_, _) => const VideoCardHSkeleton(),
Loading() => const SliverFixedExtentList(
delegate: videoHDelegate,
itemExtent: 112,
),
Success(:final response) =>

View File

@@ -187,11 +187,7 @@ class _NoteListPageState extends State<NoteListPage>
color: theme.colorScheme.outline.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverPrototypeExtentList.builder(
prototypeItem: const VideoReplySkeleton(),
itemBuilder: (_, _) => const VideoReplySkeleton(),
itemCount: 8,
),
Loading() => replySkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.separated(

View File

@@ -3,11 +3,14 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
import 'package:PiliPlus/grpc/reply.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/video/video_type.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/reply_controller.dart';
import 'package:PiliPlus/pages/video/controller.dart';
import 'package:get/get.dart';
class VideoReplyController extends ReplyController<MainListReply> {
class VideoReplyController extends ReplyController<MainListReply>
with CommonReloadMixin {
VideoReplyController({
required this.aid,
required this.videoType,

View File

@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/skeleton/video_reply.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_floating_header.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo;
@@ -86,7 +87,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
controller: widget.isNested
? null
: _videoReplyController.scrollController,
physics: const AlwaysScrollableScrollPhysics(),
physics: ReloadScrollPhysics(controller: _videoReplyController),
key: const PageStorageKey(_VideoReplyPanelState),
slivers: [
SliverFloatingHeaderWidget(
@@ -173,10 +174,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
LoadingState<List<ReplyInfo>?> loadingState,
) {
return switch (loadingState) {
Loading() => SliverList.builder(
itemBuilder: (context, index) => const VideoReplySkeleton(),
itemCount: 5,
),
Loading() => replySkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.builder(

View File

@@ -2,6 +2,8 @@ import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
show ReplyInfo, DetailListReply, Mode;
import 'package:PiliPlus/grpc/reply.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/common/common_controller.dart'
show CommonReloadMixin;
import 'package:PiliPlus/pages/common/publish/publish_route.dart';
import 'package:PiliPlus/pages/common/reply_controller.dart';
import 'package:PiliPlus/pages/video/reply_new/view.dart';
@@ -14,7 +16,7 @@ import 'package:get/get.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
class VideoReplyReplyController extends ReplyController
with GetSingleTickerProviderStateMixin {
with GetSingleTickerProviderStateMixin, CommonReloadMixin {
VideoReplyReplyController({
required this.hasRoot,
required this.id,

View File

@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/widgets/colored_box_transition.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/common/widgets/scaffold.dart';
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_header.dart';
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
@@ -189,7 +190,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
child: CustomScrollView(
key: ValueKey(scrollController.hashCode),
controller: scrollController,
physics: const AlwaysScrollableScrollPhysics(),
physics: ReloadScrollPhysics(controller: _controller),
slivers: [
if (!isDialogue) ...[
if ((widget.firstFloor ?? _controller.firstFloor.value)
@@ -284,11 +285,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel>
) {
final jumpIndex = _controller.index.value;
return switch (loadingState) {
Loading() => SliverPrototypeExtentList.builder(
prototypeItem: const VideoReplySkeleton(),
itemBuilder: (_, _) => const VideoReplySkeleton(),
itemCount: 8,
),
Loading() => replySkeleton,
Success(:final response!) => SuperSliverList.builder(
listController: _controller.listController,
itemBuilder: (context, index) {

View File

@@ -1046,6 +1046,9 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
return false;
},
onSkipSegment: videoDetailController.onSkipSegment,
onDownload: videoDetailController.isFileSource
? null
: () => videoDetailController.onDownload(context),
child: child,
);
}

View File

@@ -24,6 +24,7 @@ class PlayerFocus extends StatelessWidget {
this.canPlay,
this.onSkipSegment,
this.onRefresh,
this.onDownload,
});
final Widget child;
@@ -33,6 +34,7 @@ class PlayerFocus extends StatelessWidget {
final ValueGetter<bool>? canPlay;
final ValueGetter<bool>? onSkipSegment;
final VoidCallback? onRefresh;
final VoidCallback? onDownload;
static bool _shouldHandle(LogicalKeyboardKey logicalKey) {
return logicalKey == LogicalKeyboardKey.tab ||
@@ -224,6 +226,10 @@ class PlayerFocus extends StatelessWidget {
}
return true;
case LogicalKeyboardKey.keyX:
onDownload?.call();
return true;
case LogicalKeyboardKey.enter:
if (onSkipSegment?.call() ?? false) {
return true;

View File

@@ -112,10 +112,7 @@ class _WhisperPageState extends State<WhisperPage> {
color: Colors.grey.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const WhisperItemSkeleton(),
),
Loading() => whisperSkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.separated(

View File

@@ -96,10 +96,7 @@ class _WhisperSecPageState extends State<WhisperSecPage> {
color: Colors.grey.withValues(alpha: 0.1),
);
return switch (loadingState) {
Loading() => SliverList.builder(
itemCount: 12,
itemBuilder: (context, index) => const WhisperItemSkeleton(),
),
Loading() => whisperSkeleton,
Success(:final response) =>
response != null && response.isNotEmpty
? SliverList.separated(

View File

@@ -8,10 +8,9 @@ import 'package:flutter/rendering.dart';
mixin GridMixin {
late final gridDelegate = Grid.videoCardHDelegate();
Widget get gridSkeleton => SliverGrid.builder(
Widget get gridSkeleton => SliverGrid(
gridDelegate: gridDelegate,
itemBuilder: (_, _) => const VideoCardHSkeleton(),
itemCount: 10,
delegate: videoHDelegate,
);
}

View File

@@ -43,17 +43,9 @@ mixin DynMixin {
Widget get dynSkeleton {
if (GlobalData.dynamicsWaterfallFlow) {
return SliverGrid.builder(
gridDelegate: skeDelegate,
itemBuilder: (_, _) => const DynamicCardSkeleton(),
itemCount: 10,
);
return SliverGrid(gridDelegate: skeDelegate, delegate: dynDelegate);
}
return SliverPrototypeExtentList.builder(
prototypeItem: const DynamicCardSkeleton(),
itemBuilder: (_, _) => const DynamicCardSkeleton(),
itemCount: 10,
);
return dynSkeleton_;
}
}