feat: 首页单列模式
This commit is contained in:
@ -45,11 +45,6 @@ class VideoCardVSkeleton extends StatelessWidget {
|
|||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
),
|
),
|
||||||
Container(
|
|
||||||
width: 80,
|
|
||||||
height: 12,
|
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -15,12 +15,14 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class VideoCardV extends StatelessWidget {
|
class VideoCardV extends StatelessWidget {
|
||||||
final dynamic videoItem;
|
final dynamic videoItem;
|
||||||
|
final int crossAxisCount;
|
||||||
final Function()? longPress;
|
final Function()? longPress;
|
||||||
final Function()? longPressEnd;
|
final Function()? longPressEnd;
|
||||||
|
|
||||||
const VideoCardV({
|
const VideoCardV({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.videoItem,
|
required this.videoItem,
|
||||||
|
required this.crossAxisCount,
|
||||||
this.longPress,
|
this.longPress,
|
||||||
this.longPressEnd,
|
this.longPressEnd,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -77,7 +79,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(videoItem.id);
|
String heroTag = Utils.makeHeroTag(videoItem.id);
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 1,
|
elevation: crossAxisCount == 1 ? 0 : 1,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -100,17 +102,27 @@ class VideoCardV extends StatelessWidget {
|
|||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
return Hero(
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
src: videoItem.pic,
|
src: videoItem.pic,
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (crossAxisCount == 1)
|
||||||
|
PBadge(
|
||||||
|
bottom: 10,
|
||||||
|
right: 10,
|
||||||
|
text: videoItem.duration,
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
VideoContent(videoItem: videoItem)
|
VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -121,22 +133,47 @@ class VideoCardV extends StatelessWidget {
|
|||||||
|
|
||||||
class VideoContent extends StatelessWidget {
|
class VideoContent extends StatelessWidget {
|
||||||
final dynamic videoItem;
|
final dynamic videoItem;
|
||||||
const VideoContent({Key? key, required this.videoItem}) : super(key: key);
|
final int crossAxisCount;
|
||||||
|
const VideoContent(
|
||||||
|
{Key? key, required this.videoItem, required this.crossAxisCount})
|
||||||
|
: super(key: key);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
|
flex: crossAxisCount == 1 ? 0 : 1,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(9, 8, 9, 4),
|
padding: crossAxisCount == 1
|
||||||
|
? const EdgeInsets.fromLTRB(9, 9, 9, 4)
|
||||||
|
: const EdgeInsets.fromLTRB(9, 8, 9, 4),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
videoItem.title,
|
videoItem.title,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (videoItem.goto == 'av' && crossAxisCount == 1) ...[
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
WatchLater(
|
||||||
|
size: 32,
|
||||||
|
iconSize: 18,
|
||||||
|
callFn: () async {
|
||||||
|
int aid = videoItem.param;
|
||||||
|
var res =
|
||||||
|
await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (crossAxisCount == 1) const SizedBox(height: 6),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (videoItem.goto == 'bangumi') ...[
|
if (videoItem.goto == 'bangumi') ...[
|
||||||
@ -167,6 +204,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: crossAxisCount == 1 ? 0 : 1,
|
||||||
child: Text(
|
child: Text(
|
||||||
videoItem.owner.name,
|
videoItem.owner.name,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -177,30 +215,102 @@ class VideoContent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (videoItem.goto == 'av')
|
if (crossAxisCount == 1) ...[
|
||||||
SizedBox(
|
Text(
|
||||||
width: 24,
|
' • ',
|
||||||
height: 24,
|
style: TextStyle(
|
||||||
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
VideoStat(
|
||||||
|
videoItem: videoItem,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
const Spacer(),
|
||||||
|
if (videoItem.goto == 'av' && crossAxisCount != 1)
|
||||||
|
WatchLater(
|
||||||
|
size: 24,
|
||||||
|
iconSize: 14,
|
||||||
|
callFn: () async {
|
||||||
|
int aid = videoItem.param;
|
||||||
|
var res =
|
||||||
|
await UserHttp.toViewLater(bvid: IdUtils.av2bv(aid));
|
||||||
|
SmartDialog.showToast(res['msg']);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoStat extends StatelessWidget {
|
||||||
|
final dynamic videoItem;
|
||||||
|
|
||||||
|
const VideoStat({
|
||||||
|
Key? key,
|
||||||
|
required this.videoItem,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${videoItem.stat.view}次观看',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
' • ${videoItem.stat.danmu}条弹幕',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WatchLater extends StatelessWidget {
|
||||||
|
final double? size;
|
||||||
|
final double? iconSize;
|
||||||
|
final Function? callFn;
|
||||||
|
|
||||||
|
const WatchLater({
|
||||||
|
Key? key,
|
||||||
|
required this.size,
|
||||||
|
required this.iconSize,
|
||||||
|
this.callFn,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
child: PopupMenuButton<String>(
|
child: PopupMenuButton<String>(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
tooltip: '稍后再看',
|
tooltip: '稍后再看',
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.more_vert_outlined,
|
Icons.more_vert_outlined,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
size: 14,
|
size: iconSize,
|
||||||
),
|
),
|
||||||
position: PopupMenuPosition.under,
|
position: PopupMenuPosition.under,
|
||||||
// constraints: const BoxConstraints(maxHeight: 35),
|
// constraints: const BoxConstraints(maxHeight: 35),
|
||||||
onSelected: (String type) {},
|
onSelected: (String type) {},
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
|
||||||
<PopupMenuEntry<String>>[
|
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
onTap: () async {
|
onTap: () => callFn!(),
|
||||||
int aid = videoItem.param;
|
|
||||||
var res = await UserHttp.toViewLater(
|
|
||||||
bvid: IdUtils.av2bv(aid));
|
|
||||||
SmartDialog.showToast(res['msg']);
|
|
||||||
},
|
|
||||||
value: 'pause',
|
value: 'pause',
|
||||||
height: 35,
|
height: 35,
|
||||||
child: const Row(
|
child: const Row(
|
||||||
@ -213,116 +323,6 @@ class VideoContent extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// Row(
|
|
||||||
// children: [
|
|
||||||
// const SizedBox(width: 1),
|
|
||||||
// StatView(
|
|
||||||
// theme: 'gray',
|
|
||||||
// view: videoItem.stat.view,
|
|
||||||
// ),
|
|
||||||
// const SizedBox(width: 10),
|
|
||||||
// StatDanMu(
|
|
||||||
// theme: 'gray',
|
|
||||||
// danmu: videoItem.stat.danmaku,
|
|
||||||
// ),
|
|
||||||
// const Spacer(),
|
|
||||||
// SizedBox(
|
|
||||||
// width: 24,
|
|
||||||
// height: 24,
|
|
||||||
// child: PopupMenuButton<String>(
|
|
||||||
// padding: EdgeInsets.zero,
|
|
||||||
// tooltip: '稍后再看',
|
|
||||||
// icon: Icon(
|
|
||||||
// Icons.more_vert_outlined,
|
|
||||||
// color: Theme.of(context).colorScheme.outline,
|
|
||||||
// size: 14,
|
|
||||||
// ),
|
|
||||||
// position: PopupMenuPosition.under,
|
|
||||||
// // constraints: const BoxConstraints(maxHeight: 35),
|
|
||||||
// onSelected: (String type) {},
|
|
||||||
// itemBuilder: (BuildContext context) =>
|
|
||||||
// <PopupMenuEntry<String>>[
|
|
||||||
// PopupMenuItem<String>(
|
|
||||||
// onTap: () async {
|
|
||||||
// var res =
|
|
||||||
// await UserHttp.toViewLater(bvid: videoItem.bvid);
|
|
||||||
// SmartDialog.showToast(res['msg']);
|
|
||||||
// },
|
|
||||||
// value: 'pause',
|
|
||||||
// height: 35,
|
|
||||||
// child: const Row(
|
|
||||||
// children: [
|
|
||||||
// Icon(Icons.watch_later_outlined, size: 16),
|
|
||||||
// SizedBox(width: 6),
|
|
||||||
// Text('稍后再看', style: TextStyle(fontSize: 13))
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: 48,
|
|
||||||
padding: const EdgeInsets.only(top: 22, left: 6, right: 6),
|
|
||||||
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: 6),
|
|
||||||
StatDanMu(
|
|
||||||
theme: 'white',
|
|
||||||
danmu: danmaku,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
Utils.timeFormat(duration!),
|
|
||||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,27 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/http/live.dart';
|
import 'package:pilipala/http/live.dart';
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class LiveController extends GetxController {
|
class LiveController extends GetxController {
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
int count = 12;
|
int count = 12;
|
||||||
int _currentPage = 1;
|
int _currentPage = 1;
|
||||||
int crossAxisCount = 2;
|
RxInt crossAxisCount = 2.obs;
|
||||||
RxList<LiveItemModel> liveList = [LiveItemModel()].obs;
|
RxList<LiveItemModel> liveList = [LiveItemModel()].obs;
|
||||||
bool isLoadingMore = false;
|
bool isLoadingMore = false;
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
OverlayEntry? popupDialog;
|
OverlayEntry? popupDialog;
|
||||||
|
Box setting = GStrorage.setting;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
crossAxisCount.value =
|
||||||
|
setting.get(SettingBoxKey.enableSingleRow, defaultValue: false) ? 1 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
// 获取推荐
|
// 获取推荐
|
||||||
Future queryLiveList(type) async {
|
Future queryLiveList(type) async {
|
||||||
|
@ -129,14 +129,15 @@ class _LivePageState extends State<LivePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget contentGrid(ctr, liveList) {
|
Widget contentGrid(ctr, liveList) {
|
||||||
double maxWidth = Get.size.width;
|
// double maxWidth = Get.size.width;
|
||||||
int baseWidth = 500;
|
// int baseWidth = 500;
|
||||||
int step = 300;
|
// int step = 300;
|
||||||
int crossAxisCount =
|
// int crossAxisCount =
|
||||||
maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
|
// maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
|
||||||
if (maxWidth < 300) {
|
// if (maxWidth < 300) {
|
||||||
crossAxisCount = 1;
|
// crossAxisCount = 1;
|
||||||
}
|
// }
|
||||||
|
int crossAxisCount = ctr.crossAxisCount.value;
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
// 行间距
|
// 行间距
|
||||||
@ -147,13 +148,15 @@ class _LivePageState extends State<LivePage> {
|
|||||||
crossAxisCount: crossAxisCount,
|
crossAxisCount: crossAxisCount,
|
||||||
mainAxisExtent:
|
mainAxisExtent:
|
||||||
Get.size.width / crossAxisCount / StyleString.aspectRatio +
|
Get.size.width / crossAxisCount / StyleString.aspectRatio +
|
||||||
68 * MediaQuery.of(context).textScaleFactor,
|
(crossAxisCount == 1 ? 48 : 68) *
|
||||||
|
MediaQuery.of(context).textScaleFactor,
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return liveList!.isNotEmpty
|
return liveList!.isNotEmpty
|
||||||
? LiveCardV(
|
? LiveCardV(
|
||||||
liveItem: liveList[index],
|
liveItem: liveList[index],
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
longPress: () {
|
longPress: () {
|
||||||
_liveController.popupDialog =
|
_liveController.popupDialog =
|
||||||
_createPopupDialog(liveList[index]);
|
_createPopupDialog(liveList[index]);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
@ -9,12 +8,14 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class LiveCardV extends StatelessWidget {
|
class LiveCardV extends StatelessWidget {
|
||||||
final LiveItemModel liveItem;
|
final LiveItemModel liveItem;
|
||||||
|
final int crossAxisCount;
|
||||||
final Function()? longPress;
|
final Function()? longPress;
|
||||||
final Function()? longPressEnd;
|
final Function()? longPressEnd;
|
||||||
|
|
||||||
const LiveCardV({
|
const LiveCardV({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.liveItem,
|
required this.liveItem,
|
||||||
|
required this.crossAxisCount,
|
||||||
this.longPress,
|
this.longPress,
|
||||||
this.longPressEnd,
|
this.longPressEnd,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -23,7 +24,7 @@ class LiveCardV extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(liveItem.roomId);
|
String heroTag = Utils.makeHeroTag(liveItem.roomId);
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 1,
|
elevation: crossAxisCount == 1 ? 0 : 1,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
@ -45,12 +46,7 @@ class LiveCardV extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.all(StyleString.imgRadius),
|
||||||
topLeft: StyleString.imgRadius,
|
|
||||||
topRight: StyleString.imgRadius,
|
|
||||||
bottomLeft: StyleString.imgRadius,
|
|
||||||
bottomRight: StyleString.imgRadius,
|
|
||||||
),
|
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
@ -66,6 +62,7 @@ class LiveCardV extends StatelessWidget {
|
|||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (crossAxisCount != 1)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
@ -83,7 +80,7 @@ class LiveCardV extends StatelessWidget {
|
|||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LiveContent(liveItem: liveItem)
|
LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -94,13 +91,18 @@ class LiveCardV extends StatelessWidget {
|
|||||||
|
|
||||||
class LiveContent extends StatelessWidget {
|
class LiveContent extends StatelessWidget {
|
||||||
final dynamic liveItem;
|
final dynamic liveItem;
|
||||||
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
|
final int crossAxisCount;
|
||||||
|
const LiveContent(
|
||||||
|
{Key? key, required this.liveItem, required this.crossAxisCount})
|
||||||
|
: super(key: key);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
|
flex: crossAxisCount == 1 ? 0 : 1,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
// 多列
|
padding: crossAxisCount == 1
|
||||||
padding: const EdgeInsets.fromLTRB(9, 9, 9, 8),
|
? const EdgeInsets.fromLTRB(9, 9, 9, 4)
|
||||||
|
: const EdgeInsets.fromLTRB(9, 8, 9, 8),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@ -112,29 +114,40 @@ class LiveContent extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0.3,
|
letterSpacing: 0.3,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
maxLines: crossAxisCount == 1 ? 1 : 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
if (crossAxisCount == 1) const SizedBox(height: 4),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const PBadge(
|
Text(
|
||||||
text: 'UP',
|
|
||||||
size: 'small',
|
|
||||||
stack: 'normal',
|
|
||||||
fs: 9,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
liveItem.uname,
|
liveItem.uname,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize:
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
)
|
if (crossAxisCount == 1) ...[
|
||||||
|
Text(
|
||||||
|
' • ${liveItem!.areaName!}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
' • ${liveItem!.watchedShow!['text_small']}人观看',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -12,10 +12,14 @@ class RcmdController extends GetxController {
|
|||||||
bool isLoadingMore = true;
|
bool isLoadingMore = true;
|
||||||
OverlayEntry? popupDialog;
|
OverlayEntry? popupDialog;
|
||||||
Box recVideo = GStrorage.recVideo;
|
Box recVideo = GStrorage.recVideo;
|
||||||
|
Box setting = GStrorage.setting;
|
||||||
|
RxInt crossAxisCount = 2.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
crossAxisCount.value =
|
||||||
|
setting.get(SettingBoxKey.enableSingleRow, defaultValue: false) ? 1 : 2;
|
||||||
if (recVideo.get('cacheList') != null &&
|
if (recVideo.get('cacheList') != null &&
|
||||||
recVideo.get('cacheList').isNotEmpty) {
|
recVideo.get('cacheList').isNotEmpty) {
|
||||||
List<RecVideoItemAppModel> list = [];
|
List<RecVideoItemAppModel> list = [];
|
||||||
|
@ -142,31 +142,34 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget contentGrid(ctr, videoList) {
|
Widget contentGrid(ctr, videoList) {
|
||||||
double maxWidth = Get.size.width;
|
// double maxWidth = Get.size.width;
|
||||||
int baseWidth = 500;
|
// int baseWidth = 500;
|
||||||
int step = 300;
|
// int step = 300;
|
||||||
int crossAxisCount =
|
// int crossAxisCount =
|
||||||
maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
|
// maxWidth > baseWidth ? 2 + ((maxWidth - baseWidth) / step).ceil() : 2;
|
||||||
if (maxWidth < 300) {
|
// if (maxWidth < 300) {
|
||||||
crossAxisCount = 1;
|
// crossAxisCount = 1;
|
||||||
}
|
// }
|
||||||
|
int crossAxisCount = ctr.crossAxisCount.value;
|
||||||
|
double mainAxisExtent =
|
||||||
|
(Get.size.width / crossAxisCount / StyleString.aspectRatio) +
|
||||||
|
68 * MediaQuery.of(context).textScaleFactor;
|
||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
// 行间距
|
// 行间距
|
||||||
mainAxisSpacing: StyleString.cardSpace + 4,
|
mainAxisSpacing: StyleString.safeSpace,
|
||||||
// 列间距
|
// 列间距
|
||||||
crossAxisSpacing: StyleString.cardSpace + 4,
|
crossAxisSpacing: StyleString.safeSpace,
|
||||||
// 列数
|
// 列数
|
||||||
crossAxisCount: crossAxisCount,
|
crossAxisCount: crossAxisCount,
|
||||||
mainAxisExtent:
|
mainAxisExtent: mainAxisExtent,
|
||||||
(Get.size.width / crossAxisCount / StyleString.aspectRatio) +
|
|
||||||
68 * MediaQuery.of(context).textScaleFactor,
|
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
return videoList!.isNotEmpty
|
return videoList!.isNotEmpty
|
||||||
? VideoCardV(
|
? VideoCardV(
|
||||||
videoItem: videoList[index],
|
videoItem: videoList[index],
|
||||||
|
crossAxisCount: crossAxisCount,
|
||||||
longPress: () {
|
longPress: () {
|
||||||
_rcmdController.popupDialog =
|
_rcmdController.popupDialog =
|
||||||
_createPopupDialog(videoList[index]);
|
_createPopupDialog(videoList[index]);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/models/common/theme_type.dart';
|
import 'package:pilipala/models/common/theme_type.dart';
|
||||||
@ -75,6 +76,13 @@ class _StyleSettingState extends State<StyleSetting> {
|
|||||||
setKey: SettingBoxKey.iosTransition,
|
setKey: SettingBoxKey.iosTransition,
|
||||||
defaultVal: false,
|
defaultVal: false,
|
||||||
),
|
),
|
||||||
|
SetSwitchItem(
|
||||||
|
title: '首页单列',
|
||||||
|
subTitle: '每行展示一个内容卡片',
|
||||||
|
setKey: SettingBoxKey.enableSingleRow,
|
||||||
|
defaultVal: false,
|
||||||
|
callFn: (val) => {SmartDialog.showToast('下次启动时生效')},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
dense: false,
|
dense: false,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -8,12 +8,14 @@ class SetSwitchItem extends StatefulWidget {
|
|||||||
final String? subTitle;
|
final String? subTitle;
|
||||||
final String? setKey;
|
final String? setKey;
|
||||||
final bool? defaultVal;
|
final bool? defaultVal;
|
||||||
|
final Function? callFn;
|
||||||
|
|
||||||
const SetSwitchItem({
|
const SetSwitchItem({
|
||||||
this.title,
|
this.title,
|
||||||
this.subTitle,
|
this.subTitle,
|
||||||
this.setKey,
|
this.setKey,
|
||||||
this.defaultVal,
|
this.defaultVal,
|
||||||
|
this.callFn,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -32,12 +34,15 @@ class _SetSwitchItemState extends State<SetSwitchItem> {
|
|||||||
val = Setting.get(widget.setKey, defaultValue: widget.defaultVal ?? false);
|
val = Setting.get(widget.setKey, defaultValue: widget.defaultVal ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void switchChange(value) {
|
void switchChange(value) async {
|
||||||
val = value ?? !val;
|
val = value ?? !val;
|
||||||
Setting.put(widget.setKey, val);
|
await Setting.put(widget.setKey, val);
|
||||||
if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
|
if (widget.setKey == SettingBoxKey.autoUpdate && value == true) {
|
||||||
Utils.checkUpdata();
|
Utils.checkUpdata();
|
||||||
}
|
}
|
||||||
|
if (widget.callFn != null) {
|
||||||
|
widget.callFn!.call(val);
|
||||||
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +112,7 @@ class SettingBoxKey {
|
|||||||
static const String dynamicColor = 'dynamicColor'; // bool
|
static const String dynamicColor = 'dynamicColor'; // bool
|
||||||
static const String customColor = 'customColor'; // 自定义主题色
|
static const String customColor = 'customColor'; // 自定义主题色
|
||||||
static const String iosTransition = 'iosTransition'; // ios路由
|
static const String iosTransition = 'iosTransition'; // ios路由
|
||||||
|
static const String enableSingleRow = 'enableSingleRow'; // 首页单列
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalCacheKey {
|
class LocalCacheKey {
|
||||||
|
Reference in New Issue
Block a user