Compare commits
14 Commits
feature-fa
...
feature-ch
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d2c6a122a | |||
| 7a78729a44 | |||
| 03e5e22fef | |||
| aa93ce0b89 | |||
| 0c365ad049 | |||
| 3d5c578fef | |||
| 0a22f0f543 | |||
| 77477ff4dd | |||
| b0c56feef5 | |||
| 191472d0c4 | |||
| 40c666e3d1 | |||
| 10d2995429 | |||
| 23c8b34189 | |||
| 932be48125 |
@ -22,20 +22,27 @@ class HttpError extends StatelessWidget {
|
|||||||
"assets/images/error.svg",
|
"assets/images/error.svg",
|
||||||
height: 200,
|
height: 200,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 30),
|
||||||
Text(
|
Text(
|
||||||
errMsg ?? '请求异常',
|
errMsg ?? '请求异常',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 20),
|
||||||
OutlinedButton.icon(
|
FilledButton.tonal(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
fn!();
|
fn!();
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.arrow_forward_outlined, size: 20),
|
style: ButtonStyle(
|
||||||
label: Text(btnText ?? '点击重试'),
|
backgroundColor: MaterialStateProperty.resolveWith((states) {
|
||||||
)
|
return Theme.of(context).colorScheme.primary.withAlpha(20);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
btnText ?? '点击重试',
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
|
|||||||
|
|
||||||
class StatDanMu extends StatelessWidget {
|
class StatDanMu extends StatelessWidget {
|
||||||
final String? theme;
|
final String? theme;
|
||||||
final int? danmu;
|
final dynamic danmu;
|
||||||
final String? size;
|
final String? size;
|
||||||
|
|
||||||
const StatDanMu({Key? key, this.theme, this.danmu, this.size})
|
const StatDanMu({Key? key, this.theme, this.danmu, this.size})
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart';
|
|||||||
|
|
||||||
class StatView extends StatelessWidget {
|
class StatView extends StatelessWidget {
|
||||||
final String? theme;
|
final String? theme;
|
||||||
final int? view;
|
final dynamic view;
|
||||||
final String? size;
|
final String? size;
|
||||||
|
|
||||||
const StatView({Key? key, this.theme, this.view, this.size})
|
const StatView({Key? key, this.theme, this.view, this.size})
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
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';
|
||||||
|
import '../../models/model_rec_video_item.dart';
|
||||||
|
import 'stat/danmu.dart';
|
||||||
|
import 'stat/view.dart';
|
||||||
import '../../http/dynamics.dart';
|
import '../../http/dynamics.dart';
|
||||||
import '../../http/search.dart';
|
import '../../http/search.dart';
|
||||||
import '../../http/user.dart';
|
import '../../http/user.dart';
|
||||||
@ -322,19 +325,31 @@ class VideoStat extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RichText(
|
return Row(
|
||||||
|
children: [
|
||||||
|
StatView(
|
||||||
|
theme: 'gray',
|
||||||
|
view: videoItem.stat.view,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
StatDanMu(
|
||||||
|
theme: 'gray',
|
||||||
|
danmu: videoItem.stat.danmu,
|
||||||
|
),
|
||||||
|
if (videoItem is RecVideoItemModel) ...<Widget>[
|
||||||
|
const Spacer(),
|
||||||
|
RichText(
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: MediaQuery.textScalerOf(context)
|
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||||
.scale(Theme.of(context).textTheme.labelSmall!.fontSize!),
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
),
|
||||||
children: [
|
text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)),
|
||||||
TextSpan(text: '${Utils.numFormat(videoItem.stat.view)}观看'),
|
|
||||||
TextSpan(text: ' • ${Utils.numFormat(videoItem.stat.danmu)}弹幕'),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
]
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,7 +130,7 @@ class VideoHttp {
|
|||||||
}
|
}
|
||||||
return {'status': true, 'data': list};
|
return {'status': true, 'data': list};
|
||||||
} else {
|
} else {
|
||||||
return {'status': false, 'data': []};
|
return {'status': false, 'data': [], 'msg': res.data['message']};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {'status': false, 'data': [], 'msg': err};
|
return {'status': false, 'data': [], 'msg': err};
|
||||||
|
|||||||
@ -34,6 +34,7 @@ class PlayUrlModel {
|
|||||||
String? seekParam;
|
String? seekParam;
|
||||||
String? seekType;
|
String? seekType;
|
||||||
Dash? dash;
|
Dash? dash;
|
||||||
|
List<Durl>? durl;
|
||||||
List<FormatItem>? supportFormats;
|
List<FormatItem>? supportFormats;
|
||||||
// String? highFormat;
|
// String? highFormat;
|
||||||
int? lastPlayTime;
|
int? lastPlayTime;
|
||||||
@ -52,7 +53,8 @@ class PlayUrlModel {
|
|||||||
videoCodecid = json['video_codecid'];
|
videoCodecid = json['video_codecid'];
|
||||||
seekParam = json['seek_param'];
|
seekParam = json['seek_param'];
|
||||||
seekType = json['seek_type'];
|
seekType = json['seek_type'];
|
||||||
dash = Dash.fromJson(json['dash']);
|
dash = json['dash'] != null ? Dash.fromJson(json['dash']) : null;
|
||||||
|
durl = json['durl']?.map<Durl>((e) => Durl.fromJson(e)).toList();
|
||||||
supportFormats = json['support_formats'] != null
|
supportFormats = json['support_formats'] != null
|
||||||
? json['support_formats']
|
? json['support_formats']
|
||||||
.map<FormatItem>((e) => FormatItem.fromJson(e))
|
.map<FormatItem>((e) => FormatItem.fromJson(e))
|
||||||
@ -250,3 +252,30 @@ class Flac {
|
|||||||
audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null;
|
audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Durl {
|
||||||
|
Durl({
|
||||||
|
this.order,
|
||||||
|
this.length,
|
||||||
|
this.size,
|
||||||
|
this.ahead,
|
||||||
|
this.vhead,
|
||||||
|
this.url,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? order;
|
||||||
|
int? length;
|
||||||
|
int? size;
|
||||||
|
String? ahead;
|
||||||
|
String? vhead;
|
||||||
|
String? url;
|
||||||
|
|
||||||
|
Durl.fromJson(Map<String, dynamic> json) {
|
||||||
|
order = json['order'];
|
||||||
|
length = json['length'];
|
||||||
|
size = json['size'];
|
||||||
|
ahead = json['ahead'];
|
||||||
|
vhead = json['vhead'];
|
||||||
|
url = json['url'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -9,7 +9,6 @@ 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/home/index.dart';
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/main/index.dart';
|
import 'package:pilipala/pages/main/index.dart';
|
||||||
import 'package:pilipala/pages/rcmd/view.dart';
|
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/bangumu_card_v.dart';
|
import 'widgets/bangumu_card_v.dart';
|
||||||
@ -199,7 +198,10 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => {},
|
fn: () {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_bangumidController.queryBangumiListFeed();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -208,7 +210,6 @@ class _BangumiPageState extends State<BangumiPage>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const LoadingMore()
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -192,22 +192,6 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// Obx(
|
|
||||||
// () => Visibility(
|
|
||||||
// visible: _dynamicsController.userLogin.value,
|
|
||||||
// child: Positioned(
|
|
||||||
// right: 4,
|
|
||||||
// top: 0,
|
|
||||||
// bottom: 0,
|
|
||||||
// child: IconButton(
|
|
||||||
// padding: EdgeInsets.zero,
|
|
||||||
// onPressed: () =>
|
|
||||||
// {feedBack(), _dynamicsController.resetSearch()},
|
|
||||||
// icon: const Icon(Icons.history, size: 21),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -229,7 +213,8 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
return Obx(() => UpPanel(_dynamicsController.upData.value));
|
return Obx(() => UpPanel(_dynamicsController.upData.value));
|
||||||
} else {
|
} else {
|
||||||
return const SliverToBoxAdapter(
|
return const SliverToBoxAdapter(
|
||||||
child: SizedBox(height: 80));
|
child: SizedBox(height: 80),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return const SliverToBoxAdapter(
|
return const SliverToBoxAdapter(
|
||||||
@ -240,15 +225,6 @@ class _DynamicsPageState extends State<DynamicsPage>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: Container(
|
|
||||||
height: 6,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.onInverseSurface
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _futureBuilderFuture,
|
future: _futureBuilderFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
|||||||
@ -36,8 +36,7 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
}
|
}
|
||||||
upList.insert(
|
upList.insert(
|
||||||
0,
|
0,
|
||||||
UpItem(
|
UpItem(face: '', uname: '全部动态', mid: -1),
|
||||||
face: 'https://files.catbox.moe/8uc48f.png', uname: '全部动态', mid: -1),
|
|
||||||
);
|
);
|
||||||
userInfo = userInfoCache.get('userInfoCache');
|
userInfo = userInfoCache.get('userInfoCache');
|
||||||
upList.insert(
|
upList.insert(
|
||||||
@ -56,7 +55,7 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
floating: true,
|
floating: true,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
delegate: _SliverHeaderDelegate(
|
delegate: _SliverHeaderDelegate(
|
||||||
height: 124,
|
height: 126,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -121,6 +120,13 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Container(
|
||||||
|
height: 6,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onInverseSurface
|
||||||
|
.withOpacity(0.5),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -171,6 +177,9 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
feedBack();
|
feedBack();
|
||||||
|
if (data.mid == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
String heroTag = Utils.makeHeroTag(data.mid);
|
String heroTag = Utils.makeHeroTag(data.mid);
|
||||||
Get.toNamed('/member?mid=${data.mid}',
|
Get.toNamed('/member?mid=${data.mid}',
|
||||||
arguments: {'face': data.face, 'heroTag': heroTag});
|
arguments: {'face': data.face, 'heroTag': heroTag});
|
||||||
@ -198,11 +207,18 @@ class _UpPanelState extends State<UpPanel> {
|
|||||||
backgroundColor: data.type == 'live'
|
backgroundColor: data.type == 'live'
|
||||||
? Theme.of(context).colorScheme.secondaryContainer
|
? Theme.of(context).colorScheme.secondaryContainer
|
||||||
: Theme.of(context).colorScheme.primary,
|
: Theme.of(context).colorScheme.primary,
|
||||||
child: NetworkImgLayer(
|
child: data.face != ''
|
||||||
width: 49,
|
? NetworkImgLayer(
|
||||||
height: 49,
|
width: 50,
|
||||||
|
height: 50,
|
||||||
src: data.face,
|
src: data.face,
|
||||||
type: 'avatar',
|
type: 'avatar',
|
||||||
|
)
|
||||||
|
: const CircleAvatar(
|
||||||
|
radius: 25,
|
||||||
|
backgroundImage: AssetImage(
|
||||||
|
'assets/images/noface.jpeg',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
@ -271,13 +287,11 @@ class UpPanelSkeleton extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 49,
|
width: 50,
|
||||||
height: 49,
|
height: 50,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
borderRadius: const BorderRadius.all(
|
borderRadius: BorderRadius.circular(50),
|
||||||
Radius.circular(24),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
|||||||
@ -89,8 +89,7 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
if (data['status']) {
|
if (data['status']) {
|
||||||
return Obx(
|
return Obx(
|
||||||
() => SliverList(
|
() => SliverList(
|
||||||
delegate:
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
SliverChildBuilderDelegate((context, index) {
|
|
||||||
return VideoCardH(
|
return VideoCardH(
|
||||||
videoItem: _hotController.videoList[index],
|
videoItem: _hotController.videoList[index],
|
||||||
showPubdate: true,
|
showPubdate: true,
|
||||||
@ -110,7 +109,12 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => setState(() {}),
|
fn: () {
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_hotController.queryHotFeed('init');
|
||||||
|
});
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -10,8 +10,7 @@ class LiveController extends GetxController {
|
|||||||
int count = 12;
|
int count = 12;
|
||||||
int _currentPage = 1;
|
int _currentPage = 1;
|
||||||
RxInt crossAxisCount = 2.obs;
|
RxInt crossAxisCount = 2.obs;
|
||||||
RxList<LiveItemModel> liveList = [LiveItemModel()].obs;
|
RxList<LiveItemModel> liveList = <LiveItemModel>[].obs;
|
||||||
bool isLoadingMore = false;
|
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
OverlayEntry? popupDialog;
|
OverlayEntry? popupDialog;
|
||||||
Box setting = GStrorage.setting;
|
Box setting = GStrorage.setting;
|
||||||
@ -39,7 +38,6 @@ class LiveController extends GetxController {
|
|||||||
}
|
}
|
||||||
_currentPage += 1;
|
_currentPage += 1;
|
||||||
}
|
}
|
||||||
isLoadingMore = false;
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import 'package:pilipala/common/widgets/http_error.dart';
|
|||||||
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
import 'package:pilipala/common/widgets/overlay_pop.dart';
|
||||||
import 'package:pilipala/pages/home/index.dart';
|
import 'package:pilipala/pages/home/index.dart';
|
||||||
import 'package:pilipala/pages/main/index.dart';
|
import 'package:pilipala/pages/main/index.dart';
|
||||||
import 'package:pilipala/pages/rcmd/index.dart';
|
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'widgets/live_item.dart';
|
import 'widgets/live_item.dart';
|
||||||
@ -45,8 +44,8 @@ class _LivePageState extends State<LivePage>
|
|||||||
() {
|
() {
|
||||||
if (scrollController.position.pixels >=
|
if (scrollController.position.pixels >=
|
||||||
scrollController.position.maxScrollExtent - 200) {
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
EasyThrottle.throttle('liveList', const Duration(seconds: 1), () {
|
EasyThrottle.throttle('liveList', const Duration(milliseconds: 200),
|
||||||
_liveController.isLoadingMore = true;
|
() {
|
||||||
_liveController.onLoad();
|
_liveController.onLoad();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -108,24 +107,20 @@ class _LivePageState extends State<LivePage>
|
|||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => {},
|
fn: () {
|
||||||
|
setState(() {
|
||||||
|
_futureBuilderFuture =
|
||||||
|
_liveController.queryLiveList('init');
|
||||||
|
});
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 缓存数据
|
|
||||||
if (_liveController.liveList.length > 1) {
|
|
||||||
return contentGrid(
|
|
||||||
_liveController, _liveController.liveList);
|
|
||||||
}
|
|
||||||
// 骨架屏
|
|
||||||
else {
|
|
||||||
return contentGrid(_liveController, []);
|
return contentGrid(_liveController, []);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LoadingMore(ctr: _liveController)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -44,7 +44,7 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
if (scrollController.position.pixels >=
|
if (scrollController.position.pixels >=
|
||||||
scrollController.position.maxScrollExtent - 200) {
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
EasyThrottle.throttle(
|
EasyThrottle.throttle(
|
||||||
'my-throttler', const Duration(milliseconds: 500), () {
|
'my-throttler', const Duration(milliseconds: 200), () {
|
||||||
_rcmdController.isLoadingMore = true;
|
_rcmdController.isLoadingMore = true;
|
||||||
_rcmdController.onLoad();
|
_rcmdController.onLoad();
|
||||||
});
|
});
|
||||||
@ -113,6 +113,7 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () {
|
fn: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
_rcmdController.isLoadingMore = true;
|
||||||
_futureBuilderFuture =
|
_futureBuilderFuture =
|
||||||
_rcmdController.queryRcmdFeed('init');
|
_rcmdController.queryRcmdFeed('init');
|
||||||
});
|
});
|
||||||
@ -125,7 +126,6 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LoadingMore(ctr: _rcmdController),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -188,33 +188,3 @@ class _RcmdPageState extends State<RcmdPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadingMore extends StatelessWidget {
|
|
||||||
final dynamic ctr;
|
|
||||||
const LoadingMore({super.key, this.ctr});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SliverToBoxAdapter(
|
|
||||||
child: Container(
|
|
||||||
height: MediaQuery.of(context).padding.bottom + 80,
|
|
||||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (ctr != null) {
|
|
||||||
ctr!.isLoadingMore = true;
|
|
||||||
ctr!.onLoad();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
'点击加载更多 👇',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.outline, fontSize: 13),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -187,9 +187,13 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return HttpError(
|
return CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => setState(() {}),
|
fn: () => setState(() {}),
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -105,7 +105,11 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
slivers: [
|
slivers: [
|
||||||
HttpError(
|
HttpError(
|
||||||
errMsg: data['msg'],
|
errMsg: data['msg'],
|
||||||
fn: () => setState(() {}),
|
fn: () {
|
||||||
|
setState(() {
|
||||||
|
_searchPanelController.onSearch();
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -116,7 +120,11 @@ class _SearchPanelState extends State<SearchPanel>
|
|||||||
slivers: [
|
slivers: [
|
||||||
HttpError(
|
HttpError(
|
||||||
errMsg: '没有相关数据',
|
errMsg: '没有相关数据',
|
||||||
fn: () => setState(() {}),
|
fn: () {
|
||||||
|
setState(() {
|
||||||
|
_searchPanelController.onSearch();
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class SearchVideoPanel extends StatelessWidget {
|
|||||||
padding: index == 0
|
padding: index == 0
|
||||||
? const EdgeInsets.only(top: 2)
|
? const EdgeInsets.only(top: 2)
|
||||||
: EdgeInsets.zero,
|
: EdgeInsets.zero,
|
||||||
child: VideoCardH(videoItem: i),
|
child: VideoCardH(videoItem: i, showPubdate: true),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -70,7 +70,7 @@ class SearchVideoPanel extends StatelessWidget {
|
|||||||
controller.selectedType.value = i['type'];
|
controller.selectedType.value = i['type'];
|
||||||
ctr!.order.value =
|
ctr!.order.value =
|
||||||
i['type'].toString().split('.').last;
|
i['type'].toString().split('.').last;
|
||||||
SmartDialog.showLoading(msg: 'loooad');
|
SmartDialog.showLoading(msg: 'loading');
|
||||||
await ctr!.onRefresh();
|
await ctr!.onRefresh();
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
@ -202,7 +202,7 @@ class VideoPanelController extends GetxController {
|
|||||||
Get.find<SearchPanelController>(
|
Get.find<SearchPanelController>(
|
||||||
tag: 'video${searchPanelCtr.keyword!}');
|
tag: 'video${searchPanelCtr.keyword!}');
|
||||||
ctr.duration.value = i['value'];
|
ctr.duration.value = i['value'];
|
||||||
SmartDialog.showLoading(msg: 'loooad');
|
SmartDialog.showLoading(msg: 'loading');
|
||||||
await ctr.onRefresh();
|
await ctr.onRefresh();
|
||||||
SmartDialog.dismiss();
|
SmartDialog.dismiss();
|
||||||
},
|
},
|
||||||
|
|||||||
@ -229,9 +229,11 @@ class VideoDetailController extends GetxController
|
|||||||
seekTo: seekToTime ?? defaultST,
|
seekTo: seekToTime ?? defaultST,
|
||||||
duration: duration ?? Duration(milliseconds: data.timeLength ?? 0),
|
duration: duration ?? Duration(milliseconds: data.timeLength ?? 0),
|
||||||
// 宽>高 水平 否则 垂直
|
// 宽>高 水平 否则 垂直
|
||||||
direction: (firstVideo.width! - firstVideo.height!) > 0
|
direction: firstVideo.width != null && firstVideo.height != null
|
||||||
|
? ((firstVideo.width! - firstVideo.height!) > 0
|
||||||
? 'horizontal'
|
? 'horizontal'
|
||||||
: 'vertical',
|
: 'vertical')
|
||||||
|
: null,
|
||||||
bvid: bvid,
|
bvid: bvid,
|
||||||
cid: cid.value,
|
cid: cid.value,
|
||||||
enableHeart: enableHeart,
|
enableHeart: enableHeart,
|
||||||
@ -248,6 +250,21 @@ class VideoDetailController extends GetxController
|
|||||||
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid);
|
||||||
if (result['status']) {
|
if (result['status']) {
|
||||||
data = result['data'];
|
data = result['data'];
|
||||||
|
if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) {
|
||||||
|
SmartDialog.showToast(
|
||||||
|
'该视频为专属视频,仅提供试看',
|
||||||
|
displayTime: const Duration(seconds: 3),
|
||||||
|
);
|
||||||
|
videoUrl = data.durl!.first.url!;
|
||||||
|
audioUrl = '';
|
||||||
|
defaultST = Duration.zero;
|
||||||
|
firstVideo = VideoItem();
|
||||||
|
if (autoPlay.value) {
|
||||||
|
await playerInit();
|
||||||
|
isShowCover.value = false;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
final List<VideoItem> allVideosList = data.dash!.video!;
|
final List<VideoItem> allVideosList = data.dash!.video!;
|
||||||
try {
|
try {
|
||||||
// 当前可播放的最高质量视频
|
// 当前可播放的最高质量视频
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import 'package:pilipala/utils/id_utils.dart';
|
|||||||
import 'package:pilipala/utils/storage.dart';
|
import 'package:pilipala/utils/storage.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
import '../related/index.dart';
|
||||||
import 'widgets/group_panel.dart';
|
import 'widgets/group_panel.dart';
|
||||||
|
|
||||||
class VideoIntroController extends GetxController {
|
class VideoIntroController extends GetxController {
|
||||||
@ -478,11 +479,15 @@ class VideoIntroController extends GetxController {
|
|||||||
// 重新获取视频资源
|
// 重新获取视频资源
|
||||||
final VideoDetailController videoDetailCtr =
|
final VideoDetailController videoDetailCtr =
|
||||||
Get.find<VideoDetailController>(tag: heroTag);
|
Get.find<VideoDetailController>(tag: heroTag);
|
||||||
|
final ReleatedController releatedCtr =
|
||||||
|
Get.find<ReleatedController>(tag: heroTag);
|
||||||
videoDetailCtr.bvid = bvid;
|
videoDetailCtr.bvid = bvid;
|
||||||
videoDetailCtr.oid.value = aid;
|
videoDetailCtr.oid.value = aid;
|
||||||
videoDetailCtr.cid.value = cid;
|
videoDetailCtr.cid.value = cid;
|
||||||
videoDetailCtr.danmakuCid.value = cid;
|
videoDetailCtr.danmakuCid.value = cid;
|
||||||
videoDetailCtr.queryVideoUrl();
|
videoDetailCtr.queryVideoUrl();
|
||||||
|
releatedCtr.bvid = bvid;
|
||||||
|
releatedCtr.queryRelatedVideo();
|
||||||
// 重新请求评论
|
// 重新请求评论
|
||||||
try {
|
try {
|
||||||
/// 未渲染回复组件时可能异常
|
/// 未渲染回复组件时可能异常
|
||||||
|
|||||||
@ -1,14 +1,22 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/http/video.dart';
|
import 'package:pilipala/http/video.dart';
|
||||||
|
import '../../../../models/model_hot_video_item.dart';
|
||||||
|
|
||||||
class ReleatedController extends GetxController {
|
class ReleatedController extends GetxController {
|
||||||
// 视频aid
|
// 视频aid
|
||||||
String bvid = Get.parameters['bvid'] ?? "";
|
String bvid = Get.parameters['bvid'] ?? "";
|
||||||
// 推荐视频列表
|
// 推荐视频列表
|
||||||
List relatedVideoList = [];
|
RxList relatedVideoList = <HotVideoItemModel>[].obs;
|
||||||
|
|
||||||
OverlayEntry? popupDialog;
|
OverlayEntry? popupDialog;
|
||||||
|
|
||||||
Future<dynamic> queryRelatedVideo() => VideoHttp.relatedVideoList(bvid: bvid);
|
Future<dynamic> queryRelatedVideo() async {
|
||||||
|
return VideoHttp.relatedVideoList(bvid: bvid).then((value) {
|
||||||
|
if (value['status']) {
|
||||||
|
relatedVideoList.value = value['data'];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,35 +7,58 @@ import 'package:pilipala/common/widgets/overlay_pop.dart';
|
|||||||
import 'package:pilipala/common/widgets/video_card_h.dart';
|
import 'package:pilipala/common/widgets/video_card_h.dart';
|
||||||
import './controller.dart';
|
import './controller.dart';
|
||||||
|
|
||||||
class RelatedVideoPanel extends StatelessWidget {
|
class RelatedVideoPanel extends StatefulWidget {
|
||||||
final ReleatedController _releatedController =
|
const RelatedVideoPanel({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RelatedVideoPanel> createState() => _RelatedVideoPanelState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RelatedVideoPanelState extends State<RelatedVideoPanel>
|
||||||
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
late ReleatedController _releatedController;
|
||||||
|
late Future _futureBuilder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_releatedController =
|
||||||
Get.put(ReleatedController(), tag: Get.arguments?['heroTag']);
|
Get.put(ReleatedController(), tag: Get.arguments?['heroTag']);
|
||||||
RelatedVideoPanel({super.key});
|
_futureBuilder = _releatedController.queryRelatedVideo();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: _releatedController.queryRelatedVideo(),
|
future: _futureBuilder,
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const SliverToBoxAdapter(child: SizedBox());
|
return const SliverToBoxAdapter(child: SizedBox());
|
||||||
}
|
}
|
||||||
if (snapshot.data!['status']) {
|
if (snapshot.data!['status'] && snapshot.data != null) {
|
||||||
|
RxList relatedVideoList = _releatedController.relatedVideoList;
|
||||||
// 请求成功
|
// 请求成功
|
||||||
return SliverList(
|
return Obx(
|
||||||
|
() => SliverList(
|
||||||
delegate: SliverChildBuilderDelegate((context, index) {
|
delegate: SliverChildBuilderDelegate((context, index) {
|
||||||
if (index == snapshot.data['data'].length) {
|
if (index == relatedVideoList.length) {
|
||||||
return SizedBox(height: MediaQuery.of(context).padding.bottom);
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).padding.bottom);
|
||||||
} else {
|
} else {
|
||||||
return Material(
|
return Material(
|
||||||
child: VideoCardH(
|
child: VideoCardH(
|
||||||
videoItem: snapshot.data['data'][index],
|
videoItem: relatedVideoList[index],
|
||||||
showPubdate: true,
|
showPubdate: true,
|
||||||
longPress: () {
|
longPress: () {
|
||||||
try {
|
try {
|
||||||
_releatedController.popupDialog =
|
_releatedController.popupDialog =
|
||||||
_createPopupDialog(snapshot.data['data'][index]);
|
_createPopupDialog(_releatedController
|
||||||
|
.relatedVideoList[index]);
|
||||||
Overlay.of(context)
|
Overlay.of(context)
|
||||||
.insert(_releatedController.popupDialog!);
|
.insert(_releatedController.popupDialog!);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -48,7 +71,9 @@ class RelatedVideoPanel extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, childCount: snapshot.data['data'].length + 1));
|
}, childCount: relatedVideoList.length + 1),
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// 请求错误
|
// 请求错误
|
||||||
return HttpError(errMsg: '出错了', fn: () {});
|
return HttpError(errMsg: '出错了', fn: () {});
|
||||||
|
|||||||
@ -63,6 +63,26 @@ class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 完全相对时间显示
|
||||||
|
static String formatTimestampToRelativeTime(timeStamp) {
|
||||||
|
var difference = DateTime.now()
|
||||||
|
.difference(DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000));
|
||||||
|
|
||||||
|
if (difference.inDays > 365) {
|
||||||
|
return '${difference.inDays ~/ 365}年前';
|
||||||
|
} else if (difference.inDays > 30) {
|
||||||
|
return '${difference.inDays ~/ 30}个月前';
|
||||||
|
} else if (difference.inDays > 0) {
|
||||||
|
return '${difference.inDays}天前';
|
||||||
|
} else if (difference.inHours > 0) {
|
||||||
|
return '${difference.inHours}小时前';
|
||||||
|
} else if (difference.inMinutes > 0) {
|
||||||
|
return '${difference.inMinutes}分钟前';
|
||||||
|
} else {
|
||||||
|
return '刚刚';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 时间显示,刚刚,x分钟前
|
// 时间显示,刚刚,x分钟前
|
||||||
static String dateFormat(timeStamp, {formatType = 'list'}) {
|
static String dateFormat(timeStamp, {formatType = 'list'}) {
|
||||||
// 当前时间
|
// 当前时间
|
||||||
|
|||||||
Reference in New Issue
Block a user