mod: 首页推荐视频
3
assets/images/dm.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 4.0625C7.85812 4.0625 5.98983 4.1725 4.67735 4.2798C3.77861 4.35327 3.08174 5.04067 3.00119 5.93221C2.90388 7.00924 2.8125 8.43727 2.8125 10C2.8125 11.5627 2.90388 12.9908 3.00119 14.0678C3.08174 14.9593 3.77861 15.6467 4.67735 15.7202C5.98983 15.8275 7.85812 15.9375 10 15.9375C12.1421 15.9375 14.0105 15.8275 15.323 15.7202C16.2216 15.6467 16.9184 14.9595 16.9989 14.0682C17.0962 12.9916 17.1875 11.5639 17.1875 10C17.1875 8.43614 17.0962 7.00837 16.9989 5.9318C16.9184 5.04049 16.2216 4.3533 15.323 4.27983C14.0105 4.17252 12.1421 4.0625 10 4.0625ZM4.5755 3.03395C5.9136 2.92456 7.81674 2.8125 10 2.8125C12.1835 2.8125 14.0868 2.92458 15.4249 3.03398C16.9228 3.15645 18.108 4.31588 18.2438 5.81931C18.3435 6.92296 18.4375 8.38948 18.4375 10C18.4375 11.6105 18.3435 13.077 18.2438 14.1807C18.108 15.6841 16.9228 16.8436 15.4249 16.966C14.0868 17.0754 12.1835 17.1875 10 17.1875C7.81674 17.1875 5.9136 17.0754 4.5755 16.966C3.07738 16.8436 1.8921 15.6838 1.75626 14.1803C1.65651 13.0762 1.5625 11.6094 1.5625 10C1.5625 8.39058 1.65651 6.92379 1.75626 5.81973C1.8921 4.31616 3.07738 3.15643 4.5755 3.03395ZM5 8.4375C5 8.09232 5.27982 7.8125 5.625 7.8125H5.9375C6.28268 7.8125 6.5625 8.09232 6.5625 8.4375C6.5625 8.78268 6.28268 9.0625 5.9375 9.0625H5.625C5.27982 9.0625 5 8.78268 5 8.4375ZM7.5 8.4375C7.5 8.09232 7.77982 7.8125 8.125 7.8125H13.125C13.4702 7.8125 13.75 8.09232 13.75 8.4375C13.75 8.78268 13.4702 9.0625 13.125 9.0625H8.125C7.77982 9.0625 7.5 8.78268 7.5 8.4375ZM6.875 10.9375C6.52982 10.9375 6.25 11.2173 6.25 11.5625C6.25 11.9077 6.52982 12.1875 6.875 12.1875H7.1875C7.53268 12.1875 7.8125 11.9077 7.8125 11.5625C7.8125 11.2173 7.53268 10.9375 7.1875 10.9375H6.875ZM9.375 10.9375C9.02982 10.9375 8.75 11.2173 8.75 11.5625C8.75 11.9077 9.02982 12.1875 9.375 12.1875H14.375C14.7202 12.1875 15 11.9077 15 11.5625C15 11.2173 14.7202 10.9375 14.375 10.9375H9.375Z" fill="#999"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/images/dm_gray.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/images/dm_white.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/images/loading.gif
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
assets/images/loading.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
1
assets/images/play.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"><path d="M17.982 9.275L8.06 3.27A2.013 2.013 0 005 4.994v12.011a2.017 2.017 0 003.06 1.725l9.922-6.005a2.017 2.017 0 000-3.45z"></path></svg>
|
After Width: | Height: | Size: 201 B |
1
assets/images/tv.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"><path d="M16.118 3.667h.382a3.667 3.667 0 013.667 3.667v7.333a3.667 3.667 0 01-3.667 3.667h-11a3.667 3.667 0 01-3.667-3.667V7.333A3.667 3.667 0 015.5 3.666h.382L4.95 2.053a1.1 1.1 0 011.906-1.1l1.567 2.714h5.156L15.146.953a1.101 1.101 0 011.906 1.1l-.934 1.614z" fill="#333" data-darkreader-inline-fill="" style="--darkreader-inline-fill:#c3beb7;"></path><path d="M5.561 5.194h10.878a2.2 2.2 0 012.2 2.2v7.211a2.2 2.2 0 01-2.2 2.2H5.561a2.2 2.2 0 01-2.2-2.2V7.394a2.2 2.2 0 012.2-2.2z" fill="#fff" data-darkreader-inline-fill="" style="--darkreader-inline-fill:#e1ded9;"></path><path d="M6.967 8.556a1.1 1.1 0 011.1 1.1v2.689a1.1 1.1 0 11-2.2 0V9.656a1.1 1.1 0 011.1-1.1zM15.033 8.556a1.1 1.1 0 011.1 1.1v2.689a1.1 1.1 0 11-2.2 0V9.656a1.1 1.1 0 011.1-1.1z" fill="#333" data-darkreader-inline-fill="" style="--darkreader-inline-fill:#c3beb7;"></path></svg>
|
After Width: | Height: | Size: 916 B |
1
assets/images/up.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="18" height="16" viewBox="0 0 18 16" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M9 2.0625C6.85812 2.0625 4.98983 2.1725 3.67735 2.2798C2.77861 2.35327 2.08174 3.04067 2.00119 3.93221C1.90388 5.00924 1.8125 6.43727 1.8125 8C1.8125 9.56273 1.90388 10.9908 2.00119 12.0678C2.08174 12.9593 2.77861 13.6467 3.67735 13.7202C4.98983 13.8275 6.85812 13.9375 9 13.9375C11.1421 13.9375 13.0105 13.8275 14.323 13.7202C15.2216 13.6467 15.9184 12.9595 15.9989 12.0682C16.0962 10.9916 16.1875 9.56386 16.1875 8C16.1875 6.43614 16.0962 5.00837 15.9989 3.9318C15.9184 3.04049 15.2216 2.3533 14.323 2.27983C13.0105 2.17252 11.1421 2.0625 9 2.0625ZM3.5755 1.03395C4.9136 0.924562 6.81674 0.8125 9 0.8125C11.1835 0.8125 13.0868 0.924583 14.4249 1.03398C15.9228 1.15645 17.108 2.31588 17.2438 3.81931C17.3435 4.92296 17.4375 6.38948 17.4375 8C17.4375 9.61052 17.3435 11.077 17.2438 12.1807C17.108 13.6841 15.9228 14.8436 14.4249 14.966C13.0868 15.0754 11.1835 15.1875 9 15.1875C6.81674 15.1875 4.9136 15.0754 3.5755 14.966C2.07738 14.8436 0.892104 13.6838 0.756256 12.1803C0.656505 11.0762 0.5625 9.60942 0.5625 8C0.5625 6.39058 0.656505 4.92379 0.756257 3.81973C0.892104 2.31616 2.07738 1.15643 3.5755 1.03395ZM4.41663 4.93726C4.72729 4.93726 4.97913 5.1891 4.97913 5.49976V8.62476C4.97913 9.34963 5.56675 9.93726 6.29163 9.93726C7.0165 9.93726 7.60413 9.34963 7.60413 8.62476V5.49976C7.60413 5.1891 7.85597 4.93726 8.16663 4.93726C8.47729 4.93726 8.72913 5.1891 8.72913 5.49976V8.62476C8.72913 9.97095 7.63782 11.0623 6.29163 11.0623C4.94543 11.0623 3.85413 9.97095 3.85413 8.62476V5.49976C3.85413 5.1891 4.10597 4.93726 4.41663 4.93726ZM10.2501 4.93726C9.9394 4.93726 9.68756 5.1891 9.68756 5.49976V10.4998C9.68756 10.8104 9.9394 11.0623 10.2501 11.0623C10.5607 11.0623 10.8126 10.8104 10.8126 10.4998V9.60392H12.2292C13.5179 9.60392 14.5626 8.55925 14.5626 7.27059C14.5626 5.98193 13.5179 4.93726 12.2292 4.93726H10.2501ZM12.2292 8.47892H10.8126V6.06226H12.2292C12.8966 6.06226 13.4376 6.60325 13.4376 7.27059C13.4376 7.93793 12.8966 8.47892 12.2292 8.47892Z" fill="#999"></path></svg>
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/images/up_gray.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
3
assets/images/view.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.67735 4.2798C5.98983 4.1725 7.85812 4.0625 10 4.0625C12.1421 4.0625 14.0105 4.17252 15.323 4.27983C16.2216 4.3533 16.9184 5.04049 16.9989 5.9318C17.0962 7.00837 17.1875 8.43614 17.1875 10C17.1875 11.5639 17.0962 12.9916 16.9989 14.0682C16.9184 14.9595 16.2216 15.6467 15.323 15.7202C14.0105 15.8275 12.1421 15.9375 10 15.9375C7.85812 15.9375 5.98983 15.8275 4.67735 15.7202C3.77861 15.6467 3.08174 14.9593 3.00119 14.0678C2.90388 12.9908 2.8125 11.5627 2.8125 10C2.8125 8.43727 2.90388 7.00924 3.00119 5.93221C3.08174 5.04067 3.77861 4.35327 4.67735 4.2798ZM10 2.8125C7.81674 2.8125 5.9136 2.92456 4.5755 3.03395C3.07738 3.15643 1.8921 4.31616 1.75626 5.81973C1.65651 6.92379 1.5625 8.39058 1.5625 10C1.5625 11.6094 1.65651 13.0762 1.75626 14.1803C1.8921 15.6838 3.07738 16.8436 4.5755 16.966C5.9136 17.0754 7.81674 17.1875 10 17.1875C12.1835 17.1875 14.0868 17.0754 15.4249 16.966C16.9228 16.8436 18.108 15.6841 18.2438 14.1807C18.3435 13.077 18.4375 11.6105 18.4375 10C18.4375 8.38948 18.3435 6.92296 18.2438 5.81931C18.108 4.31588 16.9228 3.15645 15.4249 3.03398C14.0868 2.92458 12.1835 2.8125 10 2.8125ZM12.1876 10.722C12.7431 10.4013 12.7431 9.59941 12.1876 9.27866L9.06133 7.47373C8.50577 7.15298 7.81133 7.55392 7.81133 8.19542V11.8053C7.81133 12.4468 8.50577 12.8477 9.06133 12.527L12.1876 10.722Z" fill="#999"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/images/view_gray.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/images/view_white.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -4,4 +4,5 @@ class StyleString {
|
||||
static const double cardSpace = 8;
|
||||
static BorderRadius mdRadius = BorderRadius.circular(6);
|
||||
static const Radius imgRadius = Radius.circular(6);
|
||||
static const double aspectRatio = 16 / 9;
|
||||
}
|
||||
|
66
lib/common/widgets/network_img_layer.dart
Normal file
@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
|
||||
class NetworkImgLayer extends StatelessWidget {
|
||||
final String? src;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final double? cacheW;
|
||||
final double? cacheH;
|
||||
final String? type;
|
||||
final Duration? fadeOutDuration;
|
||||
final Duration? fadeInDuration;
|
||||
var onTap;
|
||||
|
||||
NetworkImgLayer(
|
||||
{Key? key,
|
||||
this.src,
|
||||
required this.width,
|
||||
required this.height,
|
||||
this.cacheW,
|
||||
this.cacheH,
|
||||
this.type,
|
||||
this.fadeOutDuration,
|
||||
this.fadeInDuration,
|
||||
this.onTap})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return src != ''
|
||||
? ClipRRect(
|
||||
borderRadius: BorderRadius.circular(type == 'avatar' ? 50 : 4),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: src!,
|
||||
width: width ?? double.infinity,
|
||||
height: height ?? double.infinity,
|
||||
maxWidthDiskCache: (cacheW ?? width!).toInt(),
|
||||
maxHeightDiskCache: (cacheH ?? height!).toInt(),
|
||||
memCacheWidth: (cacheW ?? width!).toInt(),
|
||||
memCacheHeight: (cacheH ?? height!).toInt(),
|
||||
fit: BoxFit.cover,
|
||||
fadeOutDuration:
|
||||
fadeOutDuration ?? const Duration(milliseconds: 200),
|
||||
fadeInDuration:
|
||||
fadeInDuration ?? const Duration(milliseconds: 200),
|
||||
filterQuality: FilterQuality.high,
|
||||
errorWidget: (context, url, error) => placeholder(context),
|
||||
placeholder: (context, url) => placeholder(context),
|
||||
),
|
||||
)
|
||||
: placeholder(context);
|
||||
}
|
||||
|
||||
Widget placeholder(context) {
|
||||
return SizedBox(
|
||||
width: width ?? double.infinity,
|
||||
height: height ?? double.infinity,
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
'assets/images/loading.png',
|
||||
width: 300,
|
||||
height: 300,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
34
lib/common/widgets/stat/danmu.dart
Normal file
@ -0,0 +1,34 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class StatDanMu extends StatelessWidget {
|
||||
final String? theme;
|
||||
final int? danmu;
|
||||
final String? size;
|
||||
|
||||
const StatDanMu({Key? key, this.theme, this.danmu, this.size})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/dm_$theme.png',
|
||||
width: size == 'medium' ? 16 : 14,
|
||||
height: size == 'medium' ? 16 : 14,
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
Utils.numFormat(danmu!),
|
||||
style: TextStyle(
|
||||
fontSize: size == 'medium' ? 12 : 11,
|
||||
color: theme == 'white'
|
||||
? Colors.white
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
34
lib/common/widgets/stat/view.dart
Normal file
@ -0,0 +1,34 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class StatView extends StatelessWidget {
|
||||
final String? theme;
|
||||
final int? view;
|
||||
final String? size;
|
||||
|
||||
const StatView({Key? key, this.theme, this.view, this.size}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'assets/images/view_$theme.png',
|
||||
width: size == 'medium' ? 16 : 14,
|
||||
height: size == 'medium' ? 16 : 14,
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
Utils.numFormat(view!),
|
||||
// videoItem['stat']['view'].toString(),
|
||||
style: TextStyle(
|
||||
fontSize: size == 'medium' ? 12 : 11,
|
||||
color: theme == 'white'
|
||||
? Colors.white
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
214
lib/common/widgets/video_card_v.dart
Normal file
@ -0,0 +1,214 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
import 'package:pilipala/pages/home/controller.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
|
||||
// 视频卡片 - 垂直布局
|
||||
class VideoCardV extends StatelessWidget {
|
||||
var videoItem;
|
||||
|
||||
VideoCardV({Key? key, required this.videoItem}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 0.8,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: StyleString.mdRadius,
|
||||
),
|
||||
margin: EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
Get.toNamed('/video?aid=${videoItem.id}',
|
||||
arguments: {'videoItem': videoItem});
|
||||
},
|
||||
onLongPress: () {
|
||||
print('长按');
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: StyleString.imgRadius,
|
||||
topRight: StyleString.imgRadius,
|
||||
),
|
||||
child: AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||
double maxWidth = boxConstraints.maxWidth;
|
||||
double maxHeight = boxConstraints.maxHeight;
|
||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
||||
return Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
// 指定图片尺寸
|
||||
// src: videoItem['pic'] + '@${(maxWidth * 2).toInt() }w',
|
||||
src: videoItem.pic + '@.webp',
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: AnimatedOpacity(
|
||||
opacity: 1,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: VideoStat(
|
||||
view: videoItem.stat.view,
|
||||
danmaku: videoItem.stat.danmaku,
|
||||
duration: videoItem.duration,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
VideoContent(videoItem: videoItem)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoContent extends StatelessWidget {
|
||||
final videoItem;
|
||||
const VideoContent({Key? key, required this.videoItem}) : super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
// 多列
|
||||
padding: const EdgeInsets.fromLTRB(8, 8, 6, 7),
|
||||
// 单列
|
||||
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
videoItem.title,
|
||||
textAlign: TextAlign.start,
|
||||
style: const TextStyle(
|
||||
// fontSize:
|
||||
// Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500),
|
||||
maxLines: Get.find<HomeController>().crossAxisCount,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
SizedBox(
|
||||
height: 18,
|
||||
child: Row(
|
||||
children: [
|
||||
if (videoItem.rcmdReason.content != '') ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.fromLTRB(3, 1, 3, 1),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primaryContainer
|
||||
.withOpacity(0.6),
|
||||
borderRadius: BorderRadius.circular(3)),
|
||||
child: Text(
|
||||
videoItem.rcmdReason.content,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4)
|
||||
],
|
||||
Expanded(
|
||||
child: LayoutBuilder(builder:
|
||||
(BuildContext context, BoxConstraints constraints) {
|
||||
return SizedBox(
|
||||
width: constraints.maxWidth,
|
||||
child: Text(
|
||||
videoItem.owner.name,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoStat extends StatelessWidget {
|
||||
final int? view;
|
||||
final int? danmaku;
|
||||
final int? duration;
|
||||
|
||||
const VideoStat(
|
||||
{Key? key,
|
||||
required this.view,
|
||||
required this.danmaku,
|
||||
required this.duration})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 45,
|
||||
padding: const EdgeInsets.only(top: 22, left: 8, right: 8),
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[
|
||||
Colors.transparent,
|
||||
Colors.black54,
|
||||
],
|
||||
tileMode: TileMode.mirror,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
StatView(
|
||||
theme: 'white',
|
||||
view: view,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
StatDanMu(
|
||||
theme: 'white',
|
||||
danmu: danmaku,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
Utils.timeFormat(duration!),
|
||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
97
lib/models/models_rec_video_item.dart
Normal file
@ -0,0 +1,97 @@
|
||||
class RecVideoItemModel {
|
||||
RecVideoItemModel({
|
||||
this.id,
|
||||
this.bvid,
|
||||
this.cid,
|
||||
this.goto,
|
||||
this.uri,
|
||||
this.pic,
|
||||
this.title,
|
||||
this.duration,
|
||||
this.pubdate,
|
||||
this.owner,
|
||||
this.stat,
|
||||
this.rcmdReason,
|
||||
});
|
||||
|
||||
int? id = -1;
|
||||
String? bvid = '';
|
||||
int? cid = -1;
|
||||
String? goto = '';
|
||||
String? uri = '';
|
||||
String? pic = '';
|
||||
String? title = '';
|
||||
int? duration = -1;
|
||||
int? pubdate = -1;
|
||||
Onwer? owner;
|
||||
Stat? stat;
|
||||
RcmdReason? rcmdReason;
|
||||
|
||||
RecVideoItemModel.fromJson(Map<String, dynamic> json) {
|
||||
id = json["id"];
|
||||
bvid = json["bvid"];
|
||||
cid = json["cid"];
|
||||
goto = json["goto"];
|
||||
uri = json["uri"];
|
||||
pic = json["pic"];
|
||||
title = json["title"];
|
||||
duration = json["duration"];
|
||||
pubdate = json["pubdate"];
|
||||
owner = Onwer.fromJson(json["owner"]);
|
||||
stat = Stat.fromJson(json["stat"]);
|
||||
rcmdReason = json["rcmd_reason"] != null
|
||||
? RcmdReason.fromJson(json["rcmd_reason"])
|
||||
: RcmdReason(content: '');
|
||||
}
|
||||
}
|
||||
|
||||
class Onwer {
|
||||
Onwer({
|
||||
this.mid,
|
||||
this.name,
|
||||
this.face,
|
||||
});
|
||||
|
||||
int? mid;
|
||||
String? name;
|
||||
String? face;
|
||||
|
||||
Onwer.fromJson(Map<String, dynamic> json) {
|
||||
mid = json["mid"];
|
||||
name = json["name"];
|
||||
face = json['face'];
|
||||
}
|
||||
}
|
||||
|
||||
class Stat {
|
||||
Stat({
|
||||
this.view,
|
||||
this.like,
|
||||
this.danmaku,
|
||||
});
|
||||
|
||||
int? view;
|
||||
int? like;
|
||||
int? danmaku;
|
||||
|
||||
Stat.fromJson(Map<String, dynamic> json) {
|
||||
view = json["view"];
|
||||
like = json["like"];
|
||||
danmaku = json['danmaku'];
|
||||
}
|
||||
}
|
||||
|
||||
class RcmdReason {
|
||||
RcmdReason({
|
||||
this.reasonType,
|
||||
this.content,
|
||||
});
|
||||
|
||||
int? reasonType;
|
||||
String? content = '';
|
||||
|
||||
RcmdReason.fromJson(Map<String, dynamic> json) {
|
||||
reasonType = json["reason_type"];
|
||||
content = json["content"] ?? '';
|
||||
}
|
||||
}
|
@ -2,14 +2,16 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/api.dart';
|
||||
import 'package:pilipala/http/init.dart';
|
||||
import 'package:pilipala/models/models_rec_video_item.dart';
|
||||
|
||||
class HomeController extends GetxController {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
int count = 12;
|
||||
int _currentPage = 1;
|
||||
int crossAxisCount = 2;
|
||||
RxList videoList = [].obs;
|
||||
RxList<RecVideoItemModel> videoList = [RecVideoItemModel()].obs;
|
||||
bool isLoadingMore = false;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
@ -22,13 +24,17 @@ class HomeController extends GetxController {
|
||||
Api.recommendList,
|
||||
data: {'feed_version': "V3", 'ps': count, 'fresh_idx': _currentPage},
|
||||
);
|
||||
var data = res.data['data']['item'];
|
||||
List<RecVideoItemModel> list = [];
|
||||
for (var i in res.data['data']['item']) {
|
||||
print(i);
|
||||
list.add(RecVideoItemModel.fromJson(i));
|
||||
}
|
||||
if (type == 'init') {
|
||||
videoList.value = data;
|
||||
videoList.value = list;
|
||||
} else if (type == 'onRefresh') {
|
||||
videoList.insertAll(0, data);
|
||||
videoList.insertAll(0, list);
|
||||
} else if (type == 'onLoad') {
|
||||
videoList.addAll(data);
|
||||
videoList.addAll(list);
|
||||
}
|
||||
_currentPage += 1;
|
||||
isLoadingMore = false;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/widgets/video_card_v.dart';
|
||||
import './controller.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'package:pilipala/pages/home/widgets/app_bar.dart';
|
||||
@ -76,15 +77,14 @@ class _HomePageState extends State<HomePage>
|
||||
// 列数
|
||||
crossAxisCount: _homeController.crossAxisCount,
|
||||
mainAxisExtent: MediaQuery.of(context).size.width /
|
||||
_homeController.crossAxisCount *
|
||||
(10 / 16) +
|
||||
_homeController.crossAxisCount /
|
||||
StyleString.aspectRatio +
|
||||
72),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
return Container(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: Text(index.toString()),
|
||||
);
|
||||
return videoList.isNotEmpty
|
||||
? VideoCardV(videoItem: videoList[index])
|
||||
: const Text('加载中');
|
||||
},
|
||||
childCount: videoList.isNotEmpty ? videoList.length : 10,
|
||||
),
|
||||
|
@ -1,5 +1,6 @@
|
||||
// 工具函数
|
||||
import 'dart:io';
|
||||
import 'package:get/get_utils/get_utils.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class Utils {
|
||||
@ -13,4 +14,28 @@ class Utils {
|
||||
}
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
static String numFormat(int number) {
|
||||
String res = (number / 10000).toString();
|
||||
if (int.parse(res.split('.')[0]) >= 1) {
|
||||
return '${(number / 10000).toPrecision(1)}万';
|
||||
} else {
|
||||
return number.toString();
|
||||
}
|
||||
}
|
||||
|
||||
static String timeFormat(int time) {
|
||||
// 1小时内
|
||||
if (time < 3600) {
|
||||
int minute = time ~/ 60;
|
||||
double res = time / 60;
|
||||
if (minute != res) {
|
||||
return '$minute:${(time - minute * 60) < 10 ? '0${(time - minute * 60)}' : (time - minute * 60)}';
|
||||
} else {
|
||||
return minute.toString();
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,10 +74,8 @@ flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
assets:
|
||||
- assets/images/
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
|