Merge branch 'feature-m3Design'
This commit is contained in:
@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class StyleString {
|
class StyleString {
|
||||||
static const double cardSpace = 8;
|
static const double cardSpace = 8;
|
||||||
static BorderRadius mdRadius = BorderRadius.circular(6);
|
static const double safeSpace = 12;
|
||||||
static const Radius imgRadius = Radius.circular(6);
|
static BorderRadius mdRadius = BorderRadius.circular(10);
|
||||||
|
static const Radius imgRadius = Radius.circular(10);
|
||||||
static const double aspectRatio = 16 / 10;
|
static const double aspectRatio = 16 / 10;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,9 +27,6 @@ class VideoCardHSkeleton extends StatelessWidget {
|
|||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
|
||||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
|
|||||||
@ -53,7 +53,7 @@ class VideoCardVSkeleton extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
width: 80,
|
width: 80,
|
||||||
height: 13,
|
height: 12,
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: Theme.of(context).colorScheme.background,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -13,7 +13,6 @@ class AppBarWidget extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
final bool visible;
|
final bool visible;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// TODO: implement preferredSize
|
|
||||||
Size get preferredSize => child.preferredSize;
|
Size get preferredSize => child.preferredSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HttpError extends StatelessWidget {
|
class HttpError extends StatelessWidget {
|
||||||
HttpError({required this.errMsg, required this.fn, super.key});
|
const HttpError({required this.errMsg, required this.fn, super.key});
|
||||||
|
|
||||||
String errMsg = '';
|
final String? errMsg;
|
||||||
final Function()? fn;
|
final Function()? fn;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -16,7 +16,7 @@ class HttpError extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
errMsg,
|
errMsg ?? '请求异常',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -6,9 +6,10 @@ import 'package:pilipala/pages/rcmd/controller.dart';
|
|||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class LiveCard extends StatelessWidget {
|
class LiveCard extends StatelessWidget {
|
||||||
var liveItem;
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
|
final liveItem;
|
||||||
|
|
||||||
LiveCard({
|
const LiveCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.liveItem,
|
required this.liveItem,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -37,14 +38,11 @@ class LiveCard 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;
|
||||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
// 指定图片尺寸
|
|
||||||
// src: videoItem.pic + '@${(maxWidth * 2).toInt()}w',
|
|
||||||
src: liveItem.cover + '@.webp',
|
src: liveItem.cover + '@.webp',
|
||||||
type: 'emote',
|
type: 'emote',
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
@ -79,6 +77,7 @@ class LiveCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LiveContent extends StatelessWidget {
|
class LiveContent extends StatelessWidget {
|
||||||
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final liveItem;
|
final liveItem;
|
||||||
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
|
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
@ -140,19 +139,19 @@ class LiveStat extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
// Row(
|
||||||
children: [
|
// children: [
|
||||||
// StatView(
|
// StatView(
|
||||||
// theme: 'white',
|
// theme: 'white',
|
||||||
// view: view,
|
// view: view,
|
||||||
// ),
|
// ),
|
||||||
// const SizedBox(width: 8),
|
// const SizedBox(width: 8),
|
||||||
// StatDanMu(
|
// StatDanMu(
|
||||||
// theme: 'white',
|
// theme: 'white',
|
||||||
// danmu: danmaku,
|
// danmu: danmaku,
|
||||||
// ),
|
// ),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
Text(
|
Text(
|
||||||
online.toString(),
|
online.toString(),
|
||||||
style: const TextStyle(fontSize: 11, color: Colors.white),
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import 'package:pilipala/common/constants.dart';
|
|||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
class OverlayPop extends StatelessWidget {
|
class OverlayPop extends StatelessWidget {
|
||||||
var videoItem;
|
final dynamic videoItem;
|
||||||
OverlayPop({super.key, this.videoItem});
|
const OverlayPop({super.key, this.videoItem});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: depend_on_referenced_packages
|
||||||
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui' as ui show Image;
|
import 'dart:ui' as ui show Image;
|
||||||
|
|
||||||
@ -15,7 +17,8 @@ class PullToRefreshHeader extends StatelessWidget {
|
|||||||
this.info,
|
this.info,
|
||||||
this.lastRefreshTime, {
|
this.lastRefreshTime, {
|
||||||
this.color,
|
this.color,
|
||||||
});
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
final PullToRefreshScrollNotificationInfo? info;
|
final PullToRefreshScrollNotificationInfo? info;
|
||||||
final DateTime? lastRefreshTime;
|
final DateTime? lastRefreshTime;
|
||||||
@ -23,21 +26,21 @@ class PullToRefreshHeader extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final PullToRefreshScrollNotificationInfo? _info = info;
|
final PullToRefreshScrollNotificationInfo? infos = info;
|
||||||
if (_info == null) {
|
if (infos == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
String text = '';
|
String text = '';
|
||||||
if (_info.mode == PullToRefreshIndicatorMode.armed) {
|
if (infos.mode == PullToRefreshIndicatorMode.armed) {
|
||||||
text = 'Release to refresh';
|
text = 'Release to refresh';
|
||||||
} else if (_info.mode == PullToRefreshIndicatorMode.refresh ||
|
} else if (infos.mode == PullToRefreshIndicatorMode.refresh ||
|
||||||
_info.mode == PullToRefreshIndicatorMode.snap) {
|
infos.mode == PullToRefreshIndicatorMode.snap) {
|
||||||
text = 'Loading...';
|
text = 'Loading...';
|
||||||
} else if (_info.mode == PullToRefreshIndicatorMode.done) {
|
} else if (infos.mode == PullToRefreshIndicatorMode.done) {
|
||||||
text = 'Refresh completed.';
|
text = 'Refresh completed.';
|
||||||
} else if (_info.mode == PullToRefreshIndicatorMode.drag) {
|
} else if (infos.mode == PullToRefreshIndicatorMode.drag) {
|
||||||
text = 'Pull to refresh';
|
text = 'Pull to refresh';
|
||||||
} else if (_info.mode == PullToRefreshIndicatorMode.canceled) {
|
} else if (infos.mode == PullToRefreshIndicatorMode.canceled) {
|
||||||
text = 'Cancel refresh';
|
text = 'Cancel refresh';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,16 +70,15 @@ class PullToRefreshHeader extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: RefreshImage(top),
|
|
||||||
margin: const EdgeInsets.only(right: 12.0),
|
margin: const EdgeInsets.only(right: 12.0),
|
||||||
|
child: RefreshImage(top, null),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(text, style: ts),
|
Text(text, style: ts),
|
||||||
Text(
|
Text(
|
||||||
'Last updated:' +
|
'Last updated:${DateFormat('yyyy-MM-dd hh:mm').format(time)}',
|
||||||
DateFormat('yyyy-MM-dd hh:mm').format(time),
|
|
||||||
style: ts.copyWith(fontSize: 14),
|
style: ts.copyWith(fontSize: 14),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -92,7 +94,7 @@ class PullToRefreshHeader extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RefreshImage extends StatelessWidget {
|
class RefreshImage extends StatelessWidget {
|
||||||
const RefreshImage(this.top);
|
const RefreshImage(this.top, Key? key) : super(key: key);
|
||||||
|
|
||||||
final double top;
|
final double top;
|
||||||
|
|
||||||
|
|||||||
24
lib/common/widgets/sliver_header.dart
Normal file
24
lib/common/widgets/sliver_header.dart
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SliverHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||||
|
SliverHeaderDelegate({required this.height, required this.child});
|
||||||
|
|
||||||
|
final double height;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(
|
||||||
|
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get maxExtent => height;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get minExtent => height;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
|
||||||
|
true;
|
||||||
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
@ -12,12 +11,16 @@ class StatDanMu extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color color =
|
Map<String, Color> colorObject = {
|
||||||
theme == 'white' ? Colors.white : Theme.of(context).colorScheme.outline;
|
'white': Colors.white,
|
||||||
|
'gray': Theme.of(context).colorScheme.outline,
|
||||||
|
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
|
||||||
|
};
|
||||||
|
Color color = colorObject[theme]!;
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
CupertinoIcons.ellipses_bubble,
|
Icons.subtitles_outlined,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
@ -12,12 +11,16 @@ class StatView extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color color =
|
Map<String, Color> colorObject = {
|
||||||
theme == 'white' ? Colors.white : Theme.of(context).colorScheme.outline;
|
'white': Colors.white,
|
||||||
|
'gray': Theme.of(context).colorScheme.outline,
|
||||||
|
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
|
||||||
|
};
|
||||||
|
Color color = colorObject[theme]!;
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
CupertinoIcons.play_rectangle,
|
Icons.play_circle_outlined,
|
||||||
size: 13,
|
size: 13,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/badge.dart';
|
import 'package:pilipala/common/widgets/badge.dart';
|
||||||
|
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
import 'package:pilipala/http/search.dart';
|
import 'package:pilipala/http/search.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
@ -11,11 +12,11 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
// 视频卡片 - 水平布局
|
// 视频卡片 - 水平布局
|
||||||
class VideoCardH extends StatelessWidget {
|
class VideoCardH extends StatelessWidget {
|
||||||
// ignore: prefer_typing_uninitialized_variables
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
var videoItem;
|
final videoItem;
|
||||||
Function()? longPress;
|
final Function()? longPress;
|
||||||
Function()? longPressEnd;
|
final Function()? longPressEnd;
|
||||||
|
|
||||||
VideoCardH({
|
const VideoCardH({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.videoItem,
|
required this.videoItem,
|
||||||
this.longPress,
|
this.longPress,
|
||||||
@ -53,11 +54,11 @@ class VideoCardH extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
StyleString.cardSpace, 7, StyleString.cardSpace, 7),
|
StyleString.safeSpace, 6, StyleString.safeSpace, 6),
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double width =
|
double width =
|
||||||
(boxConstraints.maxWidth - StyleString.cardSpace * 6) / 2;
|
(boxConstraints.maxWidth - StyleString.cardSpace * 9) / 2;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: width / StyleString.aspectRatio,
|
height: width / StyleString.aspectRatio,
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -70,21 +71,16 @@ class VideoCardH extends StatelessWidget {
|
|||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
double PR =
|
|
||||||
MediaQuery.of(context).devicePixelRatio;
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
// src: videoItem['pic'] +
|
|
||||||
// '@${(maxWidth * 2).toInt()}w',
|
|
||||||
src: videoItem.pic + '@.webp',
|
src: videoItem.pic + '@.webp',
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Image.network( videoItem['pic'], width: double.infinity, height: double.infinity,),
|
|
||||||
pBadge(Utils.timeFormat(videoItem.duration!),
|
pBadge(Utils.timeFormat(videoItem.duration!),
|
||||||
context, null, 6.0, 6.0, null,
|
context, null, 6.0, 6.0, null,
|
||||||
type: 'gray'),
|
type: 'gray'),
|
||||||
@ -104,12 +100,12 @@ class VideoCardH extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Divider(
|
// Divider(
|
||||||
height: 1,
|
// height: 1,
|
||||||
indent: 8,
|
// indent: 8,
|
||||||
endIndent: 12,
|
// endIndent: 12,
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
// color: Theme.of(context).dividerColor.withOpacity(0.08),
|
||||||
)
|
// )
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -118,6 +114,7 @@ class VideoCardH extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoContent extends StatelessWidget {
|
class VideoContent extends StatelessWidget {
|
||||||
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final videoItem;
|
final videoItem;
|
||||||
const VideoContent({super.key, required this.videoItem});
|
const VideoContent({super.key, required this.videoItem});
|
||||||
|
|
||||||
@ -133,9 +130,11 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
videoItem.title,
|
videoItem.title,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w500),
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@ -150,6 +149,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
color: i['type'] == 'em'
|
color: i['type'] == 'em'
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(context).colorScheme.onSurface,
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
@ -183,12 +183,13 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
videoItem.owner.name,
|
videoItem.owner.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 3),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
@ -196,12 +197,16 @@ class VideoContent extends StatelessWidget {
|
|||||||
view: videoItem.stat.view,
|
view: videoItem.stat.view,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
StatDanMu(
|
||||||
Utils.dateFormat(videoItem.pubdate!),
|
theme: 'gray',
|
||||||
style: TextStyle(
|
danmu: videoItem.stat.danmaku,
|
||||||
fontSize: 11,
|
),
|
||||||
color: Theme.of(context).colorScheme.outline),
|
// Text(
|
||||||
)
|
// Utils.dateFormat(videoItem.pubdate!),
|
||||||
|
// style: TextStyle(
|
||||||
|
// fontSize: 11,
|
||||||
|
// color: Theme.of(context).colorScheme.outline),
|
||||||
|
// )
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -6,16 +6,16 @@ import 'package:pilipala/common/widgets/stat/view.dart';
|
|||||||
import 'package:pilipala/pages/rcmd/index.dart';
|
import 'package:pilipala/pages/rcmd/index.dart';
|
||||||
import 'package:pilipala/utils/id_utils.dart';
|
import 'package:pilipala/utils/id_utils.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
import 'package:pilipala/pages/home/controller.dart';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class VideoCardV extends StatelessWidget {
|
class VideoCardV extends StatelessWidget {
|
||||||
var videoItem;
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
Function()? longPress;
|
final videoItem;
|
||||||
Function()? longPressEnd;
|
final Function()? longPress;
|
||||||
|
final Function()? longPressEnd;
|
||||||
|
|
||||||
VideoCardV({
|
const VideoCardV({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.videoItem,
|
required this.videoItem,
|
||||||
this.longPress,
|
this.longPress,
|
||||||
@ -26,7 +26,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: 0.8,
|
elevation: 0,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
@ -56,41 +56,40 @@ class VideoCardV extends StatelessWidget {
|
|||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: StyleString.imgRadius,
|
topLeft: StyleString.imgRadius,
|
||||||
topRight: 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) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
// 指定图片尺寸
|
|
||||||
// src: videoItem.pic + '@${(maxWidth * 2).toInt()}w',
|
|
||||||
src: videoItem.pic + '@.webp',
|
src: videoItem.pic + '@.webp',
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (videoItem.stat.view is int &&
|
// if (videoItem.stat.view is int &&
|
||||||
videoItem.stat.danmaku is int)
|
// videoItem.stat.danmaku is int)
|
||||||
Positioned(
|
// Positioned(
|
||||||
left: 0,
|
// left: 0,
|
||||||
right: 0,
|
// right: 0,
|
||||||
bottom: 0,
|
// bottom: 0,
|
||||||
child: AnimatedOpacity(
|
// child: AnimatedOpacity(
|
||||||
opacity: 1,
|
// opacity: 1,
|
||||||
duration: const Duration(milliseconds: 200),
|
// duration: const Duration(milliseconds: 200),
|
||||||
child: VideoStat(
|
// child: VideoStat(
|
||||||
view: videoItem.stat.view,
|
// view: videoItem.stat.view,
|
||||||
danmaku: videoItem.stat.danmaku,
|
// danmaku: videoItem.stat.danmaku,
|
||||||
duration: videoItem.duration,
|
// duration: videoItem.duration,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -106,6 +105,7 @@ class VideoCardV extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoContent extends StatelessWidget {
|
class VideoContent extends StatelessWidget {
|
||||||
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
final videoItem;
|
final videoItem;
|
||||||
const VideoContent({Key? key, required this.videoItem}) : super(key: key);
|
const VideoContent({Key? key, required this.videoItem}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
@ -113,7 +113,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
// 多列
|
// 多列
|
||||||
padding: const EdgeInsets.fromLTRB(8, 8, 6, 7),
|
padding: const EdgeInsets.fromLTRB(4, 5, 6, 6),
|
||||||
// 单列
|
// 单列
|
||||||
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
|
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -124,78 +124,76 @@ class VideoContent extends StatelessWidget {
|
|||||||
videoItem.title,
|
videoItem.title,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
// fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
),
|
),
|
||||||
maxLines: Get.find<RcmdController>().crossAxisCount,
|
maxLines: Get.find<RcmdController>().crossAxisCount,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
SizedBox(
|
|
||||||
height: 18,
|
Row(
|
||||||
child: Row(
|
children: [
|
||||||
children: [
|
if (videoItem.rcmdReason != null &&
|
||||||
if (videoItem.rcmdReason != null &&
|
videoItem.rcmdReason.content != '' ||
|
||||||
videoItem.rcmdReason.content != '') ...[
|
videoItem.isFollowed == 1) ...[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.fromLTRB(3, 1, 3, 1),
|
padding: const EdgeInsets.fromLTRB(3, 0, 3, 0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.primaryContainer
|
.primaryContainer
|
||||||
.withOpacity(0.6),
|
.withOpacity(0.6),
|
||||||
borderRadius: BorderRadius.circular(3)),
|
borderRadius: BorderRadius.circular(3)),
|
||||||
child: Text(
|
child: Center(
|
||||||
videoItem.rcmdReason.content,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize:
|
|
||||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4)
|
|
||||||
] else if (videoItem.isFollowed == 1) ...[
|
|
||||||
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(
|
|
||||||
'已关注',
|
|
||||||
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(
|
child: Text(
|
||||||
videoItem.owner.name,
|
videoItem.rcmdReason != null &&
|
||||||
maxLines: 1,
|
videoItem.rcmdReason.content != ''
|
||||||
|
? videoItem.rcmdReason.content
|
||||||
|
: '已关注',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context)
|
fontSize: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.labelMedium!
|
.labelSmall!
|
||||||
.fontSize,
|
.fontSize,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
// Row(
|
||||||
|
// children: [
|
||||||
|
// StatView(
|
||||||
|
// theme: 'black',
|
||||||
|
// view: videoItem.stat.view,
|
||||||
|
// ),
|
||||||
|
// const SizedBox(width: 6),
|
||||||
|
// StatDanMu(
|
||||||
|
// theme: 'black',
|
||||||
|
// danmu: videoItem.stat.danmaku,
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -219,7 +217,7 @@ class VideoStat extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 45,
|
height: 45,
|
||||||
padding: const EdgeInsets.only(top: 22, left: 8, right: 8),
|
padding: const EdgeInsets.only(top: 22, left: 6, right: 6),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
@ -240,7 +238,7 @@ class VideoStat extends StatelessWidget {
|
|||||||
theme: 'white',
|
theme: 'white',
|
||||||
view: view,
|
view: view,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 6),
|
||||||
StatDanMu(
|
StatDanMu(
|
||||||
theme: 'white',
|
theme: 'white',
|
||||||
danmu: danmaku,
|
danmu: danmaku,
|
||||||
|
|||||||
@ -151,7 +151,7 @@ class Request {
|
|||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioError catch (e) {
|
} on DioException catch (e) {
|
||||||
print('get error: $e');
|
print('get error: $e');
|
||||||
return Future.error(await ApiInterceptor.dioError(e));
|
return Future.error(await ApiInterceptor.dioError(e));
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ class Request {
|
|||||||
);
|
);
|
||||||
print('post success: ${response.data}');
|
print('post success: ${response.data}');
|
||||||
return response;
|
return response;
|
||||||
} on DioError catch (e) {
|
} on DioException catch (e) {
|
||||||
print('post error: $e');
|
print('post error: $e');
|
||||||
return Future.error(await ApiInterceptor.dioError(e));
|
return Future.error(await ApiInterceptor.dioError(e));
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ class Request {
|
|||||||
print('downloadFile success: ${response.data}');
|
print('downloadFile success: ${response.data}');
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
} on DioError catch (e) {
|
} on DioException catch (e) {
|
||||||
print('downloadFile error: $e');
|
print('downloadFile error: $e');
|
||||||
return Future.error(ApiInterceptor.dioError(e));
|
return Future.error(ApiInterceptor.dioError(e));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart' hide Response;
|
// import 'package:get/get.dart' hide Response;
|
||||||
|
|
||||||
class ApiInterceptor extends Interceptor {
|
class ApiInterceptor extends Interceptor {
|
||||||
@override
|
@override
|
||||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||||
print("请求之前");
|
// print("请求之前");
|
||||||
// 在请求之前添加头部或认证信息
|
// 在请求之前添加头部或认证信息
|
||||||
// options.headers['Authorization'] = 'Bearer token';
|
// options.headers['Authorization'] = 'Bearer token';
|
||||||
// options.headers['Content-Type'] = 'application/json';
|
// options.headers['Content-Type'] = 'application/json';
|
||||||
@ -19,30 +19,30 @@ class ApiInterceptor extends Interceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onError(DioError err, ErrorInterceptorHandler handler) async {
|
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||||
// 处理网络请求错误
|
// 处理网络请求错误
|
||||||
// handler.next(err);
|
// handler.next(err);
|
||||||
SmartDialog.showToast(await dioError(err));
|
SmartDialog.showToast(await dioError(err));
|
||||||
super.onError(err, handler);
|
super.onError(err, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future dioError(DioError error) async {
|
static Future dioError(DioException error) async {
|
||||||
switch (error.type) {
|
switch (error.type) {
|
||||||
case DioErrorType.badCertificate:
|
case DioExceptionType.badCertificate:
|
||||||
return '证书有误!';
|
return '证书有误!';
|
||||||
case DioErrorType.badResponse:
|
case DioExceptionType.badResponse:
|
||||||
return '服务器异常,请稍后重试!';
|
return '服务器异常,请稍后重试!';
|
||||||
case DioErrorType.cancel:
|
case DioExceptionType.cancel:
|
||||||
return "请求已被取消,请重新请求";
|
return "请求已被取消,请重新请求";
|
||||||
case DioErrorType.connectionError:
|
case DioExceptionType.connectionError:
|
||||||
return '连接错误,请检查网络设置';
|
return '连接错误,请检查网络设置';
|
||||||
case DioErrorType.connectionTimeout:
|
case DioExceptionType.connectionTimeout:
|
||||||
return "网络连接超时,请检查网络设置";
|
return "网络连接超时,请检查网络设置";
|
||||||
case DioErrorType.receiveTimeout:
|
case DioExceptionType.receiveTimeout:
|
||||||
return "响应超时,请稍后重试!";
|
return "响应超时,请稍后重试!";
|
||||||
case DioErrorType.sendTimeout:
|
case DioExceptionType.sendTimeout:
|
||||||
return "发送请求超时,请检查网络设置";
|
return "发送请求超时,请检查网络设置";
|
||||||
case DioErrorType.unknown:
|
case DioExceptionType.unknown:
|
||||||
var res = await checkConect();
|
var res = await checkConect();
|
||||||
return res + " \n 网络异常,请稍后重试!";
|
return res + " \n 网络异常,请稍后重试!";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:pilipala/http/index.dart';
|
import 'package:pilipala/http/index.dart';
|
||||||
import 'package:pilipala/models/bangumi/info.dart';
|
import 'package:pilipala/models/bangumi/info.dart';
|
||||||
import 'package:pilipala/models/common/search_type.dart';
|
import 'package:pilipala/models/common/search_type.dart';
|
||||||
@ -57,7 +55,7 @@ class SearchHttp {
|
|||||||
'page': page
|
'page': page
|
||||||
});
|
});
|
||||||
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
|
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
|
||||||
var data;
|
Object data;
|
||||||
switch (searchType) {
|
switch (searchType) {
|
||||||
case SearchType.video:
|
case SearchType.video:
|
||||||
data = SearchVideoModel.fromJson(res.data['data']);
|
data = SearchVideoModel.fromJson(res.data['data']);
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:pilipala/http/api.dart';
|
import 'package:pilipala/http/api.dart';
|
||||||
import 'package:pilipala/http/init.dart';
|
import 'package:pilipala/http/init.dart';
|
||||||
import 'package:pilipala/models/model_hot_video_item.dart';
|
import 'package:pilipala/models/model_hot_video_item.dart';
|
||||||
|
|||||||
@ -125,7 +125,6 @@ class VideoHttp {
|
|||||||
return {'status': false, 'data': []};
|
return {'status': false, 'data': []};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print('🐯:$err');
|
|
||||||
return {'status': false, 'data': [], 'msg': err};
|
return {'status': false, 'data': [], 'msg': err};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,7 +199,7 @@ class VideoHttp {
|
|||||||
if (res.data['code'] == 0) {
|
if (res.data['code'] == 0) {
|
||||||
return {'status': true, 'data': res.data['data']};
|
return {'status': true, 'data': res.data['data']};
|
||||||
} else {
|
} else {
|
||||||
return {'status': true, 'data': [], 'msg': ''};
|
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +337,7 @@ class VideoHttp {
|
|||||||
|
|
||||||
// 视频播放进度
|
// 视频播放进度
|
||||||
static Future heartBeat({bvid, cid, progress, realtime}) async {
|
static Future heartBeat({bvid, cid, progress, realtime}) async {
|
||||||
var res = await Request().post(Api.heartBeat, queryParameters: {
|
await Request().post(Api.heartBeat, queryParameters: {
|
||||||
// 'aid': aid,
|
// 'aid': aid,
|
||||||
'bvid': bvid,
|
'bvid': bvid,
|
||||||
'cid': cid,
|
'cid': cid,
|
||||||
|
|||||||
@ -34,6 +34,7 @@ class MyApp extends StatelessWidget {
|
|||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
title: 'PiLiPaLa',
|
title: 'PiLiPaLa',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
|
fontFamily: 'HarmonyOS',
|
||||||
colorScheme: lightDynamic ??
|
colorScheme: lightDynamic ??
|
||||||
ColorScheme.fromSeed(
|
ColorScheme.fromSeed(
|
||||||
seedColor: Colors.green,
|
seedColor: Colors.green,
|
||||||
@ -42,6 +43,7 @@ class MyApp extends StatelessWidget {
|
|||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
darkTheme: ThemeData(
|
darkTheme: ThemeData(
|
||||||
|
fontFamily: 'HarmonyOS',
|
||||||
colorScheme: darkDynamic ??
|
colorScheme: darkDynamic ??
|
||||||
ColorScheme.fromSeed(
|
ColorScheme.fromSeed(
|
||||||
seedColor: Colors.green,
|
seedColor: Colors.green,
|
||||||
|
|||||||
@ -193,7 +193,7 @@ class SearchUserItemModel {
|
|||||||
usign = json['usign'];
|
usign = json['usign'];
|
||||||
fans = json['fans'];
|
fans = json['fans'];
|
||||||
videos = json['videos'];
|
videos = json['videos'];
|
||||||
upic = 'https:' + json['upic'];
|
upic = 'https:${json['upic']}';
|
||||||
faceNft = json['face_nft'];
|
faceNft = json['face_nft'];
|
||||||
faceNftType = json['face_nft_type'];
|
faceNftType = json['face_nft_type'];
|
||||||
verifyInfo = json['verify_info'];
|
verifyInfo = json['verify_info'];
|
||||||
|
|||||||
@ -31,7 +31,7 @@ class SearchSuggestItem {
|
|||||||
SearchSuggestItem.fromJson(Map<String, dynamic> json, String inputTerm) {
|
SearchSuggestItem.fromJson(Map<String, dynamic> json, String inputTerm) {
|
||||||
value = json['value'];
|
value = json['value'];
|
||||||
term = json['term'];
|
term = json['term'];
|
||||||
String reg = '<em class=\"suggest_high_light\">$inputTerm</em>';
|
String reg = '<em class="suggest_high_light">$inputTerm</em>';
|
||||||
try {
|
try {
|
||||||
if (json['name'].indexOf(inputTerm) != -1) {
|
if (json['name'].indexOf(inputTerm) != -1) {
|
||||||
String str = json['name'].replaceAll(reg, '^');
|
String str = json['name'].replaceAll(reg, '^');
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
class ReplyMember {
|
class ReplyMember {
|
||||||
ReplyMember({
|
ReplyMember({
|
||||||
this.mid,
|
this.mid,
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -7,7 +9,6 @@ import 'package:pilipala/models/common/dynamics_type.dart';
|
|||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
import 'package:pilipala/models/dynamics/up.dart';
|
import 'package:pilipala/models/dynamics/up.dart';
|
||||||
import 'package:pilipala/models/live/item.dart';
|
import 'package:pilipala/models/live/item.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
|
||||||
|
|
||||||
class DynamicsController extends GetxController {
|
class DynamicsController extends GetxController {
|
||||||
int page = 1;
|
int page = 1;
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/reply.dart';
|
import 'package:pilipala/http/reply.dart';
|
||||||
import 'package:pilipala/models/common/reply_sort_type.dart';
|
import 'package:pilipala/models/common/reply_sort_type.dart';
|
||||||
import 'package:pilipala/models/video/reply/data.dart';
|
|
||||||
import 'package:pilipala/models/video/reply/item.dart';
|
import 'package:pilipala/models/video/reply/item.dart';
|
||||||
|
|
||||||
class DynamicDetailController extends GetxController {
|
class DynamicDetailController extends GetxController {
|
||||||
DynamicDetailController(this.oid, this.type);
|
DynamicDetailController(this.oid, this.type);
|
||||||
int? oid;
|
int? oid;
|
||||||
int? type;
|
int? type;
|
||||||
var item;
|
dynamic item;
|
||||||
int? floor;
|
int? floor;
|
||||||
int currentPage = 0;
|
int currentPage = 0;
|
||||||
bool isLoadingMore = false;
|
bool isLoadingMore = false;
|
||||||
|
|||||||
@ -77,8 +77,12 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
|
|||||||
Get.to(
|
Get.to(
|
||||||
() => Scaffold(
|
() => Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('评论详情'),
|
titleSpacing: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
|
title: Text(
|
||||||
|
'评论详情',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: VideoReplyReplyPanel(
|
body: VideoReplyReplyPanel(
|
||||||
oid: oid,
|
oid: oid,
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';
|
import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
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/common/skeleton/dynamic_card.dart';
|
import 'package:pilipala/common/skeleton/dynamic_card.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/models/common/dynamics_type.dart';
|
|
||||||
import 'package:pilipala/models/dynamics/result.dart';
|
import 'package:pilipala/models/dynamics/result.dart';
|
||||||
import 'package:pilipala/pages/mine/index.dart';
|
import 'package:pilipala/pages/mine/index.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
@ -55,6 +53,7 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
|||||||
@ -8,11 +8,12 @@ import 'package:pilipala/models/dynamics/result.dart';
|
|||||||
import 'package:pilipala/pages/dynamics/index.dart';
|
import 'package:pilipala/pages/dynamics/index.dart';
|
||||||
|
|
||||||
class ActionPanel extends StatefulWidget {
|
class ActionPanel extends StatefulWidget {
|
||||||
ActionPanel({
|
const ActionPanel({
|
||||||
super.key,
|
super.key,
|
||||||
this.item,
|
this.item,
|
||||||
});
|
});
|
||||||
var item;
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
|
final item;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ActionPanel> createState() => _ActionPanelState();
|
State<ActionPanel> createState() => _ActionPanelState();
|
||||||
|
|||||||
@ -153,12 +153,12 @@ Widget addWidget(item, context, type, {floor = 1}) {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
case 'ADDITIONAL_TYPE_MATCH':
|
case 'ADDITIONAL_TYPE_MATCH':
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
case 'ADDITIONAL_TYPE_COMMON':
|
case 'ADDITIONAL_TYPE_COMMON':
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
case 'ADDITIONAL_TYPE_VOTE':
|
case 'ADDITIONAL_TYPE_VOTE':
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
default:
|
default:
|
||||||
return Text('11');
|
return const Text('11');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import 'content_panel.dart';
|
|||||||
import 'forward_panel.dart';
|
import 'forward_panel.dart';
|
||||||
|
|
||||||
class DynamicPanel extends StatelessWidget {
|
class DynamicPanel extends StatelessWidget {
|
||||||
var item;
|
final dynamic item;
|
||||||
String? source;
|
final String? source;
|
||||||
DynamicPanel({this.item, this.source, Key? key}) : super(key: key);
|
DynamicPanel({this.item, this.source, Key? key}) : super(key: key);
|
||||||
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
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/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
|
|||||||
@ -90,7 +90,7 @@ InlineSpan richNode(item, context) {
|
|||||||
'/webview',
|
'/webview',
|
||||||
parameters: {
|
parameters: {
|
||||||
'url':
|
'url':
|
||||||
'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=${dynamicId}&isWeb=1',
|
'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=$dynamicId&isWeb=1',
|
||||||
'type': 'vote',
|
'type': 'vote',
|
||||||
'pageTitle': '投票'
|
'pageTitle': '投票'
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
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/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
@ -10,8 +9,8 @@ import 'package:pilipala/utils/storage.dart';
|
|||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class UpPanel extends StatefulWidget {
|
class UpPanel extends StatefulWidget {
|
||||||
FollowUpModel? upData;
|
final FollowUpModel? upData;
|
||||||
UpPanel(this.upData, {Key? key}) : super(key: key);
|
const UpPanel(this.upData, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<UpPanel> createState() => _UpPanelState();
|
State<UpPanel> createState() => _UpPanelState();
|
||||||
@ -168,7 +167,9 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
smallSize: 8,
|
smallSize: 8,
|
||||||
label: data.type == 'live' ? const Text('Live') : null,
|
label: data.type == 'live' ? const Text('Live') : null,
|
||||||
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
alignment: AlignmentDirectional.bottomCenter,
|
alignment: data.type == 'live'
|
||||||
|
? AlignmentDirectional.topCenter
|
||||||
|
: AlignmentDirectional.topEnd,
|
||||||
padding: const EdgeInsets.only(left: 6, right: 6),
|
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||||
isLabelVisible: data.type == 'live' ||
|
isLabelVisible: data.type == 'live' ||
|
||||||
(data.type == 'up' && (data.hasUpdate ?? false)),
|
(data.type == 'up' && (data.hasUpdate ?? false)),
|
||||||
|
|||||||
@ -44,7 +44,11 @@ class _FansPageState extends State<FansPage> {
|
|||||||
elevation: 0,
|
elevation: 0,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: const Text('我的粉丝'),
|
titleSpacing: 0,
|
||||||
|
title: Text(
|
||||||
|
'我的粉丝',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
onRefresh: () async => await _fansController.queryFans('init'),
|
onRefresh: () async => await _fansController.queryFans('init'),
|
||||||
@ -57,7 +61,7 @@ class _FansPageState extends State<FansPage> {
|
|||||||
List<FansItemModel> list = _fansController.fansList;
|
List<FansItemModel> list = _fansController.fansList;
|
||||||
return Obx(
|
return Obx(
|
||||||
() => list.length == 1
|
() => list.length == 1
|
||||||
? SizedBox()
|
? const SizedBox()
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
@ -74,7 +78,7 @@ class _FansPageState extends State<FansPage> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class FavItem extends StatelessWidget {
|
class FavItem extends StatelessWidget {
|
||||||
var favFolderItem;
|
// ignore: prefer_typing_uninitialized_variables
|
||||||
FavItem({super.key, required this.favFolderItem});
|
final favFolderItem;
|
||||||
|
const FavItem({super.key, required this.favFolderItem});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -38,7 +39,6 @@ class FavItem extends StatelessWidget {
|
|||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
|
||||||
return Hero(
|
return Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
@ -62,7 +62,7 @@ class FavItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoContent extends StatelessWidget {
|
class VideoContent extends StatelessWidget {
|
||||||
final favFolderItem;
|
final dynamic favFolderItem;
|
||||||
const VideoContent({super.key, required this.favFolderItem});
|
const VideoContent({super.key, required this.favFolderItem});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -76,9 +76,11 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
favFolderItem.title,
|
favFolderItem.title,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w500),
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'${favFolderItem.mediaCount}个内容',
|
'${favFolderItem.mediaCount}个内容',
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import '../controller.dart';
|
|||||||
|
|
||||||
// 收藏视频卡片 - 水平布局
|
// 收藏视频卡片 - 水平布局
|
||||||
class FavVideoCardH extends StatelessWidget {
|
class FavVideoCardH extends StatelessWidget {
|
||||||
var videoItem;
|
final dynamic videoItem;
|
||||||
final FavDetailController _favDetailController =
|
final FavDetailController _favDetailController =
|
||||||
Get.put(FavDetailController());
|
Get.put(FavDetailController());
|
||||||
|
|
||||||
@ -28,9 +28,9 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.errorContainer,
|
color: Theme.of(context).colorScheme.errorContainer,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: const Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: const [
|
children: [
|
||||||
Icon(Icons.clear_all_rounded),
|
Icon(Icons.clear_all_rounded),
|
||||||
SizedBox(width: 6),
|
SizedBox(width: 6),
|
||||||
Text('取消收藏')
|
Text('取消收藏')
|
||||||
@ -51,7 +51,8 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12, 5, 12, 5),
|
padding: const EdgeInsets.fromLTRB(
|
||||||
|
StyleString.safeSpace, 5, StyleString.safeSpace, 5),
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double width =
|
double width =
|
||||||
@ -68,21 +69,16 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
double PR =
|
|
||||||
MediaQuery.of(context).devicePixelRatio;
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
// src: videoItem['pic'] +
|
|
||||||
// '@${(maxWidth * 2).toInt()}w',
|
|
||||||
src: videoItem.pic + '@.webp',
|
src: videoItem.pic + '@.webp',
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Image.network( videoItem['pic'], width: double.infinity, height: double.infinity,),
|
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 4,
|
right: 4,
|
||||||
bottom: 4,
|
bottom: 4,
|
||||||
@ -121,7 +117,7 @@ class FavVideoCardH extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoContent extends StatelessWidget {
|
class VideoContent extends StatelessWidget {
|
||||||
final videoItem;
|
final dynamic videoItem;
|
||||||
const VideoContent({super.key, required this.videoItem});
|
const VideoContent({super.key, required this.videoItem});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -135,9 +131,11 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
videoItem.title,
|
videoItem.title,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w500),
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@ -145,10 +143,11 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
videoItem.owner.name,
|
videoItem.owner.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
StatView(
|
StatView(
|
||||||
|
|||||||
@ -43,8 +43,12 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
|
titleSpacing: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: const Text('我的关注'),
|
title: Text(
|
||||||
|
'我的关注',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
onRefresh: () async =>
|
onRefresh: () async =>
|
||||||
@ -58,7 +62,7 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
List<FollowItemModel> list = _followController.followList;
|
List<FollowItemModel> list = _followController.followList;
|
||||||
return Obx(
|
return Obx(
|
||||||
() => list.length == 1
|
() => list.length == 1
|
||||||
? SizedBox()
|
? const SizedBox()
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
itemCount: list.length,
|
itemCount: list.length,
|
||||||
@ -75,7 +79,7 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
|
|||||||
@ -11,13 +11,16 @@ Widget followItem({item}) {
|
|||||||
leading: Hero(
|
leading: Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
width: 38,
|
width: 45,
|
||||||
height: 38,
|
height: 45,
|
||||||
type: 'avatar',
|
type: 'avatar',
|
||||||
src: item.face,
|
src: item.face,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Text(item.uname),
|
title: Text(
|
||||||
|
item.uname,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
item.sign,
|
item.sign,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import 'package:pilipala/utils/id_utils.dart';
|
|||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
class HistoryItem extends StatelessWidget {
|
class HistoryItem extends StatelessWidget {
|
||||||
var videoItem;
|
final dynamic videoItem;
|
||||||
HistoryItem({super.key, required this.videoItem});
|
const HistoryItem({super.key, required this.videoItem});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -59,7 +59,7 @@ class HistoryItem extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
StyleString.cardSpace, 7, StyleString.cardSpace, 7),
|
StyleString.cardSpace, 5, StyleString.cardSpace, 5),
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double width =
|
double width =
|
||||||
@ -76,14 +76,11 @@ class HistoryItem extends StatelessWidget {
|
|||||||
builder: (context, boxConstraints) {
|
builder: (context, boxConstraints) {
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: heroTag,
|
tag: heroTag,
|
||||||
child: NetworkImgLayer(
|
child: NetworkImgLayer(
|
||||||
// src: videoItem['pic'] +
|
|
||||||
// '@${(maxWidth * 2).toInt()}w',
|
|
||||||
src: (videoItem.cover != ''
|
src: (videoItem.cover != ''
|
||||||
? videoItem.cover
|
? videoItem.cover
|
||||||
: videoItem.covers.first) +
|
: videoItem.covers.first) +
|
||||||
@ -124,12 +121,6 @@ class HistoryItem extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Divider(
|
|
||||||
height: 1,
|
|
||||||
indent: 8,
|
|
||||||
endIndent: 12,
|
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -137,7 +128,7 @@ class HistoryItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoContent extends StatelessWidget {
|
class VideoContent extends StatelessWidget {
|
||||||
final videoItem;
|
final dynamic videoItem;
|
||||||
const VideoContent({super.key, required this.videoItem});
|
const VideoContent({super.key, required this.videoItem});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -151,9 +142,11 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
videoItem.title,
|
videoItem.title,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w500),
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
maxLines: videoItem.videos > 1 ? 1 : 2,
|
maxLines: videoItem.videos > 1 ? 1 : 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@ -162,7 +155,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
videoItem.showTitle,
|
videoItem.showTitle,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Theme.of(context).colorScheme.outline),
|
color: Theme.of(context).colorScheme.outline),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
@ -174,7 +167,7 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
videoItem.authorName,
|
videoItem.authorName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -185,7 +178,8 @@ class VideoContent extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
Utils.dateFormat(videoItem.viewAt!),
|
Utils.dateFormat(videoItem.viewAt!),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
color: Theme.of(context).colorScheme.outline),
|
color: Theme.of(context).colorScheme.outline),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@ -7,9 +7,30 @@ import 'package:pilipala/pages/rcmd/index.dart';
|
|||||||
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
List tabs = [
|
List tabs = [
|
||||||
{'label': '直播', 'type': 'live'},
|
{
|
||||||
{'label': '推荐', 'type': 'rcm'},
|
'icon': const Icon(
|
||||||
{'label': '热门', 'type': 'hot'},
|
Icons.live_tv_outlined,
|
||||||
|
size: 15,
|
||||||
|
),
|
||||||
|
'label': '直播',
|
||||||
|
'type': 'live'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(
|
||||||
|
Icons.thumb_up_off_alt_outlined,
|
||||||
|
size: 15,
|
||||||
|
),
|
||||||
|
'label': '推荐',
|
||||||
|
'type': 'rcm'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'icon': const Icon(
|
||||||
|
Icons.whatshot_outlined,
|
||||||
|
size: 15,
|
||||||
|
),
|
||||||
|
'label': '热门',
|
||||||
|
'type': 'hot'
|
||||||
|
},
|
||||||
];
|
];
|
||||||
int initialIndex = 1;
|
int initialIndex = 1;
|
||||||
late TabController tabController;
|
late TabController tabController;
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
title: Padding(
|
title: Padding(
|
||||||
padding: const EdgeInsets.only(left: 4, right: 4),
|
padding: const EdgeInsets.only(left: 12, right: 12, bottom: 0),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
const Align(
|
const Align(
|
||||||
@ -55,26 +55,45 @@ class _HomePageState extends State<HomePage>
|
|||||||
splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明
|
splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明
|
||||||
highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明
|
highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明
|
||||||
),
|
),
|
||||||
child: TabBar(
|
child: Padding(
|
||||||
controller: _homeController.tabController,
|
padding: const EdgeInsets.only(top: 4),
|
||||||
tabs: [
|
child: TabBar(
|
||||||
for (var i in _homeController.tabs) Tab(text: i['label']),
|
controller: _homeController.tabController,
|
||||||
],
|
tabs: [
|
||||||
isScrollable: true,
|
for (var i in _homeController.tabs)
|
||||||
indicatorWeight: 0,
|
// Tab(text: i['label'])
|
||||||
indicatorPadding:
|
Padding(
|
||||||
const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
indicator: BoxDecoration(
|
horizontal: 0, vertical: 11),
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
child: Row(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
children: [
|
||||||
|
i['icon'],
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(i['label'])
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
isScrollable: true,
|
||||||
|
indicatorWeight: 0,
|
||||||
|
indicatorPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 4, vertical: 5),
|
||||||
|
indicator: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primaryContainer
|
||||||
|
.withOpacity(0.8),
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
|
indicatorSize: TabBarIndicatorSize.tab,
|
||||||
|
labelColor: Theme.of(context).colorScheme.primary,
|
||||||
|
labelStyle: const TextStyle(fontSize: 13),
|
||||||
|
dividerColor: Colors.transparent,
|
||||||
|
unselectedLabelColor:
|
||||||
|
Theme.of(context).colorScheme.outline,
|
||||||
|
onTap: (value) => {_homeController.initialIndex = value},
|
||||||
),
|
),
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
|
||||||
labelColor:
|
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
|
||||||
labelStyle: const TextStyle(fontSize: 13),
|
|
||||||
dividerColor: Colors.transparent,
|
|
||||||
unselectedLabelColor: Theme.of(context).colorScheme.outline,
|
|
||||||
onTap: (value) => {_homeController.initialIndex = value},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|||||||
@ -86,7 +86,7 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
return SliverList(
|
return SliverList(
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
return const VideoCardHSkeleton();
|
return const VideoCardHSkeleton();
|
||||||
}, childCount: 5),
|
}, childCount: 10),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -26,8 +26,12 @@ class _LaterPageState extends State<LaterPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('稍后再看'),
|
titleSpacing: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
|
title: Text(
|
||||||
|
'稍后再看',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
controller: _laterController.scrollController,
|
controller: _laterController.scrollController,
|
||||||
|
|||||||
@ -19,9 +19,6 @@ class LivePage extends StatefulWidget {
|
|||||||
class _LivePageState extends State<LivePage> {
|
class _LivePageState extends State<LivePage> {
|
||||||
final LiveController _liveController = Get.put(LiveController());
|
final LiveController _liveController = Get.put(LiveController());
|
||||||
|
|
||||||
@override
|
|
||||||
bool get wantKeepAlive => true;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -50,7 +47,7 @@ class _LivePageState extends State<LivePage> {
|
|||||||
SliverPadding(
|
SliverPadding(
|
||||||
// 单列布局 EdgeInsets.zero
|
// 单列布局 EdgeInsets.zero
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
StyleString.cardSpace, 0, StyleString.cardSpace, 8),
|
StyleString.safeSpace, 0, StyleString.safeSpace, 0),
|
||||||
sliver: FutureBuilder(
|
sliver: FutureBuilder(
|
||||||
future: _liveController.queryLiveList('init'),
|
future: _liveController.queryLiveList('init'),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -97,13 +94,13 @@ class _LivePageState extends State<LivePage> {
|
|||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
// 行间距
|
// 行间距
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace + 2,
|
||||||
// 列间距
|
// 列间距
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.cardSpace + 3,
|
||||||
// 列数
|
// 列数
|
||||||
crossAxisCount: ctr.crossAxisCount,
|
crossAxisCount: ctr.crossAxisCount,
|
||||||
mainAxisExtent:
|
mainAxisExtent:
|
||||||
Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 70,
|
Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 60,
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@ -8,11 +8,11 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|||||||
|
|
||||||
// 视频卡片 - 垂直布局
|
// 视频卡片 - 垂直布局
|
||||||
class LiveCardV extends StatelessWidget {
|
class LiveCardV extends StatelessWidget {
|
||||||
LiveItemModel liveItem;
|
final LiveItemModel liveItem;
|
||||||
Function()? longPress;
|
final Function()? longPress;
|
||||||
Function()? longPressEnd;
|
final Function()? longPressEnd;
|
||||||
|
|
||||||
LiveCardV({
|
const LiveCardV({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.liveItem,
|
required this.liveItem,
|
||||||
this.longPress,
|
this.longPress,
|
||||||
@ -23,7 +23,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: 0.8,
|
elevation: 0,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
@ -52,6 +52,8 @@ class LiveCardV extends StatelessWidget {
|
|||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: StyleString.imgRadius,
|
topLeft: StyleString.imgRadius,
|
||||||
topRight: StyleString.imgRadius,
|
topRight: StyleString.imgRadius,
|
||||||
|
bottomLeft: StyleString.imgRadius,
|
||||||
|
bottomRight: StyleString.imgRadius,
|
||||||
),
|
),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
aspectRatio: StyleString.aspectRatio,
|
||||||
@ -68,6 +70,18 @@ class LiveCardV extends StatelessWidget {
|
|||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: 1,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: VideoStat(
|
||||||
|
liveItem: liveItem,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -83,36 +97,39 @@ class LiveCardV extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LiveContent extends StatelessWidget {
|
class LiveContent extends StatelessWidget {
|
||||||
final liveItem;
|
final dynamic liveItem;
|
||||||
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
|
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
// 多列
|
// 多列
|
||||||
padding: const EdgeInsets.fromLTRB(8, 7, 6, 4),
|
padding: const EdgeInsets.fromLTRB(4, 5, 6, 6),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
liveItem.title,
|
liveItem.title,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
UpTag(),
|
const UpTag(),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
liveItem.uname,
|
liveItem.uname,
|
||||||
textAlign: TextAlign.start,
|
textAlign: TextAlign.start,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
@ -120,23 +137,50 @@ class LiveContent extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'${'[' + liveItem.areaName}]',
|
|
||||||
style: const TextStyle(fontSize: 11),
|
|
||||||
),
|
|
||||||
const Text(' • '),
|
|
||||||
Text(
|
|
||||||
liveItem.watchedShow['text_large'],
|
|
||||||
style: const TextStyle(fontSize: 11),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VideoStat extends StatelessWidget {
|
||||||
|
final LiveItemModel? liveItem;
|
||||||
|
|
||||||
|
const VideoStat({
|
||||||
|
Key? key,
|
||||||
|
required this.liveItem,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 45,
|
||||||
|
padding: const EdgeInsets.only(top: 22, left: 10, right: 10),
|
||||||
|
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: [
|
||||||
|
Text(
|
||||||
|
liveItem!.areaName!,
|
||||||
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
liveItem!.watchedShow!['text_small'],
|
||||||
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import 'package:pilipala/models/live/room_info.dart';
|
|||||||
class LiveRoomController extends GetxController {
|
class LiveRoomController extends GetxController {
|
||||||
String cover = '';
|
String cover = '';
|
||||||
late int roomId;
|
late int roomId;
|
||||||
var liveItem;
|
dynamic liveItem;
|
||||||
late String heroTag;
|
late String heroTag;
|
||||||
double volume = 0.0;
|
double volume = 0.0;
|
||||||
// 静音状态
|
// 静音状态
|
||||||
@ -75,6 +75,5 @@ class LiveRoomController extends GetxController {
|
|||||||
volumeOff.value = true;
|
volumeOff.value = true;
|
||||||
meeduPlayerController.setVolume(0);
|
meeduPlayerController.setVolume(0);
|
||||||
}
|
}
|
||||||
print('🌹:${volumeOff.value}');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'dart:ui';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
@ -18,7 +15,6 @@ class LiveRoomPage extends StatefulWidget {
|
|||||||
class _LiveRoomPageState extends State<LiveRoomPage> {
|
class _LiveRoomPageState extends State<LiveRoomPage> {
|
||||||
final LiveRoomController _liveRoomController = Get.put(LiveRoomController());
|
final LiveRoomController _liveRoomController = Get.put(LiveRoomController());
|
||||||
MeeduPlayerController? _meeduPlayerController;
|
MeeduPlayerController? _meeduPlayerController;
|
||||||
StreamSubscription? _playerEventSubs;
|
|
||||||
|
|
||||||
bool isShowCover = true;
|
bool isShowCover = true;
|
||||||
bool isPlay = true;
|
bool isPlay = true;
|
||||||
@ -27,7 +23,7 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_meeduPlayerController = _liveRoomController.meeduPlayerController;
|
_meeduPlayerController = _liveRoomController.meeduPlayerController;
|
||||||
_playerEventSubs = _meeduPlayerController!.onPlayerStatusChanged.listen(
|
_meeduPlayerController!.onPlayerStatusChanged.listen(
|
||||||
(PlayerStatus status) {
|
(PlayerStatus status) {
|
||||||
if (status == PlayerStatus.playing) {
|
if (status == PlayerStatus.playing) {
|
||||||
isShowCover = false;
|
isShowCover = false;
|
||||||
@ -45,7 +41,6 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
|
||||||
final videoHeight = MediaQuery.of(context).size.width * 9 / 16;
|
final videoHeight = MediaQuery.of(context).size.width * 9 / 16;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -97,8 +92,8 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
aspectRatio: 16 / 9,
|
aspectRatio: 16 / 9,
|
||||||
child: MeeduVideoPlayer(
|
child: MeeduVideoPlayer(
|
||||||
header: (BuildContext context,
|
header: (BuildContext context,
|
||||||
MeeduPlayerController _meeduPlayerController,
|
MeeduPlayerController meeduPlayerController,
|
||||||
Responsive) {
|
Responsive responsive) {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
primary: false,
|
primary: false,
|
||||||
@ -115,7 +110,7 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
|
|||||||
height: 38,
|
height: 38,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
_meeduPlayerController.enterPip(context),
|
meeduPlayerController.enterPip(context),
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.branding_watermark_outlined,
|
Icons.branding_watermark_outlined,
|
||||||
size: 19,
|
size: 19,
|
||||||
|
|||||||
@ -1,19 +1,12 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:pilipala/pages/dynamics/index.dart';
|
import 'package:pilipala/pages/dynamics/index.dart';
|
||||||
import 'package:pilipala/pages/home/view.dart';
|
import 'package:pilipala/pages/home/view.dart';
|
||||||
import 'package:pilipala/pages/hot/view.dart';
|
|
||||||
import 'package:pilipala/pages/media/index.dart';
|
import 'package:pilipala/pages/media/index.dart';
|
||||||
import 'package:pilipala/pages/mine/index.dart';
|
|
||||||
import 'package:pilipala/utils/storage.dart';
|
|
||||||
|
|
||||||
class MainController extends GetxController {
|
class MainController extends GetxController {
|
||||||
List<Widget> pages = <Widget>[
|
List<Widget> pages = <Widget>[
|
||||||
const HomePage(),
|
const HomePage(),
|
||||||
// const HotPage(),
|
|
||||||
const DynamicsPage(),
|
const DynamicsPage(),
|
||||||
const MediaPage(),
|
const MediaPage(),
|
||||||
];
|
];
|
||||||
@ -25,13 +18,6 @@ class MainController extends GetxController {
|
|||||||
),
|
),
|
||||||
'label': "推荐",
|
'label': "推荐",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// 'icon': const Icon(
|
|
||||||
// Icons.eco,
|
|
||||||
// size: 20,
|
|
||||||
// ),
|
|
||||||
// 'label': "热门",
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
Icons.bolt,
|
Icons.bolt,
|
||||||
|
|||||||
@ -155,7 +155,7 @@ class MediaPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -165,9 +165,9 @@ class MediaPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FavFolderItem extends StatelessWidget {
|
class FavFolderItem extends StatelessWidget {
|
||||||
FavFolderItem({super.key, this.item, this.index});
|
const FavFolderItem({super.key, this.item, this.index});
|
||||||
FavFolderItemData? item;
|
final FavFolderItemData? item;
|
||||||
int? index;
|
final int? index;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
String heroTag = Utils.makeHeroTag(item!.fid);
|
String heroTag = Utils.makeHeroTag(item!.fid);
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class _ArchivePanelState extends State<ArchivePanel>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return PullToRefreshNotification(
|
return PullToRefreshNotification(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import 'package:pilipala/http/video.dart';
|
|||||||
import 'package:pilipala/models/member/archive.dart';
|
import 'package:pilipala/models/member/archive.dart';
|
||||||
import 'package:pilipala/models/member/info.dart';
|
import 'package:pilipala/models/member/info.dart';
|
||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/wbi_sign.dart';
|
|
||||||
|
|
||||||
class MemberController extends GetxController {
|
class MemberController extends GetxController {
|
||||||
late int mid;
|
late int mid;
|
||||||
@ -48,13 +47,13 @@ class MemberController extends GetxController {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future getMemberCardInfo() async {
|
// Future getMemberCardInfo() async {
|
||||||
var res = await MemberHttp.memberCardInfo(mid: mid);
|
// var res = await MemberHttp.memberCardInfo(mid: mid);
|
||||||
if (res['status']) {
|
// if (res['status']) {
|
||||||
print(userStat);
|
// print(userStat);
|
||||||
}
|
// }
|
||||||
return res;
|
// return res;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 关注/取关up
|
// 关注/取关up
|
||||||
Future actionRelationMod() async {
|
Future actionRelationMod() async {
|
||||||
|
|||||||
@ -2,12 +2,8 @@ import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loading_more_list/loading_more_list.dart';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|
||||||
import 'package:pilipala/models/live/item.dart';
|
|
||||||
import 'package:pilipala/pages/member/archive/view.dart';
|
import 'package:pilipala/pages/member/archive/view.dart';
|
||||||
import 'package:pilipala/pages/member/index.dart';
|
import 'package:pilipala/pages/member/index.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
|
||||||
|
|
||||||
import 'widgets/profile.dart';
|
import 'widgets/profile.dart';
|
||||||
|
|
||||||
@ -231,7 +227,7 @@ class _MemberPageState extends State<MemberPage>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: no_leading_underscores_for_local_identifiers
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -321,11 +323,11 @@ class MinePage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ActionItem extends StatelessWidget {
|
class ActionItem extends StatelessWidget {
|
||||||
Icon? icon;
|
final Icon? icon;
|
||||||
Function? onTap;
|
final Function? onTap;
|
||||||
String? text;
|
final String? text;
|
||||||
|
|
||||||
ActionItem({
|
const ActionItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.icon,
|
this.icon,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
import 'package:image_gallery_saver/image_gallery_saver.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
class PreviewController extends GetxController {
|
class PreviewController extends GetxController {
|
||||||
@ -36,10 +35,8 @@ class PreviewController extends GetxController {
|
|||||||
// Permission.photos
|
// Permission.photos
|
||||||
].request();
|
].request();
|
||||||
|
|
||||||
final info = statuses[Permission.storage].toString();
|
statuses[Permission.storage].toString();
|
||||||
// final photosInfo = statuses[Permission.photos].toString();
|
// final photosInfo = statuses[Permission.photos].toString();
|
||||||
|
|
||||||
print('授权状态:$info');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图片保存
|
// 图片保存
|
||||||
@ -52,6 +49,7 @@ class PreviewController extends GetxController {
|
|||||||
name: "pic_vvex${DateTime.now().toString().split('-').join()}");
|
name: "pic_vvex${DateTime.now().toString().split('-').join()}");
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (result['isSuccess']) {
|
if (result['isSuccess']) {
|
||||||
|
// ignore: avoid_print
|
||||||
print('已保存到相册');
|
print('已保存到相册');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
|
// ignore_for_file: library_private_types_in_public_api
|
||||||
|
|
||||||
import 'package:dismissible_page/dismissible_page.dart';
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:extended_image/extended_image.dart';
|
import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:pilipala/common/widgets/appbar.dart';
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
|
||||||
typedef DoubleClickAnimationListener = void Function();
|
typedef DoubleClickAnimationListener = void Function();
|
||||||
@ -145,6 +146,8 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initGestureConfigHandler: (ExtendedImageState state) {
|
initGestureConfigHandler: (ExtendedImageState state) {
|
||||||
@ -168,7 +171,8 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: Container(
|
child: Container(
|
||||||
// height: 45,
|
// height: 45,
|
||||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom, top: 20),
|
padding: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context).padding.bottom, top: 20),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
@ -186,23 +190,29 @@ class _ImagePreviewState extends State<ImagePreview>
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Obx(
|
Obx(
|
||||||
() => Text.rich(
|
() => Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white, fontSize: 18),
|
||||||
fontSize: 18
|
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
TextSpan(text: _previewController.currentPage.toString()),
|
TextSpan(
|
||||||
const TextSpan(text: ' / '),
|
text:
|
||||||
TextSpan(text: _previewController.imgList.length.toString()),
|
_previewController.currentPage.toString()),
|
||||||
]),
|
const TextSpan(text: ' / '),
|
||||||
|
TextSpan(
|
||||||
|
text: _previewController.imgList.length
|
||||||
|
.toString()),
|
||||||
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
ElevatedButton(onPressed: () => _previewController.onShareImg(), child: Text('分享')),
|
ElevatedButton(
|
||||||
|
onPressed: () => _previewController.onShareImg(),
|
||||||
|
child: const Text('分享')),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
ElevatedButton(onPressed: () => _previewController.onSaveImg(), child: Text('保存'))
|
ElevatedButton(
|
||||||
|
onPressed: () => _previewController.onSaveImg(),
|
||||||
|
child: const Text('保存'))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -41,6 +41,7 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
return await _rcmdController.onRefresh();
|
return await _rcmdController.onRefresh();
|
||||||
@ -53,7 +54,7 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
padding: _rcmdController.crossAxisCount == 1
|
padding: _rcmdController.crossAxisCount == 1
|
||||||
? EdgeInsets.zero
|
? EdgeInsets.zero
|
||||||
: const EdgeInsets.fromLTRB(
|
: const EdgeInsets.fromLTRB(
|
||||||
StyleString.cardSpace, 0, StyleString.cardSpace, 8),
|
StyleString.safeSpace, 0, StyleString.safeSpace, 0),
|
||||||
sliver: FutureBuilder(
|
sliver: FutureBuilder(
|
||||||
future: _rcmdController.queryRcmdFeed('init'),
|
future: _rcmdController.queryRcmdFeed('init'),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -99,13 +100,13 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
return SliverGrid(
|
return SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
// 行间距
|
// 行间距
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace + 2,
|
||||||
// 列间距
|
// 列间距
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.cardSpace + 3,
|
||||||
// 列数
|
// 列数
|
||||||
crossAxisCount: ctr.crossAxisCount,
|
crossAxisCount: ctr.crossAxisCount,
|
||||||
mainAxisExtent:
|
mainAxisExtent:
|
||||||
Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 70,
|
Get.size.width / ctr.crossAxisCount / StyleString.aspectRatio + 60,
|
||||||
),
|
),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
(BuildContext context, int index) {
|
(BuildContext context, int index) {
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
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:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/hotKeyword.dart';
|
import 'widgets/hot_keyword.dart';
|
||||||
import 'widgets/search_text.dart';
|
import 'widgets/search_text.dart';
|
||||||
|
|
||||||
class SearchPage extends StatefulWidget {
|
class SearchPage extends StatefulWidget {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
// ignore: file_names
|
// ignore: file_names
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
|
||||||
|
|
||||||
class HotKeyword extends StatelessWidget {
|
class HotKeyword extends StatelessWidget {
|
||||||
final double? width;
|
final double? width;
|
||||||
@ -1,10 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SearchText extends StatelessWidget {
|
class SearchText extends StatelessWidget {
|
||||||
String? searchText;
|
final String? searchText;
|
||||||
Function? onSelect;
|
final Function? onSelect;
|
||||||
int? searchTextIdx;
|
final int? searchTextIdx;
|
||||||
SearchText({super.key, this.searchText, this.onSelect, this.searchTextIdx});
|
const SearchText(
|
||||||
|
{super.key, this.searchText, this.onSelect, this.searchTextIdx});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@ -11,10 +11,10 @@ import 'widgets/user_panel.dart';
|
|||||||
import 'widgets/video_panel.dart';
|
import 'widgets/video_panel.dart';
|
||||||
|
|
||||||
class SearchPanel extends StatefulWidget {
|
class SearchPanel extends StatefulWidget {
|
||||||
String? keyword;
|
final String? keyword;
|
||||||
SearchType? searchType;
|
final SearchType? searchType;
|
||||||
String? tag;
|
final String? tag;
|
||||||
SearchPanel(
|
const SearchPanel(
|
||||||
{required this.keyword, required this.searchType, this.tag, Key? key})
|
{required this.keyword, required this.searchType, this.tag, Key? key})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await _searchPanelController!.onRefresh();
|
await _searchPanelController!.onRefresh();
|
||||||
|
|||||||
@ -12,8 +12,8 @@ Widget searchLivePanel(BuildContext context, ctr, list) {
|
|||||||
controller: ctr!.scrollController,
|
controller: ctr!.scrollController,
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
crossAxisSpacing: StyleString.cardSpace,
|
crossAxisSpacing: StyleString.cardSpace + 2,
|
||||||
mainAxisSpacing: StyleString.cardSpace,
|
mainAxisSpacing: StyleString.cardSpace + 3,
|
||||||
mainAxisExtent:
|
mainAxisExtent:
|
||||||
MediaQuery.of(context).size.width / 2 / StyleString.aspectRatio +
|
MediaQuery.of(context).size.width / 2 / StyleString.aspectRatio +
|
||||||
65,
|
65,
|
||||||
@ -22,7 +22,7 @@ Widget searchLivePanel(BuildContext context, ctr, list) {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var i = list![index];
|
var i = list![index];
|
||||||
return Card(
|
return Card(
|
||||||
elevation: 0.8,
|
elevation: 0,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
@ -32,41 +32,46 @@ Widget searchLivePanel(BuildContext context, ctr, list) {
|
|||||||
onTap: () {},
|
onTap: () {},
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
AspectRatio(
|
ClipRRect(
|
||||||
aspectRatio: StyleString.aspectRatio,
|
borderRadius: const BorderRadius.only(
|
||||||
child: LayoutBuilder(builder: (context, boxConstraints) {
|
topLeft: StyleString.imgRadius,
|
||||||
double maxWidth = boxConstraints.maxWidth;
|
topRight: StyleString.imgRadius,
|
||||||
double maxHeight = boxConstraints.maxHeight;
|
bottomLeft: StyleString.imgRadius,
|
||||||
double PR = MediaQuery.of(context).devicePixelRatio;
|
bottomRight: StyleString.imgRadius,
|
||||||
return Stack(
|
),
|
||||||
children: [
|
child: AspectRatio(
|
||||||
Hero(
|
aspectRatio: StyleString.aspectRatio,
|
||||||
tag: Utils.makeHeroTag(i.roomid),
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
child: NetworkImgLayer(
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
// 指定图片尺寸
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
// src: videoItem.pic + '@${(maxWidth * 2).toInt()}w',
|
return Stack(
|
||||||
src: i.cover + '@.webp',
|
children: [
|
||||||
type: 'emote',
|
Hero(
|
||||||
width: maxWidth,
|
tag: Utils.makeHeroTag(i.roomid),
|
||||||
height: maxHeight,
|
child: NetworkImgLayer(
|
||||||
),
|
src: i.cover + '@.webp',
|
||||||
),
|
type: 'emote',
|
||||||
Positioned(
|
width: maxWidth,
|
||||||
left: 0,
|
height: maxHeight,
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: AnimatedOpacity(
|
|
||||||
opacity: 1,
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
child: LiveStat(
|
|
||||||
online: i.online,
|
|
||||||
cateName: i.cateName,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Positioned(
|
||||||
],
|
left: 0,
|
||||||
);
|
right: 0,
|
||||||
}),
|
bottom: 0,
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: 1,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: LiveStat(
|
||||||
|
online: i.online,
|
||||||
|
cateName: i.cateName,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
LiveContent(liveItem: i)
|
LiveContent(liveItem: i)
|
||||||
],
|
],
|
||||||
@ -79,13 +84,13 @@ Widget searchLivePanel(BuildContext context, ctr, list) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LiveContent extends StatelessWidget {
|
class LiveContent extends StatelessWidget {
|
||||||
final liveItem;
|
final dynamic liveItem;
|
||||||
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
|
const LiveContent({Key? key, required this.liveItem}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(8, 8, 6, 7),
|
padding: const EdgeInsets.fromLTRB(4, 5, 6, 6),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@ -99,6 +104,7 @@ class LiveContent extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0.3,
|
||||||
color: i['type'] == 'em'
|
color: i['type'] == 'em'
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(context).colorScheme.onSurface,
|
: Theme.of(context).colorScheme.onSurface,
|
||||||
|
|||||||
@ -42,9 +42,8 @@ Widget searchUserPanel(BuildContext context, ctr, list) {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i!.uname,
|
i!.uname,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize:
|
fontSize: 14,
|
||||||
Theme.of(context).textTheme.titleMedium!.fontSize,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
@ -71,7 +70,6 @@ Widget searchUserPanel(BuildContext context, ctr, list) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
;
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:pilipala/pages/setting/index.dart';
|
|||||||
class SettingPage extends StatelessWidget {
|
class SettingPage extends StatelessWidget {
|
||||||
const SettingPage({super.key});
|
const SettingPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final SettingController settingController = Get.put(SettingController());
|
final SettingController settingController = Get.put(SettingController());
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|||||||
@ -105,7 +105,6 @@ class VideoIntroController extends GetxController {
|
|||||||
// 获取up主粉丝数
|
// 获取up主粉丝数
|
||||||
Future queryUserStat() async {
|
Future queryUserStat() async {
|
||||||
var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!);
|
var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!);
|
||||||
print('🌹:$result');
|
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
userStat = result['data'];
|
userStat = result['data'];
|
||||||
}
|
}
|
||||||
@ -185,9 +184,11 @@ class VideoIntroController extends GetxController {
|
|||||||
if (!hasLike.value) {
|
if (!hasLike.value) {
|
||||||
SmartDialog.showToast('点赞成功 👍');
|
SmartDialog.showToast('点赞成功 👍');
|
||||||
hasLike.value = true;
|
hasLike.value = true;
|
||||||
|
videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1;
|
||||||
} else if (hasLike.value) {
|
} else if (hasLike.value) {
|
||||||
SmartDialog.showToast('取消赞');
|
SmartDialog.showToast('取消赞');
|
||||||
hasLike.value = false;
|
hasLike.value = false;
|
||||||
|
videoDetail.value.stat!.like = videoDetail.value.stat!.like! - 1;
|
||||||
}
|
}
|
||||||
hasLike.refresh();
|
hasLike.refresh();
|
||||||
} else {
|
} else {
|
||||||
@ -238,14 +239,15 @@ class VideoIntroController extends GetxController {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var res = await VideoHttp.coinVideo(
|
var res = await VideoHttp.coinVideo(
|
||||||
bvid: bvid, multiply: _tempThemeValue);
|
bvid: bvid, multiply: _tempThemeValue);
|
||||||
print(res);
|
|
||||||
if (res['status']) {
|
if (res['status']) {
|
||||||
SmartDialog.showToast('投币成功');
|
SmartDialog.showToast('投币成功 👏');
|
||||||
|
hasCoin.value = true;
|
||||||
|
videoDetail.value.stat!.coin =
|
||||||
|
videoDetail.value.stat!.coin! + _tempThemeValue;
|
||||||
} else {
|
} else {
|
||||||
SmartDialog.showToast(res['msg']);
|
SmartDialog.showToast(res['msg']);
|
||||||
}
|
}
|
||||||
Get.back();
|
Get.back();
|
||||||
queryHasCoinVideo();
|
|
||||||
},
|
},
|
||||||
child: const Text('确定'))
|
child: const Text('确定'))
|
||||||
],
|
],
|
||||||
@ -263,7 +265,10 @@ class VideoIntroController extends GetxController {
|
|||||||
delMediaIdsNew.add(i.id);
|
delMediaIdsNew.add(i.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
// ignore: avoid_print
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
var result = await VideoHttp.favVideo(
|
var result = await VideoHttp.favVideo(
|
||||||
aid: IdUtils.bv2av(bvid),
|
aid: IdUtils.bv2av(bvid),
|
||||||
addIds: addMediaIdsNew.join(','),
|
addIds: addMediaIdsNew.join(','),
|
||||||
@ -282,10 +287,8 @@ class VideoIntroController extends GetxController {
|
|||||||
|
|
||||||
// 分享视频
|
// 分享视频
|
||||||
Future actionShareVideo() async {
|
Future actionShareVideo() async {
|
||||||
var result =
|
var result = await Share.share('${HttpString.baseUrl}/video/$bvid')
|
||||||
await Share.share('${HttpString.baseUrl}/video/$bvid').whenComplete(() {
|
.whenComplete(() {});
|
||||||
print("share completion block ");
|
|
||||||
});
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
@ -7,10 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
import 'package:pilipala/common/widgets/http_error.dart';
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
import 'package:pilipala/pages/fav/index.dart';
|
|
||||||
import 'package:pilipala/pages/favDetail/index.dart';
|
|
||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/widgets/expandable_section.dart';
|
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||||
import 'package:pilipala/common/widgets/stat/view.dart';
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
@ -19,7 +15,9 @@ import 'package:pilipala/pages/video/detail/introduction/controller.dart';
|
|||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:pilipala/utils/utils.dart';
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
|
import 'widgets/action_row_item.dart';
|
||||||
import 'widgets/fav_panel.dart';
|
import 'widgets/fav_panel.dart';
|
||||||
|
import 'widgets/intro_detail.dart';
|
||||||
import 'widgets/season.dart';
|
import 'widgets/season.dart';
|
||||||
|
|
||||||
class VideoIntroPanel extends StatefulWidget {
|
class VideoIntroPanel extends StatefulWidget {
|
||||||
@ -55,6 +53,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: videoIntroController.queryVideoIntro(),
|
future: videoIntroController.queryVideoIntro(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -79,10 +78,10 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
class VideoInfo extends StatefulWidget {
|
class VideoInfo extends StatefulWidget {
|
||||||
bool loadingStatus = false;
|
final bool loadingStatus;
|
||||||
VideoDetailData? videoDetail;
|
final VideoDetailData? videoDetail;
|
||||||
|
|
||||||
VideoInfo({Key? key, required this.loadingStatus, this.videoDetail})
|
const VideoInfo({Key? key, this.loadingStatus = false, this.videoDetail})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -95,14 +94,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
Get.put(VideoIntroController(), tag: Get.arguments['heroTag']);
|
Get.put(VideoIntroController(), tag: Get.arguments['heroTag']);
|
||||||
bool isExpand = false;
|
bool isExpand = false;
|
||||||
|
|
||||||
/// 手动控制动画的控制器
|
|
||||||
late AnimationController? _manualController;
|
|
||||||
|
|
||||||
/// 手动控制
|
|
||||||
late Animation<double>? _manualAnimation;
|
|
||||||
|
|
||||||
final FavController _favController = Get.put(FavController());
|
|
||||||
|
|
||||||
late VideoDetailController? videoDetailCtr;
|
late VideoDetailController? videoDetailCtr;
|
||||||
Box localCache = GStrorage.localCache;
|
Box localCache = GStrorage.localCache;
|
||||||
late double sheetHeight;
|
late double sheetHeight;
|
||||||
@ -111,13 +102,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
/// 不设置重复,使用代码控制进度,动画时间1秒
|
|
||||||
_manualController = AnimationController(
|
|
||||||
vsync: this,
|
|
||||||
duration: const Duration(milliseconds: 400),
|
|
||||||
);
|
|
||||||
_manualAnimation =
|
|
||||||
Tween<double>(begin: 0.5, end: 1.5).animate(_manualController!);
|
|
||||||
videoDetailCtr =
|
videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
|
||||||
sheetHeight = localCache.get('sheetHeight');
|
sheetHeight = localCache.get('sheetHeight');
|
||||||
@ -141,143 +125,123 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliverPadding(
|
return SliverPadding(
|
||||||
padding: const EdgeInsets.only(left: 12, right: 12, top: 10),
|
padding: const EdgeInsets.only(
|
||||||
|
left: StyleString.safeSpace, right: StyleString.safeSpace, top: 13),
|
||||||
sliver: SliverToBoxAdapter(
|
sliver: SliverToBoxAdapter(
|
||||||
child: !widget.loadingStatus || videoItem.isNotEmpty
|
child: !widget.loadingStatus || videoItem.isNotEmpty
|
||||||
? Column(
|
? Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SelectableRegion(
|
GestureDetector(
|
||||||
magnifierConfiguration: const TextMagnifierConfiguration(),
|
|
||||||
focusNode: FocusNode(),
|
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
|
||||||
child: Text(
|
|
||||||
!widget.loadingStatus
|
|
||||||
? widget.videoDetail!.title
|
|
||||||
: videoItem['title'],
|
|
||||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
InkWell(
|
|
||||||
splashColor: Colors.transparent,
|
|
||||||
hoverColor: Colors.transparent,
|
|
||||||
highlightColor: Colors.transparent,
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_manualController!.animateTo(isExpand ? 0 : 0.5);
|
showBottomSheet(
|
||||||
setState(() {
|
context: context,
|
||||||
isExpand = !isExpand;
|
enableDrag: true,
|
||||||
});
|
builder: (BuildContext context) {
|
||||||
|
return IntroDetail(videoDetail: widget.videoDetail!);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 2),
|
Expanded(
|
||||||
StatView(
|
child: Text(
|
||||||
theme: 'gray',
|
!widget.loadingStatus
|
||||||
view: !widget.loadingStatus
|
? widget.videoDetail!.title
|
||||||
? widget.videoDetail!.stat!.view
|
: videoItem['title'],
|
||||||
: videoItem['stat'].view,
|
style: const TextStyle(
|
||||||
size: 'medium',
|
fontSize: 18,
|
||||||
),
|
letterSpacing: 0.3,
|
||||||
const SizedBox(width: 10),
|
fontWeight: FontWeight.w500,
|
||||||
StatDanMu(
|
|
||||||
theme: 'gray',
|
|
||||||
danmu: !widget.loadingStatus
|
|
||||||
? widget.videoDetail!.stat!.danmaku
|
|
||||||
: videoItem['stat'].danmaku,
|
|
||||||
size: 'medium',
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
Utils.dateFormat(
|
|
||||||
!widget.loadingStatus
|
|
||||||
? widget.videoDetail!.pubdate
|
|
||||||
: videoItem['pubdate'],
|
|
||||||
formatType: 'detail'),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.outline),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
RotationTransition(
|
|
||||||
turns: _manualAnimation!,
|
|
||||||
child: SizedBox(
|
|
||||||
width: 35,
|
|
||||||
height: 35,
|
|
||||||
child: IconButton(
|
|
||||||
padding: const EdgeInsets.all(2.0),
|
|
||||||
onPressed: () {
|
|
||||||
/// 0.5代表 180弧度
|
|
||||||
_manualController!
|
|
||||||
.animateTo(isExpand ? 0 : 0.5);
|
|
||||||
setState(() {
|
|
||||||
isExpand = !isExpand;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
icon: Icon(
|
|
||||||
FontAwesomeIcons.angleUp,
|
|
||||||
size: 15,
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
SizedBox(
|
||||||
|
width: 34,
|
||||||
|
height: 34,
|
||||||
|
child: IconButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
padding:
|
||||||
|
MaterialStateProperty.all(EdgeInsets.zero),
|
||||||
|
backgroundColor:
|
||||||
|
MaterialStateProperty.resolveWith((states) {
|
||||||
|
return Theme.of(context)
|
||||||
|
.highlightColor
|
||||||
|
.withOpacity(0.2);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showBottomSheet(
|
||||||
|
context: context,
|
||||||
|
enableDrag: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return IntroDetail(
|
||||||
|
videoDetail: widget.videoDetail!);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.more_horiz),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// 简介 默认收起
|
const SizedBox(height: 6),
|
||||||
if (!widget.loadingStatus)
|
Row(
|
||||||
ExpandedSection(
|
children: [
|
||||||
expand: isExpand,
|
const SizedBox(width: 2),
|
||||||
begin: 0.0,
|
StatView(
|
||||||
end: 1.0,
|
theme: 'black',
|
||||||
child: DefaultTextStyle(
|
view: !widget.loadingStatus
|
||||||
style: TextStyle(
|
? widget.videoDetail!.stat!.view
|
||||||
color: Theme.of(context).colorScheme.outline,
|
: videoItem['stat'].view,
|
||||||
height: 1.5,
|
size: 'medium',
|
||||||
fontSize:
|
),
|
||||||
Theme.of(context).textTheme.labelMedium?.fontSize,
|
const SizedBox(width: 10),
|
||||||
),
|
StatDanMu(
|
||||||
child: Padding(
|
theme: 'black',
|
||||||
padding: const EdgeInsets.only(bottom: 10),
|
danmu: !widget.loadingStatus
|
||||||
child: SelectableRegion(
|
? widget.videoDetail!.stat!.danmaku
|
||||||
magnifierConfiguration:
|
: videoItem['stat'].danmaku,
|
||||||
const TextMagnifierConfiguration(),
|
size: 'medium',
|
||||||
focusNode: FocusNode(),
|
),
|
||||||
selectionControls: MaterialTextSelectionControls(),
|
const SizedBox(width: 10),
|
||||||
child: Column(
|
Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Utils.dateFormat(
|
||||||
children: [
|
!widget.loadingStatus
|
||||||
Text(widget.videoDetail!.bvid!),
|
? widget.videoDetail!.pubdate
|
||||||
Text.rich(
|
: videoItem['pubdate'],
|
||||||
TextSpan(
|
formatType: 'detail'),
|
||||||
children: [
|
style: const TextStyle(fontSize: 12),
|
||||||
buildContent(
|
),
|
||||||
context, widget.videoDetail!),
|
],
|
||||||
],
|
),
|
||||||
),
|
// 点赞收藏转发
|
||||||
),
|
Padding(
|
||||||
],
|
padding: const EdgeInsets.only(top: 15),
|
||||||
),
|
child: SingleChildScrollView(
|
||||||
),
|
scrollDirection: Axis.horizontal,
|
||||||
),
|
child: actionRow(
|
||||||
|
context,
|
||||||
|
videoIntroController,
|
||||||
|
videoDetailCtr,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
),
|
||||||
// 点赞收藏转发
|
|
||||||
_actionGrid(context, videoIntroController, videoDetailCtr),
|
|
||||||
// 合集
|
// 合集
|
||||||
if (!widget.loadingStatus &&
|
if (!widget.loadingStatus &&
|
||||||
widget.videoDetail!.ugcSeason != null) ...[
|
widget.videoDetail!.ugcSeason != null) ...[
|
||||||
seasonPanel(widget.videoDetail!.ugcSeason!,
|
seasonPanel(widget.videoDetail!.ugcSeason!,
|
||||||
widget.videoDetail!.pages!.first.cid, sheetHeight)
|
widget.videoDetail!.pages!.first.cid, sheetHeight)
|
||||||
],
|
],
|
||||||
Divider(
|
// Divider(
|
||||||
height: 26,
|
// height: 26,
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
// color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||||
),
|
// ),
|
||||||
|
const SizedBox(height: 20),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
int mid = !widget.loadingStatus
|
int mid = !widget.loadingStatus
|
||||||
@ -293,53 +257,84 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
const SizedBox(width: 5),
|
||||||
NetworkImgLayer(
|
NetworkImgLayer(
|
||||||
type: 'avatar',
|
type: 'avatar',
|
||||||
src: !widget.loadingStatus
|
src: !widget.loadingStatus
|
||||||
? widget.videoDetail!.owner!.face
|
? widget.videoDetail!.owner!.face
|
||||||
: videoItem['owner'].face,
|
: videoItem['owner'].face,
|
||||||
width: 38,
|
width: 34,
|
||||||
height: 38,
|
height: 34,
|
||||||
fadeInDuration: Duration.zero,
|
fadeInDuration: Duration.zero,
|
||||||
fadeOutDuration: Duration.zero,
|
fadeOutDuration: Duration.zero,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 14),
|
const SizedBox(width: 10),
|
||||||
Column(
|
Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
!widget.loadingStatus
|
||||||
children: [
|
? widget.videoDetail!.owner!.name
|
||||||
Text(!widget.loadingStatus
|
: videoItem['owner'].name,
|
||||||
? widget.videoDetail!.owner!.name
|
style: const TextStyle(fontSize: 13),
|
||||||
: videoItem['owner'].name),
|
),
|
||||||
// const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Text(
|
Text(
|
||||||
widget.loadingStatus
|
widget.loadingStatus
|
||||||
? '- 粉丝'
|
? '- 粉丝'
|
||||||
: '${Utils.numFormat(videoIntroController.userStat['follower'])}粉丝',
|
: Utils.numFormat(
|
||||||
style: TextStyle(
|
videoIntroController.userStat['follower']),
|
||||||
fontSize: Theme.of(context)
|
style: TextStyle(
|
||||||
.textTheme
|
fontSize: Theme.of(context)
|
||||||
.labelSmall!
|
.textTheme
|
||||||
.fontSize,
|
.labelSmall!
|
||||||
color: Theme.of(context).colorScheme.outline),
|
.fontSize,
|
||||||
),
|
color: Theme.of(context).colorScheme.outline),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
opacity: widget.loadingStatus ? 0 : 1,
|
opacity: widget.loadingStatus ? 0 : 1,
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 36,
|
height: 32,
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => videoIntroController.followStatus.isNotEmpty
|
() => videoIntroController.followStatus.isNotEmpty
|
||||||
? ElevatedButton(
|
? TextButton(
|
||||||
onPressed: () => videoIntroController
|
onPressed: () => videoIntroController
|
||||||
.actionRelationMod(),
|
.actionRelationMod(),
|
||||||
child: Text(videoIntroController
|
style: TextButton.styleFrom(
|
||||||
.followStatus['attribute'] ==
|
padding: const EdgeInsets.only(
|
||||||
0
|
left: 8, right: 8),
|
||||||
? '关注'
|
foregroundColor:
|
||||||
: '已关注'),
|
videoIntroController.followStatus[
|
||||||
|
'attribute'] !=
|
||||||
|
0
|
||||||
|
? Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline
|
||||||
|
: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onPrimary,
|
||||||
|
backgroundColor:
|
||||||
|
videoIntroController.followStatus[
|
||||||
|
'attribute'] !=
|
||||||
|
0
|
||||||
|
? Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onInverseSurface
|
||||||
|
: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary, // 设置按钮背景色
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
videoIntroController.followStatus[
|
||||||
|
'attribute'] !=
|
||||||
|
0
|
||||||
|
? '已关注'
|
||||||
|
: '关注',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelMedium!
|
||||||
|
.fontSize),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: ElevatedButton(
|
: ElevatedButton(
|
||||||
onPressed: () => videoIntroController
|
onPressed: () => videoIntroController
|
||||||
@ -349,110 +344,87 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 4)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
Divider(
|
// Divider(
|
||||||
height: 26,
|
// height: 12,
|
||||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
// color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||||
),
|
// ),
|
||||||
// const SizedBox(height: 10),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: const Center(child: CircularProgressIndicator()),
|
: const SizedBox(
|
||||||
|
height: 100,
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 喜欢 投币 分享
|
Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) {
|
||||||
Widget _actionGrid(
|
return Row(children: [
|
||||||
BuildContext context, videoIntroController, videoDetailCtr) {
|
Obx(
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
() => ActionRowItem(
|
||||||
return SizedBox(
|
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
||||||
height: constraints.maxWidth / 5 * 0.8,
|
onTap: () => videoIntroController.actionLikeVideo(),
|
||||||
child: Material(
|
selectStatus: videoIntroController.hasLike.value,
|
||||||
child: GridView.count(
|
loadingStatus: widget.loadingStatus,
|
||||||
primary: false,
|
text: !widget.loadingStatus
|
||||||
padding: const EdgeInsets.all(0),
|
? widget.videoDetail!.stat!.like!.toString()
|
||||||
crossAxisCount: 5,
|
: '-',
|
||||||
childAspectRatio: 1.25,
|
|
||||||
children: <Widget>[
|
|
||||||
// InkWell(
|
|
||||||
// onTap: () => videoIntroController.actionOneThree(),
|
|
||||||
// borderRadius: StyleString.mdRadius,
|
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsets.all(12),
|
|
||||||
// child: Image.asset(
|
|
||||||
// 'assets/images/logo/logo_big.png',
|
|
||||||
// width: 10,
|
|
||||||
// height: 10,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
Obx(
|
|
||||||
() => ActionItem(
|
|
||||||
icon: const Icon(FontAwesomeIcons.thumbsUp),
|
|
||||||
selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
|
|
||||||
onTap: () => videoIntroController.actionLikeVideo(),
|
|
||||||
selectStatus: videoIntroController.hasLike.value,
|
|
||||||
loadingStatus: widget.loadingStatus,
|
|
||||||
text: !widget.loadingStatus
|
|
||||||
? widget.videoDetail!.stat!.like!.toString()
|
|
||||||
: '-'),
|
|
||||||
),
|
|
||||||
// ActionItem(
|
|
||||||
// icon: const Icon(FontAwesomeIcons.thumbsDown),
|
|
||||||
// selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown),
|
|
||||||
// onTap: () => {},
|
|
||||||
// selectStatus: false,
|
|
||||||
// loadingStatus: widget.loadingStatus,
|
|
||||||
// text: '不喜欢'),
|
|
||||||
Obx(
|
|
||||||
() => ActionItem(
|
|
||||||
icon: const Icon(FontAwesomeIcons.b),
|
|
||||||
selectIcon: const Icon(FontAwesomeIcons.b),
|
|
||||||
onTap: () => videoIntroController.actionCoinVideo(),
|
|
||||||
selectStatus: videoIntroController.hasCoin.value,
|
|
||||||
loadingStatus: widget.loadingStatus,
|
|
||||||
text: !widget.loadingStatus
|
|
||||||
? widget.videoDetail!.stat!.coin!.toString()
|
|
||||||
: '-'),
|
|
||||||
),
|
|
||||||
Obx(
|
|
||||||
() => ActionItem(
|
|
||||||
icon: const Icon(FontAwesomeIcons.heart),
|
|
||||||
selectIcon: const Icon(FontAwesomeIcons.heartCircleCheck),
|
|
||||||
onTap: () => showFavBottomSheet(),
|
|
||||||
selectStatus: videoIntroController.hasFav.value,
|
|
||||||
loadingStatus: widget.loadingStatus,
|
|
||||||
text: !widget.loadingStatus
|
|
||||||
? widget.videoDetail!.stat!.favorite!.toString()
|
|
||||||
: '-'),
|
|
||||||
),
|
|
||||||
ActionItem(
|
|
||||||
icon: const Icon(FontAwesomeIcons.shareFromSquare),
|
|
||||||
onTap: () => videoIntroController.actionShareVideo(),
|
|
||||||
selectStatus: false,
|
|
||||||
loadingStatus: widget.loadingStatus,
|
|
||||||
text: !widget.loadingStatus
|
|
||||||
? widget.videoDetail!.stat!.share!.toString()
|
|
||||||
: '-'),
|
|
||||||
ActionItem(
|
|
||||||
icon: const Icon(FontAwesomeIcons.comments),
|
|
||||||
onTap: () {
|
|
||||||
videoDetailCtr.tabCtr.animateTo(1);
|
|
||||||
},
|
|
||||||
selectStatus: false,
|
|
||||||
loadingStatus: widget.loadingStatus,
|
|
||||||
text: !widget.loadingStatus
|
|
||||||
? widget.videoDetail!.stat!.reply!.toString()
|
|
||||||
: '-'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
});
|
const SizedBox(width: 8),
|
||||||
|
Obx(
|
||||||
|
() => ActionRowItem(
|
||||||
|
icon: const Icon(FontAwesomeIcons.b),
|
||||||
|
onTap: () => videoIntroController.actionCoinVideo(),
|
||||||
|
selectStatus: videoIntroController.hasCoin.value,
|
||||||
|
loadingStatus: widget.loadingStatus,
|
||||||
|
text: !widget.loadingStatus
|
||||||
|
? widget.videoDetail!.stat!.coin!.toString()
|
||||||
|
: '-',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Obx(
|
||||||
|
() => ActionRowItem(
|
||||||
|
icon: const Icon(FontAwesomeIcons.heart),
|
||||||
|
onTap: () => showFavBottomSheet(),
|
||||||
|
selectStatus: videoIntroController.hasFav.value,
|
||||||
|
loadingStatus: widget.loadingStatus,
|
||||||
|
text: !widget.loadingStatus
|
||||||
|
? widget.videoDetail!.stat!.favorite!.toString()
|
||||||
|
: '-',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ActionRowItem(
|
||||||
|
icon: const Icon(FontAwesomeIcons.comment),
|
||||||
|
onTap: () {
|
||||||
|
videoDetailCtr.tabCtr.animateTo(1);
|
||||||
|
},
|
||||||
|
selectStatus: false,
|
||||||
|
loadingStatus: widget.loadingStatus,
|
||||||
|
text: !widget.loadingStatus
|
||||||
|
? widget.videoDetail!.stat!.reply!.toString()
|
||||||
|
: '-',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ActionRowItem(
|
||||||
|
icon: const Icon(FontAwesomeIcons.share),
|
||||||
|
onTap: () => videoIntroController.actionShareVideo(),
|
||||||
|
selectStatus: false,
|
||||||
|
loadingStatus: widget.loadingStatus,
|
||||||
|
// text: !widget.loadingStatus
|
||||||
|
// ? widget.videoDetail!.stat!.share!.toString()
|
||||||
|
// : '-',
|
||||||
|
text: '转发'),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
InlineSpan buildContent(BuildContext context, content) {
|
InlineSpan buildContent(BuildContext context, content) {
|
||||||
@ -491,54 +463,3 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
return TextSpan(children: spanChilds);
|
return TextSpan(children: spanChilds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActionItem extends StatelessWidget {
|
|
||||||
Icon? icon;
|
|
||||||
Icon? selectIcon;
|
|
||||||
Function? onTap;
|
|
||||||
bool? loadingStatus;
|
|
||||||
String? text;
|
|
||||||
bool selectStatus = false;
|
|
||||||
|
|
||||||
ActionItem({
|
|
||||||
Key? key,
|
|
||||||
this.icon,
|
|
||||||
this.selectIcon,
|
|
||||||
this.onTap,
|
|
||||||
this.loadingStatus,
|
|
||||||
this.text,
|
|
||||||
required this.selectStatus,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InkWell(
|
|
||||||
onTap: () => onTap!(),
|
|
||||||
borderRadius: StyleString.mdRadius,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
selectStatus
|
|
||||||
? Icon(selectIcon!.icon!,
|
|
||||||
size: 21, color: Theme.of(context).primaryColor)
|
|
||||||
: Icon(icon!.icon!,
|
|
||||||
size: 21, color: Theme.of(context).colorScheme.outline),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
AnimatedOpacity(
|
|
||||||
opacity: loadingStatus! ? 0 : 1,
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
child: Text(
|
|
||||||
text ?? '',
|
|
||||||
style: TextStyle(
|
|
||||||
color: selectStatus
|
|
||||||
? Theme.of(context).primaryColor
|
|
||||||
: Theme.of(context).colorScheme.outline,
|
|
||||||
fontSize: Theme.of(context).textTheme.labelSmall?.fontSize),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
53
lib/pages/video/detail/introduction/widgets/action_item.dart
Normal file
53
lib/pages/video/detail/introduction/widgets/action_item.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pilipala/common/constants.dart';
|
||||||
|
|
||||||
|
class ActionItem extends StatelessWidget {
|
||||||
|
final Icon? icon;
|
||||||
|
final Icon? selectIcon;
|
||||||
|
final Function? onTap;
|
||||||
|
final bool? loadingStatus;
|
||||||
|
final String? text;
|
||||||
|
final bool selectStatus;
|
||||||
|
|
||||||
|
const ActionItem({
|
||||||
|
Key? key,
|
||||||
|
this.icon,
|
||||||
|
this.selectIcon,
|
||||||
|
this.onTap,
|
||||||
|
this.loadingStatus,
|
||||||
|
this.text,
|
||||||
|
this.selectStatus = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => onTap!(),
|
||||||
|
borderRadius: StyleString.mdRadius,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
selectStatus
|
||||||
|
? Icon(selectIcon!.icon!,
|
||||||
|
size: 21, color: Theme.of(context).primaryColor)
|
||||||
|
: Icon(icon!.icon!,
|
||||||
|
size: 21, color: Theme.of(context).colorScheme.outline),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
AnimatedOpacity(
|
||||||
|
opacity: loadingStatus! ? 0 : 1,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: Text(
|
||||||
|
text ?? '',
|
||||||
|
style: TextStyle(
|
||||||
|
color: selectStatus
|
||||||
|
? Theme.of(context).primaryColor
|
||||||
|
: Theme.of(context).colorScheme.outline,
|
||||||
|
fontSize: Theme.of(context).textTheme.labelSmall?.fontSize),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ActionRowItem extends StatelessWidget {
|
||||||
|
final Icon? icon;
|
||||||
|
final Icon? selectIcon;
|
||||||
|
final Function? onTap;
|
||||||
|
final bool? loadingStatus;
|
||||||
|
final String? text;
|
||||||
|
final bool selectStatus;
|
||||||
|
|
||||||
|
const ActionRowItem({
|
||||||
|
Key? key,
|
||||||
|
this.icon,
|
||||||
|
this.selectIcon,
|
||||||
|
this.onTap,
|
||||||
|
this.loadingStatus,
|
||||||
|
this.text,
|
||||||
|
this.selectStatus = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
color: selectStatus
|
||||||
|
? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.6)
|
||||||
|
: Theme.of(context).highlightColor.withOpacity(0.2),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => onTap!(),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(13, 6.5, 15, 6.3),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (icon != null) ...[
|
||||||
|
Icon(icon!.icon!,
|
||||||
|
size: 13,
|
||||||
|
color: selectStatus
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSecondaryContainer),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
],
|
||||||
|
AnimatedOpacity(
|
||||||
|
opacity: loadingStatus! ? 0 : 1,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
transitionBuilder:
|
||||||
|
(Widget child, Animation<double> animation) {
|
||||||
|
return ScaleTransition(scale: animation, child: child);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
text ?? '',
|
||||||
|
key: ValueKey<String>(text ?? ''),
|
||||||
|
style: TextStyle(
|
||||||
|
color: selectStatus
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: null,
|
||||||
|
fontSize:
|
||||||
|
Theme.of(context).textTheme.labelSmall!.fontSize),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,8 +5,8 @@ import 'package:pilipala/common/widgets/http_error.dart';
|
|||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class FavPanel extends StatefulWidget {
|
class FavPanel extends StatefulWidget {
|
||||||
var ctr;
|
final dynamic ctr;
|
||||||
FavPanel({this.ctr});
|
const FavPanel({super.key, this.ctr});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FavPanel> createState() => _FavPanelState();
|
State<FavPanel> createState() => _FavPanelState();
|
||||||
@ -111,7 +111,7 @@ class _FavPanelState extends State<FavPanel> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 骨架屏
|
// 骨架屏
|
||||||
return Text('请求中');
|
return const Text('请求中');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
149
lib/pages/video/detail/introduction/widgets/intro_detail.dart
Normal file
149
lib/pages/video/detail/introduction/widgets/intro_detail.dart
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/common/widgets/stat/danmu.dart';
|
||||||
|
import 'package:pilipala/common/widgets/stat/view.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
import 'package:pilipala/utils/utils.dart';
|
||||||
|
|
||||||
|
Box localCache = GStrorage.localCache;
|
||||||
|
late double sheetHeight;
|
||||||
|
|
||||||
|
class IntroDetail extends StatelessWidget {
|
||||||
|
final dynamic videoDetail;
|
||||||
|
|
||||||
|
const IntroDetail({
|
||||||
|
Key? key,
|
||||||
|
this.videoDetail,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
sheetHeight = localCache.get('sheetHeight');
|
||||||
|
return Container(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
padding: const EdgeInsets.only(left: 14, right: 14),
|
||||||
|
height: sheetHeight,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 35,
|
||||||
|
padding: const EdgeInsets.only(bottom: 2),
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
width: 32,
|
||||||
|
height: 3,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondaryContainer
|
||||||
|
.withOpacity(0.5),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(3))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
videoDetail!.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 2),
|
||||||
|
StatView(
|
||||||
|
theme: 'black',
|
||||||
|
view: videoDetail!.stat!.view,
|
||||||
|
size: 'medium',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
StatDanMu(
|
||||||
|
theme: 'black',
|
||||||
|
danmu: videoDetail!.stat!.danmaku,
|
||||||
|
size: 'medium',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
Utils.dateFormat(videoDetail!.pubdate,
|
||||||
|
formatType: 'detail'),
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: SelectableRegion(
|
||||||
|
magnifierConfiguration:
|
||||||
|
const TextMagnifierConfiguration(),
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
selectionControls: MaterialTextSelectionControls(),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(videoDetail!.bvid!),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
buildContent(context, videoDetail!),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineSpan buildContent(BuildContext context, content) {
|
||||||
|
String desc = content.desc;
|
||||||
|
List descV2 = content.descV2;
|
||||||
|
// type
|
||||||
|
// 1 普通文本
|
||||||
|
// 2 @用户
|
||||||
|
List<InlineSpan> spanChilds = [];
|
||||||
|
if (descV2.isNotEmpty) {
|
||||||
|
for (var i = 0; i < descV2.length; i++) {
|
||||||
|
if (descV2[i].type == 1) {
|
||||||
|
spanChilds.add(TextSpan(text: descV2[i].rawText));
|
||||||
|
} else if (descV2[i].type == 2) {
|
||||||
|
spanChilds.add(
|
||||||
|
TextSpan(
|
||||||
|
text: '@${descV2[i].rawText}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
String heroTag = Utils.makeHeroTag(descV2[i].bizId);
|
||||||
|
Get.toNamed(
|
||||||
|
'/member?mid=${descV2[i].bizId}',
|
||||||
|
arguments: {'face': '', 'heroTag': heroTag},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spanChilds.add(TextSpan(text: desc));
|
||||||
|
}
|
||||||
|
return TextSpan(children: spanChilds);
|
||||||
|
}
|
||||||
|
}
|
||||||
93
lib/pages/video/detail/introduction/widgets/menu_row.dart
Normal file
93
lib/pages/video/detail/introduction/widgets/menu_row.dart
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MenuRow extends StatelessWidget {
|
||||||
|
final bool? loadingStatus;
|
||||||
|
const MenuRow({
|
||||||
|
Key? key,
|
||||||
|
this.loadingStatus,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(children: [
|
||||||
|
actionRowLineItem(
|
||||||
|
context,
|
||||||
|
() => {},
|
||||||
|
loadingStatus,
|
||||||
|
'推荐',
|
||||||
|
selectStatus: true,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
actionRowLineItem(
|
||||||
|
context,
|
||||||
|
() => {},
|
||||||
|
loadingStatus,
|
||||||
|
'弹幕',
|
||||||
|
selectStatus: false,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
actionRowLineItem(
|
||||||
|
context,
|
||||||
|
() => {},
|
||||||
|
loadingStatus,
|
||||||
|
'评论列表',
|
||||||
|
selectStatus: false,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
actionRowLineItem(
|
||||||
|
context,
|
||||||
|
() => {},
|
||||||
|
loadingStatus,
|
||||||
|
'播放列表',
|
||||||
|
selectStatus: false,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget actionRowLineItem(
|
||||||
|
context, Function? onTap, bool? loadingStatus, String? text,
|
||||||
|
{bool selectStatus = false}) {
|
||||||
|
return Material(
|
||||||
|
color: selectStatus
|
||||||
|
? Theme.of(context).highlightColor.withOpacity(0.2)
|
||||||
|
: Colors.transparent,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => onTap!(),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(13, 5.5, 13, 5.5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||||
|
border: Border.all(
|
||||||
|
color: selectStatus
|
||||||
|
? Colors.transparent
|
||||||
|
: Theme.of(context).highlightColor.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
AnimatedOpacity(
|
||||||
|
opacity: loadingStatus! ? 0 : 1,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: Text(
|
||||||
|
text!,
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:pilipala/models/video_detail_res.dart';
|
import 'package:pilipala/models/video_detail_res.dart';
|
||||||
|
|
||||||
Widget seasonPanel(UgcSeason ugcSeason, cid, sheetHeight) {
|
Widget seasonPanel(UgcSeason ugcSeason, cid, sheetHeight) {
|
||||||
@ -75,21 +74,28 @@ Widget seasonPanel(UgcSeason ugcSeason, cid, sheetHeight) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
padding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'合集:${ugcSeason.title!}',
|
'合集:${ugcSeason.title!}',
|
||||||
|
style: Theme.of(context).textTheme.labelMedium,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/live.gif',
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
height: 11,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
'${currentIndex + 1} / ${ugcSeason.epCount}',
|
'${currentIndex + 1} / ${ugcSeason.epCount}',
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
style: Theme.of(context).textTheme.labelMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 6),
|
||||||
const Icon(
|
const Icon(
|
||||||
Icons.arrow_forward_ios_outlined,
|
Icons.arrow_forward_ios_outlined,
|
||||||
size: 13,
|
size: 13,
|
||||||
|
|||||||
@ -114,6 +114,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
_videoReplyController.currentPage = 0;
|
_videoReplyController.currentPage = 0;
|
||||||
@ -130,8 +131,18 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
floating: true,
|
floating: true,
|
||||||
delegate: _MySliverPersistentHeaderDelegate(
|
delegate: _MySliverPersistentHeaderDelegate(
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.background,
|
height: 45,
|
||||||
padding: const EdgeInsets.fromLTRB(12, 6, 10, 6),
|
padding: const EdgeInsets.fromLTRB(12, 0, 6, 0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline
|
||||||
|
.withOpacity(0.1)),
|
||||||
|
),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -155,9 +166,11 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
|||||||
child: TextButton.icon(
|
child: TextButton.icon(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
_videoReplyController.queryBySort(),
|
_videoReplyController.queryBySort(),
|
||||||
icon: const Icon(Icons.sort, size: 17),
|
icon: const Icon(Icons.sort, size: 15),
|
||||||
label: Obx(() => Text(
|
label: Obx(() => Text(
|
||||||
_videoReplyController.sortTypeLabel.value)),
|
_videoReplyController.sortTypeLabel.value,
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@ -12,21 +12,21 @@ import 'package:pilipala/utils/utils.dart';
|
|||||||
import 'zan.dart';
|
import 'zan.dart';
|
||||||
|
|
||||||
class ReplyItem extends StatelessWidget {
|
class ReplyItem extends StatelessWidget {
|
||||||
ReplyItem({
|
const ReplyItem({
|
||||||
super.key,
|
|
||||||
this.replyItem,
|
this.replyItem,
|
||||||
this.addReply,
|
this.addReply,
|
||||||
this.replyLevel,
|
this.replyLevel,
|
||||||
this.showReplyRow,
|
this.showReplyRow = true,
|
||||||
this.replyReply,
|
this.replyReply,
|
||||||
this.replyType,
|
this.replyType,
|
||||||
});
|
Key? key,
|
||||||
ReplyItemModel? replyItem;
|
}) : super(key: key);
|
||||||
Function? addReply;
|
final ReplyItemModel? replyItem;
|
||||||
String? replyLevel;
|
final Function? addReply;
|
||||||
bool? showReplyRow = true;
|
final String? replyLevel;
|
||||||
Function? replyReply;
|
final bool? showReplyRow;
|
||||||
ReplyType? replyType;
|
final Function? replyReply;
|
||||||
|
final ReplyType? replyType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -108,8 +108,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
replyItem!.member!.vip!['vipStatus'] > 0
|
replyItem!.member!.vip!['vipStatus'] > 0
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(context).colorScheme.outline,
|
: Theme.of(context).colorScheme.outline,
|
||||||
fontSize:
|
fontSize: 13,
|
||||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
@ -118,7 +117,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
height: 11,
|
height: 11,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
if (replyItem!.isUp!) UpTag(),
|
if (replyItem!.isUp!) const UpTag(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -186,7 +185,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
if (replyItem!.isTop!)
|
if (replyItem!.isTop!)
|
||||||
WidgetSpan(child: UpTag(tagText: 'TOP')),
|
const WidgetSpan(child: UpTag(tagText: 'TOP')),
|
||||||
buildContent(context, replyItem!, replyReply, null),
|
buildContent(context, replyItem!, replyReply, null),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -204,7 +203,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
child: ReplyItemRow(
|
child: ReplyItemRow(
|
||||||
replies: replyItem!.replies,
|
replies: replyItem!.replies,
|
||||||
replyControl: replyItem!.replyControl,
|
replyControl: replyItem!.replyControl,
|
||||||
f_rpid: replyItem!.rpid,
|
// f_rpid: replyItem!.rpid,
|
||||||
replyItem: replyItem,
|
replyItem: replyItem,
|
||||||
replyReply: replyReply,
|
replyReply: replyReply,
|
||||||
),
|
),
|
||||||
@ -216,7 +215,6 @@ class ReplyItem extends StatelessWidget {
|
|||||||
|
|
||||||
// 感谢、回复、复制
|
// 感谢、回复、复制
|
||||||
Widget bottonAction(context, replyControl) {
|
Widget bottonAction(context, replyControl) {
|
||||||
var color = Theme.of(context).colorScheme.outline;
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 48),
|
const SizedBox(width: 48),
|
||||||
@ -297,13 +295,13 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
this.replies,
|
this.replies,
|
||||||
this.replyControl,
|
this.replyControl,
|
||||||
this.f_rpid,
|
// this.f_rpid,
|
||||||
this.replyItem,
|
this.replyItem,
|
||||||
this.replyReply,
|
this.replyReply,
|
||||||
});
|
});
|
||||||
List? replies;
|
List? replies;
|
||||||
ReplyControl? replyControl;
|
ReplyControl? replyControl;
|
||||||
int? f_rpid;
|
// int? f_rpid;
|
||||||
ReplyItemModel? replyItem;
|
ReplyItemModel? replyItem;
|
||||||
Function? replyReply;
|
Function? replyReply;
|
||||||
|
|
||||||
@ -361,7 +359,7 @@ class ReplyItemRow extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (replies![i].isUp)
|
if (replies![i].isUp)
|
||||||
WidgetSpan(
|
const WidgetSpan(
|
||||||
child: UpTag(),
|
child: UpTag(),
|
||||||
),
|
),
|
||||||
buildContent(
|
buildContent(
|
||||||
@ -439,16 +437,14 @@ InlineSpan buildContent(
|
|||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () => {
|
..onTap = () => Get.toNamed(
|
||||||
Get.toNamed(
|
'/webview',
|
||||||
'/webview',
|
parameters: {
|
||||||
parameters: {
|
'url': content.vote['url'],
|
||||||
'url': content.vote['url'],
|
'type': 'vote',
|
||||||
'type': 'vote',
|
'pageTitle': content.vote['title'],
|
||||||
'pageTitle': content.vote['title'],
|
},
|
||||||
},
|
),
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return '';
|
return '';
|
||||||
@ -554,11 +550,9 @@ InlineSpan buildContent(
|
|||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () => {
|
..onTap = () => Get.toNamed('/searchResult', parameters: {
|
||||||
Get.toNamed('/searchResult', parameters: {
|
'keyword': content.jumpUrl[matchStr]['title']
|
||||||
'keyword': content.jumpUrl[matchStr]['title']
|
}),
|
||||||
})
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
spanChilds.add(
|
spanChilds.add(
|
||||||
@ -718,16 +712,14 @@ InlineSpan buildContent(
|
|||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () => {
|
..onTap = () => Get.toNamed(
|
||||||
Get.toNamed(
|
'/webview',
|
||||||
'/webview',
|
parameters: {
|
||||||
parameters: {
|
'url': content.richText['note']['click_url'],
|
||||||
'url': content.richText['note']['click_url'],
|
'type': 'note',
|
||||||
'type': 'note',
|
'pageTitle': '笔记预览'
|
||||||
'pageTitle': '笔记预览'
|
},
|
||||||
},
|
),
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -736,8 +728,8 @@ InlineSpan buildContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UpTag extends StatelessWidget {
|
class UpTag extends StatelessWidget {
|
||||||
String? tagText;
|
final String? tagText;
|
||||||
UpTag({super.key, this.tagText = 'UP'});
|
const UpTag({super.key, this.tagText = 'UP'});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Color primary = Theme.of(context).colorScheme.primary;
|
Color primary = Theme.of(context).colorScheme.primary;
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import 'package:pilipala/models/common/reply_type.dart';
|
|||||||
import 'package:pilipala/models/video/reply/item.dart';
|
import 'package:pilipala/models/video/reply/item.dart';
|
||||||
|
|
||||||
class ZanButton extends StatefulWidget {
|
class ZanButton extends StatefulWidget {
|
||||||
ZanButton({
|
const ZanButton({
|
||||||
super.key,
|
super.key,
|
||||||
this.replyItem,
|
this.replyItem,
|
||||||
this.replyType,
|
this.replyType,
|
||||||
});
|
});
|
||||||
|
|
||||||
ReplyItemModel? replyItem;
|
final ReplyItemModel? replyItem;
|
||||||
final ReplyType? replyType;
|
final ReplyType? replyType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -10,13 +9,14 @@ import 'package:pilipala/models/video/reply/item.dart';
|
|||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
class VideoReplyNewDialog extends StatefulWidget {
|
class VideoReplyNewDialog extends StatefulWidget {
|
||||||
int? oid;
|
final int? oid;
|
||||||
int? root;
|
final int? root;
|
||||||
int? parent;
|
final int? parent;
|
||||||
ReplyType? replyType;
|
final ReplyType? replyType;
|
||||||
ReplyItemModel? replyItem;
|
final ReplyItemModel? replyItem;
|
||||||
|
|
||||||
VideoReplyNewDialog({
|
const VideoReplyNewDialog({
|
||||||
|
super.key,
|
||||||
this.oid,
|
this.oid,
|
||||||
this.root,
|
this.root,
|
||||||
this.parent,
|
this.parent,
|
||||||
@ -56,7 +56,9 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
|
|
||||||
_autoFocus() async {
|
_autoFocus() async {
|
||||||
await Future.delayed(const Duration(milliseconds: 300));
|
await Future.delayed(const Duration(milliseconds: 300));
|
||||||
FocusScope.of(context).requestFocus(replyContentFocusNode);
|
if (context.mounted) {
|
||||||
|
FocusScope.of(context).requestFocus(replyContentFocusNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_printLatestValue() {
|
_printLatestValue() {
|
||||||
@ -91,9 +93,8 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
super.didChangeMetrics();
|
super.didChangeMetrics();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
// 键盘高度
|
// 键盘高度
|
||||||
final viewInsets = EdgeInsets.fromWindowPadding(
|
final viewInsets = EdgeInsets.fromViewPadding(
|
||||||
WidgetsBinding.instance.window.viewInsets,
|
View.of(context).viewInsets, View.of(context).devicePixelRatio);
|
||||||
WidgetsBinding.instance.window.devicePixelRatio);
|
|
||||||
_debouncer.run(() {
|
_debouncer.run(() {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -197,7 +198,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void DebounceCallback();
|
typedef DebounceCallback = void Function();
|
||||||
|
|
||||||
class Debouncer {
|
class Debouncer {
|
||||||
DebounceCallback? callback;
|
DebounceCallback? callback;
|
||||||
|
|||||||
@ -81,7 +81,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
|
|||||||
if (widget.source == 'videoDetail')
|
if (widget.source == 'videoDetail')
|
||||||
Container(
|
Container(
|
||||||
height: 45,
|
height: 45,
|
||||||
padding: const EdgeInsets.only(left: 14, right: 14),
|
padding: const EdgeInsets.only(left: 12, right: 2),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
import 'package:pilipala/common/widgets/sliver_header.dart';
|
||||||
|
import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart';
|
||||||
import 'package:pilipala/pages/video/detail/reply/index.dart';
|
import 'package:pilipala/pages/video/detail/reply/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/controller.dart';
|
import 'package:pilipala/pages/video/detail/controller.dart';
|
||||||
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
import 'package:pilipala/pages/video/detail/introduction/index.dart';
|
||||||
@ -171,8 +173,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
controller: _meeduPlayerController!,
|
controller: _meeduPlayerController!,
|
||||||
header: (BuildContext context,
|
header: (BuildContext context,
|
||||||
MeeduPlayerController
|
MeeduPlayerController
|
||||||
_meeduPlayerController,
|
meeduPlayerController,
|
||||||
Responsive) {
|
Responsive responsive) {
|
||||||
return AppBar(
|
return AppBar(
|
||||||
toolbarHeight: 40,
|
toolbarHeight: 40,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
@ -230,7 +232,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 45,
|
height: 0,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
@ -250,8 +252,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
() => TabBar(
|
() => TabBar(
|
||||||
controller: videoDetailController.tabCtr,
|
controller: videoDetailController.tabCtr,
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
// indicatorColor:
|
indicatorColor:
|
||||||
// Theme.of(context).colorScheme.background,
|
Theme.of(context).colorScheme.background,
|
||||||
tabs: videoDetailController.tabs
|
tabs: videoDetailController.tabs
|
||||||
.map((String name) => Tab(text: name))
|
.map((String name) => Tab(text: name))
|
||||||
.toList(),
|
.toList(),
|
||||||
@ -267,11 +269,20 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
children: [
|
children: [
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return const CustomScrollView(
|
return CustomScrollView(
|
||||||
key: PageStorageKey<String>('简介'),
|
key: const PageStorageKey<String>('简介'),
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
VideoIntroPanel(),
|
const VideoIntroPanel(),
|
||||||
RelatedVideoPanel(),
|
SliverPersistentHeader(
|
||||||
|
floating: true,
|
||||||
|
pinned: true,
|
||||||
|
delegate: SliverHeaderDelegate(
|
||||||
|
height: 50,
|
||||||
|
child:
|
||||||
|
const MenuRow(loadingStatus: false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const RelatedVideoPanel(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -296,6 +307,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
|
|||||||
snapshot.data!.toDouble(),
|
snapshot.data!.toDouble(),
|
||||||
continuePlay,
|
continuePlay,
|
||||||
playerStatus,
|
playerStatus,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,14 +3,15 @@ import 'package:flutter_meedu_media_kit/meedu_player.dart';
|
|||||||
|
|
||||||
class ScrollAppBar extends StatelessWidget {
|
class ScrollAppBar extends StatelessWidget {
|
||||||
final double scrollVal;
|
final double scrollVal;
|
||||||
Function callback;
|
final Function callback;
|
||||||
final PlayerStatus playerStatus;
|
final PlayerStatus playerStatus;
|
||||||
|
|
||||||
ScrollAppBar(
|
const ScrollAppBar(
|
||||||
this.scrollVal,
|
this.scrollVal,
|
||||||
this.callback,
|
this.callback,
|
||||||
this.playerStatus,
|
this.playerStatus,
|
||||||
);
|
Key? key,
|
||||||
|
) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@ -1,16 +1,20 @@
|
|||||||
|
// ignore_for_file: library_private_types_in_public_api
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class ExpandedSection extends StatefulWidget {
|
class ExpandedSection extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget? child;
|
||||||
final bool expand;
|
final bool expand;
|
||||||
double begin = 0.0;
|
final double begin;
|
||||||
double end = 1.0;
|
final double end;
|
||||||
|
|
||||||
ExpandedSection(
|
const ExpandedSection({
|
||||||
{this.expand = false,
|
super.key,
|
||||||
required this.child,
|
this.expand = false,
|
||||||
required this.begin,
|
this.child,
|
||||||
required this.end});
|
this.begin = 0.0,
|
||||||
|
this.end = 1.0,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_ExpandedSectionState createState() => _ExpandedSectionState();
|
_ExpandedSectionState createState() => _ExpandedSectionState();
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.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';
|
||||||
@ -5,7 +7,6 @@ import 'package:pilipala/http/constants.dart';
|
|||||||
import 'package:pilipala/http/init.dart';
|
import 'package:pilipala/http/init.dart';
|
||||||
import 'package:pilipala/http/user.dart';
|
import 'package:pilipala/http/user.dart';
|
||||||
import 'package:pilipala/pages/dynamics/index.dart';
|
import 'package:pilipala/pages/dynamics/index.dart';
|
||||||
import 'package:pilipala/pages/home/index.dart';
|
|
||||||
import 'package:pilipala/pages/mine/index.dart';
|
import 'package:pilipala/pages/mine/index.dart';
|
||||||
import 'package:pilipala/pages/rcmd/controller.dart';
|
import 'package:pilipala/pages/rcmd/controller.dart';
|
||||||
import 'package:pilipala/utils/cookie.dart';
|
import 'package:pilipala/utils/cookie.dart';
|
||||||
|
|||||||
@ -67,6 +67,6 @@ class Routes {
|
|||||||
// 用户中心
|
// 用户中心
|
||||||
GetPage(name: '/member', page: () => const MemberPage()),
|
GetPage(name: '/member', page: () => const MemberPage()),
|
||||||
// 二级回复
|
// 二级回复
|
||||||
GetPage(name: '/replyReply', page: () => VideoReplyReplyPanel()),
|
GetPage(name: '/replyReply', page: () => const VideoReplyReplyPanel()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
// 工具函数
|
// 工具函数
|
||||||
|
// ignore_for_file: non_constant_identifier_names
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
@ -52,7 +54,7 @@ class Utils {
|
|||||||
// 当前时间
|
// 当前时间
|
||||||
int time = (DateTime.now().millisecondsSinceEpoch / 1000).round();
|
int time = (DateTime.now().millisecondsSinceEpoch / 1000).round();
|
||||||
// 对比
|
// 对比
|
||||||
int _distance = (time - timeStamp).toInt();
|
int distance = (time - timeStamp).toInt();
|
||||||
// 当前年日期
|
// 当前年日期
|
||||||
String currentYearStr = 'MM月DD日 hh:mm';
|
String currentYearStr = 'MM月DD日 hh:mm';
|
||||||
String lastYearStr = 'YY年MM月DD日 hh:mm';
|
String lastYearStr = 'YY年MM月DD日 hh:mm';
|
||||||
@ -65,12 +67,12 @@ class Utils {
|
|||||||
toInt: false,
|
toInt: false,
|
||||||
formatType: formatType);
|
formatType: formatType);
|
||||||
}
|
}
|
||||||
if (_distance <= 60) {
|
if (distance <= 60) {
|
||||||
return '刚刚';
|
return '刚刚';
|
||||||
} else if (_distance <= 3600) {
|
} else if (distance <= 3600) {
|
||||||
return '${(_distance / 60).floor()}分钟前';
|
return '${(distance / 60).floor()}分钟前';
|
||||||
} else if (_distance <= 43200) {
|
} else if (distance <= 43200) {
|
||||||
return '${(_distance / 60 / 60).floor()}小时前';
|
return '${(distance / 60 / 60).floor()}小时前';
|
||||||
} else if (DateTime.fromMillisecondsSinceEpoch(time * 1000).year ==
|
} else if (DateTime.fromMillisecondsSinceEpoch(time * 1000).year ==
|
||||||
DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000).year) {
|
DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000).year) {
|
||||||
return CustomStamp_str(
|
return CustomStamp_str(
|
||||||
|
|||||||
Reference in New Issue
Block a user