Files
PiliPlus/lib/pages/music/video/view.dart
My-Responsitories 08a33d9ce5 feat: musicDetail (#1157)
* feat: musicDetail

* opt: marquee
2025-08-28 17:40:12 +08:00

128 lines
3.9 KiB
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/refresh_indicator.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/music/bgm_recommend_list.dart';
import 'package:PiliPlus/pages/music/video/controller.dart';
import 'package:PiliPlus/pages/music/widget/music_video_card_h.dart';
import 'package:PiliPlus/utils/grid.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MusicRecommandPage extends StatefulWidget {
const MusicRecommandPage({super.key});
@override
State<MusicRecommandPage> createState() => _MusicRecommandPageState();
}
class _MusicRecommandPageState extends State<MusicRecommandPage>
with GridMixin, SingleTickerProviderStateMixin {
late final _controller = Get.put(
MusicRecommendController(),
tag: Utils.generateRandomString(8),
);
late final _animation = AnimationController(
vsync: this,
duration: const Duration(seconds: 5),
reverseDuration: const Duration(seconds: 5),
)..repeat(reverse: true);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final padding = MediaQuery.viewPaddingOf(context);
return Material(
color: theme.colorScheme.surface,
child: refreshIndicator(
onRefresh: _controller.onRefresh,
child: CustomScrollView(
controller: _controller.scrollController,
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
_buildAppBar(theme, padding),
SliverPadding(
padding: EdgeInsets.only(
top: 7,
left: padding.left,
right: padding.right,
bottom: padding.bottom + 100,
),
sliver: Obx(
() => _buildBody(_controller.loadingState.value),
),
),
],
),
),
);
}
Widget _buildBody(LoadingState<List<BgmRecommend>?> loadingState) {
return switch (loadingState) {
Loading() => gridSkeleton,
Success(:var response) =>
response?.isNotEmpty == true
? SliverGrid.builder(
gridDelegate: gridDelegate,
itemBuilder: (context, index) => MusicVideoCardH(
videoItem: response[index],
animation: _animation,
),
itemCount: response!.length,
)
: HttpError(onReload: _controller.onReload),
Error(:var errMsg) => HttpError(
errMsg: errMsg,
onReload: _controller.onReload,
),
};
}
Widget _buildAppBar(ThemeData theme, EdgeInsets padding) {
final info = _controller.musicDetail;
return SliverAppBar(
pinned: true,
title: Row(
spacing: 12,
children: [
NetworkImgLayer(
width: 40,
height: 40,
src: info.mvCover,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
info.musicTitle!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.titleMedium,
),
Obx(() {
final count = _controller.loadingState.value.dataOrNull?.length;
return count == null
? const SizedBox.shrink()
: Text(
'$count条视频',
style: theme.textTheme.labelMedium,
);
}),
],
),
],
),
);
}
@override
void dispose() {
_animation.dispose();
super.dispose();
}
}