mod: 图标修改

This commit is contained in:
guozhigq
2023-07-22 15:51:16 +08:00
45 changed files with 1302 additions and 551 deletions

View File

@ -34,8 +34,6 @@ PODS:
- sqflite (0.0.2):
- Flutter
- FMDB (>= 2.7.5)
- url_launcher_ios (0.0.1):
- Flutter
- volume_controller (0.0.1):
- Flutter
- wakelock (0.0.1):
@ -60,7 +58,6 @@ DEPENDENCIES:
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
- webview_cookie_manager (from `.symlinks/plugins/webview_cookie_manager/ios`)
@ -100,8 +97,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
volume_controller:
:path: ".symlinks/plugins/volume_controller/ios"
wakelock:
@ -128,7 +123,6 @@ SPEC CHECKSUMS:
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7

View File

@ -0,0 +1,129 @@
import 'dart:math';
import 'dart:ui' as ui show Image;
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart';
double get maxDragOffset => 100;
double hideHeight = maxDragOffset / 2.3;
double refreshHeight = maxDragOffset / 1.5;
class PullToRefreshHeader extends StatelessWidget {
const PullToRefreshHeader(
this.info,
this.lastRefreshTime, {
this.color,
});
final PullToRefreshScrollNotificationInfo? info;
final DateTime? lastRefreshTime;
final Color? color;
@override
Widget build(BuildContext context) {
final PullToRefreshScrollNotificationInfo? _info = info;
if (_info == null) {
return Container();
}
String text = '';
if (_info.mode == PullToRefreshIndicatorMode.armed) {
text = 'Release to refresh';
} else if (_info.mode == PullToRefreshIndicatorMode.refresh ||
_info.mode == PullToRefreshIndicatorMode.snap) {
text = 'Loading...';
} else if (_info.mode == PullToRefreshIndicatorMode.done) {
text = 'Refresh completed.';
} else if (_info.mode == PullToRefreshIndicatorMode.drag) {
text = 'Pull to refresh';
} else if (_info.mode == PullToRefreshIndicatorMode.canceled) {
text = 'Cancel refresh';
}
final TextStyle ts = const TextStyle(
color: Colors.grey,
).copyWith(fontSize: 14);
final double dragOffset = info?.dragOffset ?? 0.0;
final DateTime time = lastRefreshTime ?? DateTime.now();
final double top = -hideHeight + dragOffset;
return Container(
height: dragOffset,
color: color ?? Colors.transparent,
// padding: EdgeInsets.only(top: dragOffset / 3),
// padding: EdgeInsets.only(bottom: 5.0),
child: Stack(
children: <Widget>[
Positioned(
left: 0.0,
right: 0.0,
top: top,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
alignment: Alignment.centerRight,
child: RefreshImage(top),
margin: const EdgeInsets.only(right: 12.0),
),
),
Column(
children: <Widget>[
Text(text, style: ts),
Text(
'Last updated:' +
DateFormat('yyyy-MM-dd hh:mm').format(time),
style: ts.copyWith(fontSize: 14),
)
],
),
const Spacer(),
],
),
)
],
),
);
}
}
class RefreshImage extends StatelessWidget {
const RefreshImage(this.top);
final double top;
@override
Widget build(BuildContext context) {
const double imageSize = 30;
return ExtendedImage.asset(
'assets/flutterCandies_grey.png',
width: imageSize,
height: imageSize,
afterPaintImage: (Canvas canvas, Rect rect, ui.Image image, Paint paint) {
final double imageHeight = image.height.toDouble();
final double imageWidth = image.width.toDouble();
final Size size = rect.size;
final double y =
(1 - min(top / (refreshHeight - hideHeight), 1)) * imageHeight;
canvas.drawImageRect(
image,
Rect.fromLTWH(0.0, y, imageWidth, imageHeight - y),
Rect.fromLTWH(rect.left, rect.top + y / imageHeight * size.height,
size.width, (imageHeight - y) / imageHeight * size.height),
Paint()
..colorFilter =
const ColorFilter.mode(Color(0xFFea5504), BlendMode.srcIn)
..isAntiAlias = false
..filterQuality = FilterQuality.low,
);
//canvas.restore();
},
);
}
}

View File

@ -12,13 +12,17 @@ class StatDanMu extends StatelessWidget {
@override
Widget build(BuildContext context) {
Color color =
theme == 'white' ? Colors.white : Theme.of(context).colorScheme.outline;
Map<String, Color> colorObject = {
'white': Colors.white,
'gray': Theme.of(context).colorScheme.outline,
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
};
Color color = colorObject[theme]!;
return Row(
children: [
Icon(
// CupertinoIcons.ellipses_bubble,
Icons.subtitles_outlined,
Icons.subtitles_sharp,
size: 14,
color: color,
),

View File

@ -12,13 +12,17 @@ class StatView extends StatelessWidget {
@override
Widget build(BuildContext context) {
Color color =
theme == 'white' ? Colors.white : Theme.of(context).colorScheme.outline;
Map<String, Color> colorObject = {
'white': Colors.white,
'gray': Theme.of(context).colorScheme.outline,
'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8),
};
Color color = colorObject[theme]!;
return Row(
children: [
Icon(
// CupertinoIcons.play_rectangle,
Icons.play_circle_outlined,
Icons.play_circle_fill_outlined,
size: 13,
color: color,
),

View File

@ -80,22 +80,22 @@ class VideoCardV extends StatelessWidget {
height: maxHeight,
),
),
if (videoItem.stat.view is int &&
videoItem.stat.danmaku is int)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: AnimatedOpacity(
opacity: 1,
duration: const Duration(milliseconds: 200),
child: VideoStat(
view: videoItem.stat.view,
danmaku: videoItem.stat.danmaku,
duration: videoItem.duration,
),
),
),
// if (videoItem.stat.view is int &&
// videoItem.stat.danmaku is int)
// Positioned(
// left: 0,
// right: 0,
// bottom: 0,
// child: AnimatedOpacity(
// opacity: 1,
// duration: const Duration(milliseconds: 200),
// child: VideoStat(
// view: videoItem.stat.view,
// danmaku: videoItem.stat.danmaku,
// duration: videoItem.duration,
// ),
// ),
// ),
],
);
}),
@ -118,7 +118,7 @@ class VideoContent extends StatelessWidget {
return Expanded(
child: Padding(
// 多列
padding: const EdgeInsets.fromLTRB(4, 5, 6, 8),
padding: const EdgeInsets.fromLTRB(4, 5, 6, 12),
// 单列
// padding: const EdgeInsets.fromLTRB(14, 10, 4, 8),
child: Column(
@ -136,71 +136,84 @@ class VideoContent extends StatelessWidget {
maxLines: Get.find<RcmdController>().crossAxisCount,
overflow: TextOverflow.ellipsis,
),
SizedBox(
height: 18,
child: Row(
children: [
if (videoItem.rcmdReason != null &&
videoItem.rcmdReason.content != '') ...[
Container(
padding: const EdgeInsets.fromLTRB(3, 1, 3, 1),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.primaryContainer
.withOpacity(0.6),
borderRadius: BorderRadius.circular(3)),
child: Text(
videoItem.rcmdReason.content,
style: TextStyle(
fontSize:
Theme.of(context).textTheme.labelSmall!.fontSize,
color: Theme.of(context).colorScheme.primary,
),
),
),
const SizedBox(width: 4)
] 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(
videoItem.owner.name,
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
);
}),
),
],
),
// SizedBox(
// height: 18,
// child: Row(
// children: [
// if (videoItem.rcmdReason != null &&
// videoItem.rcmdReason.content != '') ...[
// Container(
// padding: const EdgeInsets.fromLTRB(3, 1, 3, 1),
// decoration: BoxDecoration(
// color: Theme.of(context)
// .colorScheme
// .primaryContainer
// .withOpacity(0.6),
// borderRadius: BorderRadius.circular(3)),
// child: Text(
// videoItem.rcmdReason.content,
// style: TextStyle(
// fontSize:
// Theme.of(context).textTheme.labelSmall!.fontSize,
// color: Theme.of(context).colorScheme.primary,
// ),
// ),
// ),
// const SizedBox(width: 4)
// ] 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(
// 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,
),
],
),
],
),

View File

@ -203,4 +203,19 @@ class Api {
// 用户名片信息
static const String memberCardInfo = '/x/web-interface/card';
// 用户投稿
// https://api.bilibili.com/x/space/wbi/arc/search?
// mid=85754245&
// ps=30&
// tid=0&
// pn=1&
// keyword=&
// order=pubdate&
// platform=web&
// web_location=1550101&
// order_avoided=true&
// w_rid=d893cf98a4e010cf326373194a648360&
// wts=1689767832
static const String memberArchive = '/x/space/wbi/arc/search';
}

View File

@ -1,9 +1,23 @@
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/member/archive.dart';
import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/utils/wbi_sign.dart';
class MemberHttp {
static Future memberInfo({String? params}) async {
var res = await Request().get(Api.memberInfo + params!);
static Future memberInfo({
int? mid,
String token = '',
}) async {
Map params = await WbiSign().makSign({
'mid': mid,
'token': token,
'platform': 'web',
'web_location': 1550101,
});
var res = await Request().get(
Api.memberInfo,
data: params,
);
if (res.data['code'] == 0) {
return {
'status': true,
@ -44,4 +58,42 @@ class MemberHttp {
};
}
}
static Future memberArchive({
int? mid,
int ps = 30,
int tid = 0,
int? pn,
String keyword = '',
String order = 'pubdate',
bool orderAvoided = true,
}) async {
Map params = await WbiSign().makSign({
'mid': mid,
'ps': ps,
'tid': tid,
'pn': pn,
'keyword': keyword,
'order': order,
'platform': 'web',
'web_location': 1550101,
'order_avoided': orderAvoided
});
var res = await Request().get(
Api.memberArchive,
data: params,
);
if (res.data['code'] == 0) {
return {
'status': true,
'data': MemberArchiveDataModel.fromJson(res.data['data'])
};
} else {
return {
'status': false,
'data': [],
'msg': res.data['message'],
};
}
}
}

View File

@ -23,7 +23,7 @@ class ReplyHttp {
Map errMap = {
-400: '请求错误',
-404: '无此项',
12002: '评论区已关闭',
12002: '当前页面评论功能已关闭"',
12009: '评论主体的type不合法',
12061: 'UP主已关闭评论区',
};
@ -48,6 +48,7 @@ class ReplyHttp {
'pn': pageNum,
'type': type,
'sort': 1,
'csrf': await Request.getCsrf(),
});
if (res.data['code'] == 0) {
return {

View File

@ -4,6 +4,8 @@ enum ReplyType {
video,
// 话题
topic,
//
unset2,
// 活动
activity,
// 小视频

View File

@ -0,0 +1,164 @@
class MemberArchiveDataModel {
MemberArchiveDataModel({
this.list,
this.page,
});
ArchiveListModel? list;
Map? page;
MemberArchiveDataModel.fromJson(Map<String, dynamic> json) {
list = ArchiveListModel.fromJson(json['list']);
page = json['page'];
}
}
class ArchiveListModel {
ArchiveListModel({
this.tlist,
this.vlist,
});
Map<String, TListItemModel>? tlist;
List<VListItemModel>? vlist;
ArchiveListModel.fromJson(Map<String, dynamic> json) {
tlist = json['tlist'] != null
? Map.from(json['tlist']).map((k, v) =>
MapEntry<String, TListItemModel>(k, TListItemModel.fromJson(v)))
: {};
vlist = json['vlist']
.map<VListItemModel>((e) => VListItemModel.fromJson(e))
.toList();
}
}
class TListItemModel {
TListItemModel({
this.tid,
this.count,
this.name,
});
int? tid;
int? count;
String? name;
TListItemModel.fromJson(Map<String, dynamic> json) {
tid = json['tid'];
count = json['count'];
name = json['name'];
}
}
class VListItemModel {
VListItemModel({
this.comment,
this.typeid,
this.play,
this.pic,
this.subtitle,
this.description,
this.copyright,
this.title,
this.review,
this.author,
this.mid,
this.created,
this.pubdate,
this.length,
this.duration,
this.videoReview,
this.aid,
this.bvid,
this.cid,
this.hideClick,
this.isChargingSrc,
this.rcmdReason,
this.owner,
});
int? comment;
int? typeid;
int? play;
String? pic;
String? subtitle;
String? description;
String? copyright;
String? title;
int? review;
String? author;
int? mid;
int? created;
int? pubdate;
String? length;
String? duration;
int? videoReview;
int? aid;
String? bvid;
int? cid;
bool? hideClick;
bool? isChargingSrc;
Stat? stat;
String? rcmdReason;
Owner? owner;
VListItemModel.fromJson(Map<String, dynamic> json) {
comment = json['comment'];
typeid = json['typeid'];
play = json['play'];
pic = json['pic'];
subtitle = json['subtitle'];
description = json['description'];
copyright = json['copyright'];
title = json['title'];
review = json['review'];
author = json['author'];
mid = json['mid'];
created = json['created'];
pubdate = json['created'];
length = json['length'];
duration = json['length'];
videoReview = json['video_review'];
aid = json['aid'];
bvid = json['bvid'];
cid = null;
hideClick = json['hide_click'];
isChargingSrc = json['is_charging_arc'];
stat = Stat.fromJson(json);
rcmdReason = null;
owner = Owner.fromJson(json);
}
}
class Stat {
Stat({
this.view,
this.danmaku,
});
int? view;
int? danmaku;
Stat.fromJson(Map<String, dynamic> json) {
view = json["play"];
danmaku = json['comment'];
}
}
class Owner {
Owner({
this.mid,
this.name,
this.face,
});
int? mid;
String? name;
String? face;
Owner.fromJson(Map<String, dynamic> json) {
mid = json["mid"];
name = json["author"];
face = '';
}
}

View File

@ -71,6 +71,7 @@ class LiveRoom {
this.cover,
this.roomId,
this.roundStatus,
this.watchedShow,
});
int? roomStatus;
@ -80,6 +81,7 @@ class LiveRoom {
String? cover;
int? roomId;
int? roundStatus;
Map? watchedShow;
LiveRoom.fromJson(Map<String, dynamic> json) {
roomStatus = json['roomStatus'];
@ -89,5 +91,6 @@ class LiveRoom {
cover = json['cover'];
roomId = json['roomid'];
roundStatus = json['roundStatus'];
watchedShow = json['watched_show'];
}
}

View File

@ -57,9 +57,9 @@ class DynamicsController extends GetxController {
);
if (res['status']) {
if (type == 'init') {
dynamicsList!.value = res['data'].items;
dynamicsList.value = res['data'].items;
} else {
dynamicsList!.addAll(res['data'].items);
dynamicsList.addAll(res['data'].items);
}
offset = res['data'].offset;
page++;
@ -69,7 +69,7 @@ class DynamicsController extends GetxController {
onSelectType(value) async {
dynamicsType.value = filterTypeList[value - 1]['value'];
dynamicsList!.value = [DynamicItemModel()];
dynamicsList.value = [DynamicItemModel()];
page = 1;
initialValue.value = value;
await queryFollowDynamic();
@ -128,6 +128,7 @@ class DynamicsController extends GetxController {
'mid': author.mid,
'face': author.face,
'roomid': liveRcmd.roomId,
'watched_show': liveRcmd.watchedShow,
});
Get.toNamed('/liveRoom?roomid=${liveItem.roomId}', arguments: {
'liveItem': liveItem,
@ -151,7 +152,7 @@ class DynamicsController extends GetxController {
onSelectUp(mid) async {
dynamicsType.value = DynamicsType.values[0];
dynamicsList!.value = [DynamicItemModel()];
dynamicsList.value = [DynamicItemModel()];
page = 1;
queryFollowDynamic();
}

View File

@ -4,9 +4,11 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_reply.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/pages/dynamics/deatil/index.dart';
import 'package:pilipala/pages/dynamics/widgets/author_panel.dart';
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
import '../widgets/dynamic_panel.dart';
@ -25,19 +27,20 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
final ScrollController scrollController = ScrollController();
bool _visibleTitle = false;
String? action;
// 回复类型
late int type;
@override
void initState() {
super.initState();
int oid = 0;
int type = 0;
// floor 1原创 2转发
if (Get.arguments['floor'] == 1) {
oid = int.parse(Get.arguments['item'].basic!['comment_id_str']);
type = Get.arguments['item'].basic!['comment_type'];
} else {
oid = Get.arguments['item'].modules.moduleDynamic.major.draw.id;
type = 11;
}
type = Get.arguments['item'].basic!['comment_type'];
action =
Get.arguments.containsKey('action') ? Get.arguments['action'] : null;
_dynamicDetailController = Get.put(DynamicDetailController(oid, type));
@ -68,6 +71,26 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
}
}
void replyReply(replyItem, paddingTop) {
int oid = replyItem.replies!.first.oid;
int rpid = replyItem.rpid!;
Get.to(
() => Scaffold(
appBar: AppBar(
title: const Text('评论详情'),
centerTitle: false,
),
body: VideoReplyReplyPanel(
oid: oid,
rpid: rpid,
paddingTop: paddingTop,
source: 'dynamic',
replyType: ReplyType.values[type],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -189,10 +212,14 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
);
} else {
return ReplyItem(
replyItem: _dynamicDetailController!
.replyList[index],
showReplyRow: true,
replyLevel: '1');
replyItem:
_dynamicDetailController!.replyList[index],
showReplyRow: true,
replyLevel: '1',
replyReply: (replyItem, paddingTop) =>
replyReply(replyItem, paddingTop),
replyType: ReplyType.album,
);
}
},
childCount:
@ -212,7 +239,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage> {
return SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 5),
}, childCount: 8),
);
}
},

View File

@ -234,7 +234,7 @@ class _DynamicsPageState extends State<DynamicsPage>
Map data = snapshot.data;
if (data['status']) {
List<DynamicItemModel> list =
_dynamicsController.dynamicsList!;
_dynamicsController.dynamicsList;
return Obx(
() => list.length == 1
? skeleton()

View File

@ -23,7 +23,7 @@ Widget addWidget(item, context, type, {floor = 1}) {
onTap: () {},
child: Container(
padding:
const EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 8),
const EdgeInsets.only(left: 12, top: 8, right: 12, bottom: 8),
color: bgColor,
child: Row(
children: [
@ -60,90 +60,104 @@ Widget addWidget(item, context, type, {floor = 1}) {
),
);
case 'ADDITIONAL_TYPE_RESERVE':
return InkWell(
onTap: () {},
child: Container(
margin: const EdgeInsets.only(top: 8),
padding:
const EdgeInsets.only(left: 15, top: 12, right: 15, bottom: 10),
color: bgColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(dynamicProperty[type].title),
Text.rich(TextSpan(
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
children: [
TextSpan(text: dynamicProperty[type].desc1['text']),
TextSpan(text: dynamicProperty[type].desc2['text']),
]))
],
),
// TextButton(onPressed: () {}, child: Text('123'))
],
return Padding(
padding: const EdgeInsets.only(top: 8),
child: InkWell(
onTap: () {},
child: Container(
width: double.infinity,
padding:
const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10),
color: bgColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
dynamicProperty[type].title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 1),
Text.rich(
TextSpan(
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize),
children: [
TextSpan(text: dynamicProperty[type].desc1['text']),
const TextSpan(text: ' '),
TextSpan(text: dynamicProperty[type].desc2['text']),
],
),
)
],
),
// TextButton(onPressed: () {}, child: Text('123'))
),
),
);
case 'ADDITIONAL_TYPE_GOODS':
return Container(
margin: const EdgeInsets.only(top: 6),
padding: const EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 8),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.all(Radius.circular(6)),
),
child: Row(
children: [
NetworkImgLayer(
width: 75,
height: 75,
src: dynamicProperty[type].items.first.cover,
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
return Padding(
padding: const EdgeInsets.only(top: 6),
child: InkWell(
onTap: () {},
child: Container(
padding:
const EdgeInsets.only(left: 12, top: 8, right: 12, bottom: 8),
decoration: BoxDecoration(
color: bgColor,
borderRadius: const BorderRadius.all(Radius.circular(6)),
),
child: Row(
children: [
Text(
dynamicProperty[type].items.first.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
NetworkImgLayer(
width: 75,
height: 75,
src: dynamicProperty[type].items.first.cover,
),
Text(
dynamicProperty[type].items.first.brief,
maxLines: 1,
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize,
),
),
const SizedBox(height: 2),
Text(
dynamicProperty[type].items.first.price,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
dynamicProperty[type].items.first.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
dynamicProperty[type].items.first.brief,
maxLines: 1,
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize,
),
),
const SizedBox(height: 2),
Text(
dynamicProperty[type].items.first.price,
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
),
),
],
),
),
],
),
),
],
),
);
));
case 'ADDITIONAL_TYPE_MATCH':
return SizedBox();
case 'ADDITIONAL_TYPE_COMMON':
return SizedBox();
case 'ADDITIONAL_TYPE_VOTE':
return SizedBox();
default:
return Text('11');
}

View File

@ -7,24 +7,34 @@ Widget content(item, context, source) {
TextStyle authorStyle =
TextStyle(color: Theme.of(context).colorScheme.primary);
return Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(12, 0, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (item.modules.moduleDynamic.topic != null) ...[
GestureDetector(
child: Text(
'#${item.modules.moduleDynamic.topic.name}',
style: authorStyle,
),
width: double.infinity,
padding: const EdgeInsets.fromLTRB(12, 0, 12, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (item.modules.moduleDynamic.topic != null) ...[
GestureDetector(
child: Text(
'#${item.modules.moduleDynamic.topic.name}',
style: authorStyle,
),
],
Text.rich(
richNode(item, context),
maxLines: source == 'detail' ? 999 : 3,
overflow: TextOverflow.ellipsis,
),
],
));
IgnorePointer(
// 禁用SelectableRegion的触摸交互功能
ignoring: source == 'detail' ? false : true,
child: SelectableRegion(
magnifierConfiguration: const TextMagnifierConfiguration(),
focusNode: FocusNode(),
selectionControls: MaterialTextSelectionControls(),
child: Text.rich(
richNode(item, context),
maxLines: source == 'detail' ? 999 : 3,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
);
}

View File

@ -64,7 +64,12 @@ InlineSpan richNode(item, context) {
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: GestureDetector(
onTap: () {},
onTap: () {
Get.toNamed(
'/webview',
parameters: {'url': i.origText, 'type': 'url', 'pageTitle': ''},
);
},
child: Text(
i.text,
style: authorStyle,
@ -79,7 +84,18 @@ InlineSpan richNode(item, context) {
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: GestureDetector(
onTap: () {},
onTap: () {
String dynamicId = item.basic['comment_id_str'];
Get.toNamed(
'/webview',
parameters: {
'url':
'https://t.bilibili.com/vote/h5/index/#/result?vote_id=${i.rid}&dynamic_id=${dynamicId}&isWeb=1',
'type': 'vote',
'pageTitle': '投票'
},
);
},
child: Text(
'投票:${i.text}',
style: authorStyle,

View File

@ -7,6 +7,7 @@ import 'package:pilipala/models/dynamics/up.dart';
import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/pages/dynamics/controller.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/utils.dart';
class UpPanel extends StatefulWidget {
FollowUpModel? upData;
@ -92,8 +93,10 @@ class _UpPanelState extends State<UpPanel> {
child: Center(
child: Text(
'全部',
style:
TextStyle(color: Theme.of(context).primaryColor),
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onSecondaryContainer),
),
),
),
@ -146,6 +149,11 @@ class _UpPanelState extends State<UpPanel> {
);
}
},
onLongPress: () {
String heroTag = Utils.makeHeroTag(data.mid);
Get.toNamed('/member?mid=${data.mid}',
arguments: {'face': data.face, 'heroTag': heroTag});
},
child: Padding(
padding: itemPadding,
child: AnimatedOpacity(

View File

@ -29,7 +29,7 @@ class _HomePageState extends State<HomePage>
appBar: AppBar(
titleSpacing: 0,
title: Padding(
padding: const EdgeInsets.only(left: 8, right: 8),
padding: const EdgeInsets.only(left: 12, right: 12, bottom: 8),
child: Stack(
children: [
const Align(
@ -79,13 +79,12 @@ class _HomePageState extends State<HomePage>
indicatorPadding: const EdgeInsets.symmetric(
horizontal: 4, vertical: 5),
indicator: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
color: Theme.of(context).colorScheme.primary,
borderRadius:
const BorderRadius.all(Radius.circular(20)),
),
indicatorSize: TabBarIndicatorSize.tab,
labelColor:
Theme.of(context).colorScheme.onSecondaryContainer,
labelColor: Theme.of(context).colorScheme.onPrimary,
labelStyle: const TextStyle(fontSize: 13),
dividerColor: Colors.transparent,
unselectedLabelColor:

View File

@ -10,11 +10,14 @@ class LiveRoomController extends GetxController {
late int roomId;
var liveItem;
late String heroTag;
double volume = 0.0;
// 静音状态
RxBool volumeOff = false.obs;
MeeduPlayerController meeduPlayerController = MeeduPlayerController(
colorTheme: Theme.of(Get.context!).colorScheme.primary,
pipEnabled: true,
controlsStyle: ControlsStyle.youtube,
controlsStyle: ControlsStyle.live,
enabledButtons: const EnabledButtons(pip: true),
);
@ -45,6 +48,7 @@ class LiveRoomController extends GetxController {
),
autoplay: true,
);
volume = meeduPlayerController.volume.value;
}
Future queryLiveInfo() async {
@ -59,4 +63,18 @@ class LiveRoomController extends GetxController {
playerInit(videoUrl);
}
}
void setVolumn(value) {
if (value == 0) {
// 设置音量
volumeOff.value = false;
meeduPlayerController.setVolume(volume);
} else {
// 取消音量
volume = value;
volumeOff.value = true;
meeduPlayerController.setVolume(0);
}
print('🌹:${volumeOff.value}');
}
}

View File

@ -69,13 +69,23 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
_liveRoomController.liveItem.uname,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 3),
Text(_liveRoomController.liveItem.title,
style: const TextStyle(fontSize: 12)),
const SizedBox(height: 1),
if (_liveRoomController.liveItem.watchedShow != null)
Text(
_liveRoomController.liveItem.watchedShow['text_large'] ??
'',
style: const TextStyle(fontSize: 12)),
],
)
),
],
),
actions: [
SizedBox(
height: 34,
child: ElevatedButton(onPressed: () {}, child: const Text('关注')),
),
const SizedBox(width: 12),
],
),
body: Column(
children: [
@ -86,6 +96,36 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
AspectRatio(
aspectRatio: 16 / 9,
child: MeeduVideoPlayer(
header: (BuildContext context,
MeeduPlayerController _meeduPlayerController,
Responsive) {
return AppBar(
backgroundColor: Colors.transparent,
primary: false,
elevation: 0,
scrolledUnderElevation: 0,
foregroundColor: Colors.white,
automaticallyImplyLeading: false,
centerTitle: false,
title: Text(_liveRoomController.liveItem.title,
style: const TextStyle(fontSize: 12)),
actions: [
SizedBox(
width: 38,
height: 38,
child: IconButton(
onPressed: () =>
_meeduPlayerController.enterPip(context),
icon: const Icon(
Icons.branding_watermark_outlined,
size: 19,
),
),
),
const SizedBox(width: 12)
],
);
},
controller: _meeduPlayerController!,
),
),
@ -110,9 +150,6 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
Container(
height: 45,
padding: const EdgeInsets.only(left: 12, right: 12),
child: Row(children: [
Text(_liveRoomController.liveItem.watchedShow['text_large']),
]),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
border: Border(
@ -120,6 +157,56 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
color: Theme.of(context).dividerColor.withOpacity(0.1)),
),
),
child: Row(children: <Widget>[
// SizedBox(
// width: 38,
// height: 38,
// child: IconButton(
// onPressed: () {},
// icon: const Icon(
// Icons.subtitles_outlined,
// size: 21,
// ),
// ),
// ),
const Spacer(),
// SizedBox(
// width: 38,
// height: 38,
// child: IconButton(
// onPressed: () {},
// icon: const Icon(
// Icons.hd_outlined,
// size: 20,
// ),
// ),
// ),
SizedBox(
width: 38,
height: 38,
child: IconButton(
onPressed: () => _liveRoomController
.setVolumn(_meeduPlayerController!.volume.value),
icon: Obx(() => Icon(
_liveRoomController.volumeOff.value
? Icons.volume_off_outlined
: Icons.volume_up_outlined,
size: 21,
)),
),
),
SizedBox(
width: 38,
height: 38,
child: IconButton(
onPressed: () =>
_meeduPlayerController!.goToFullscreen(context),
icon: const Icon(
Icons.fullscreen,
),
),
),
]),
),
],
),

View File

@ -0,0 +1,22 @@
import 'package:get/get.dart';
import 'package:pilipala/http/member.dart';
class ArchiveController extends GetxController {
int? mid;
int pn = 1;
@override
void onInit() {
super.onInit();
mid = int.parse(Get.parameters['mid']!);
}
// 获取用户投稿
Future getMemberArchive() async {
var res = await MemberHttp.memberArchive(mid: mid, pn: pn);
if (res['status']) {
pn += 1;
}
return res;
}
}

View File

@ -0,0 +1,4 @@
library archive_panel;
export './controller.dart';
export 'index.dart';

View File

@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loading_more_list/loading_more_list.dart';
import 'package:pilipala/common/widgets/pull_to_refresh_header.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
import 'package:pilipala/models/member/archive.dart';
import 'package:pilipala/pages/member/archive/index.dart';
import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart';
class ArchivePanel extends StatefulWidget {
const ArchivePanel({super.key});
@override
State<ArchivePanel> createState() => _ArchivePanelState();
}
class _ArchivePanelState extends State<ArchivePanel>
with AutomaticKeepAliveClientMixin {
DateTime lastRefreshTime = DateTime.now();
late final LoadMoreListSource source = LoadMoreListSource();
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
return PullToRefreshNotification(
onRefresh: () async {
await Future.delayed(const Duration(seconds: 1));
return true;
},
maxDragOffset: 50,
child: GlowNotificationWidget(
Column(
children: <Widget>[
// 下拉刷新指示器
PullToRefreshContainer(
(PullToRefreshScrollNotificationInfo? info) {
return PullToRefreshHeader(info, lastRefreshTime);
},
),
const SizedBox(height: 4),
Expanded(
child: LoadingMoreList<VListItemModel>(
ListConfig<VListItemModel>(
sourceList: source,
itemBuilder:
(BuildContext c, VListItemModel item, int index) {
return VideoCardH(videoItem: item);
},
indicatorBuilder: (context, status) {
return const Center(child: Text('加载中'));
},
),
),
)
],
),
showGlowLeading: false,
),
);
}
}
class LoadMoreListSource extends LoadingMoreBase<VListItemModel> {
final ArchiveController _archiveController = Get.put(ArchiveController());
@override
Future<bool> loadData([bool isloadMoreAction = false]) {
return Future<bool>(() async {
var res = await _archiveController.getMemberArchive();
if (res['status']) {
addAll(res['data'].list.vlist);
}
return true;
});
}
}

View File

@ -1,6 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/http/member.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/member/archive.dart';
import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/wbi_sign.dart';
@ -13,6 +17,8 @@ class MemberController extends GetxController {
String? heroTag;
Box user = GStrorage.user;
late int ownerMid;
// 投稿列表
RxList<VListItemModel>? archiveList = [VListItemModel()].obs;
@override
void onInit() {
@ -26,14 +32,7 @@ class MemberController extends GetxController {
// 获取用户信息
Future<Map<String, dynamic>> getInfo() async {
await getMemberStat();
String params = await WbiSign().makSign({
'mid': mid,
'token': '',
'platform': 'web',
'web_location': 1550101,
});
params = '?$params';
var res = await MemberHttp.memberInfo(params: params);
var res = await MemberHttp.memberInfo(mid: mid);
if (res['status']) {
memberInfo.value = res['data'];
}
@ -56,4 +55,43 @@ class MemberController extends GetxController {
}
return res;
}
// 关注/取关up
Future actionRelationMod() async {
if (user.get(UserBoxKey.userMid) == null) {
SmartDialog.showToast('账号未登录');
return;
}
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: const Text('点错了')),
TextButton(
onPressed: () async {
await VideoHttp.relationMod(
mid: mid,
act: memberInfo.value.isFollowed! ? 2 : 1,
reSrc: 11,
);
memberInfo.value.isFollowed = !memberInfo.value.isFollowed!;
SmartDialog.dismiss();
SmartDialog.showLoading();
SmartDialog.dismiss();
memberInfo.update((val) {});
},
child: const Text('确认'),
)
],
);
},
);
}
}

View File

@ -1,14 +1,16 @@
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.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/models/user/stat.dart';
import 'package:pilipala/pages/member/archive/view.dart';
import 'package:pilipala/pages/member/index.dart';
import 'package:pilipala/utils/utils.dart';
import 'widgets/profile.dart';
class MemberPage extends StatefulWidget {
const MemberPage({super.key});
@ -19,13 +21,15 @@ class MemberPage extends StatefulWidget {
class _MemberPageState extends State<MemberPage>
with SingleTickerProviderStateMixin {
final MemberController _memberController = Get.put(MemberController());
Future? _futureBuilderFuture;
final ScrollController _extendNestCtr = ScrollController();
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_tabController = TabController(length: 3, vsync: this, initialIndex: 2);
_futureBuilderFuture = _memberController.getInfo();
}
@override
@ -90,7 +94,7 @@ class _MemberPageState extends State<MemberPage>
Padding(
padding: const EdgeInsets.only(left: 18, right: 18),
child: FutureBuilder(
future: _memberController.getInfo(),
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
@ -104,8 +108,7 @@ class _MemberPageState extends State<MemberPage>
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
profile(
_memberController.memberInfo.value),
profile(_memberController),
const SizedBox(height: 14),
Row(
children: [
@ -232,7 +235,8 @@ class _MemberPageState extends State<MemberPage>
}
} else {
// 骨架屏
return profile(null, loadingStatus: true);
return profile(_memberController,
loadingStatus: true);
}
},
),
@ -249,10 +253,10 @@ class _MemberPageState extends State<MemberPage>
onlyOneScrollInBody: true,
body: Column(
children: [
Container(
SizedBox(
width: double.infinity,
height: 50,
child: TabBar(controller: _tabController, tabs: [
child: TabBar(controller: _tabController, tabs: const [
Tab(text: '主页'),
Tab(text: '动态'),
Tab(text: '投稿'),
@ -261,10 +265,10 @@ class _MemberPageState extends State<MemberPage>
Expanded(
child: TabBarView(
controller: _tabController,
children: [
children: const [
Text('主页'),
Text('动态'),
Text('投稿'),
ArchivePanel(),
],
))
],
@ -272,186 +276,4 @@ class _MemberPageState extends State<MemberPage>
),
);
}
Widget profile(memberInfo, {loadingStatus = false}) {
return Padding(
padding: EdgeInsets.only(top: 3 * MediaQuery.of(context).padding.top),
child: Row(
children: [
Hero(
tag: _memberController.heroTag!,
child: Stack(
children: [
NetworkImgLayer(
width: 90,
height: 90,
type: 'avatar',
src: !loadingStatus
? memberInfo.face
: _memberController.face,
),
if (!loadingStatus &&
memberInfo.liveRoom != null &&
memberInfo.liveRoom.liveStatus == 1)
Positioned(
bottom: 0,
left: 14,
child: GestureDetector(
onTap: () {
LiveItemModel liveItem = LiveItemModel.fromJson({
'title': memberInfo.liveRoom.title,
'uname': memberInfo.name,
'face': memberInfo.face,
'roomid': memberInfo.liveRoom.roomId,
});
Get.toNamed(
'/liveRoom?roomid=${memberInfo.liveRoom.roomId}',
arguments: {'liveItem': liveItem},
);
},
child: Container(
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius:
const BorderRadius.all(Radius.circular(10)),
),
child: Row(children: [
Image.asset(
'assets/images/live.gif',
height: 10,
),
Text(
' 直播中',
style: TextStyle(
color: Colors.white,
fontSize: Theme.of(context)
.textTheme
.labelSmall!
.fontSize),
)
]),
),
),
)
],
)),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text(
!loadingStatus
? _memberController.userStat!['following']
.toString()
: '-',
style: const TextStyle(fontWeight: FontWeight.bold),
),
Text(
'关注',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
Column(
children: [
Text(
!loadingStatus
? Utils.numFormat(
_memberController.userStat!['follower'],
)
: '-',
style:
const TextStyle(fontWeight: FontWeight.bold)),
Text('粉丝',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize))
],
),
Column(
children: [
const Text('-',
style: TextStyle(fontWeight: FontWeight.bold)),
Text(
'获赞',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
],
),
),
const SizedBox(height: 10),
if (_memberController.ownerMid != _memberController.mid) ...[
Row(
children: [
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 42, right: 42),
foregroundColor:
!loadingStatus && memberInfo.isFollowed
? Theme.of(context).colorScheme.outline
: Theme.of(context).colorScheme.onPrimary,
backgroundColor: !loadingStatus &&
memberInfo.isFollowed
? Theme.of(context).colorScheme.onInverseSurface
: Theme.of(context)
.colorScheme
.primary, // 设置按钮背景色
),
child: Text(!loadingStatus && memberInfo.isFollowed
? '取关'
: '关注'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 42, right: 42),
backgroundColor:
Theme.of(context).colorScheme.onInverseSurface,
),
child: const Text('发消息'),
)
],
)
] else ...[
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 80, right: 80),
foregroundColor: Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).colorScheme.primary,
),
child: const Text('编辑资料'),
)
]
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,193 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/live/item.dart';
import 'package:pilipala/models/member/info.dart';
import 'package:pilipala/utils/utils.dart';
Widget profile(ctr, {loadingStatus = false}) {
MemberInfoModel memberInfo = ctr.memberInfo.value;
return Builder(
builder: ((context) {
return Padding(
padding: EdgeInsets.only(top: 3 * MediaQuery.of(context).padding.top),
child: Row(
children: [
Hero(
tag: ctr.heroTag!,
child: Stack(
children: [
NetworkImgLayer(
width: 90,
height: 90,
type: 'avatar',
src: !loadingStatus ? memberInfo.face : ctr.face,
),
if (!loadingStatus &&
memberInfo.liveRoom != null &&
memberInfo.liveRoom!.liveStatus == 1)
Positioned(
bottom: 0,
left: 14,
child: GestureDetector(
onTap: () {
LiveItemModel liveItem = LiveItemModel.fromJson({
'title': memberInfo.liveRoom!.title,
'uname': memberInfo.name,
'face': memberInfo.face,
'roomid': memberInfo.liveRoom!.roomId,
'watched_show': memberInfo.liveRoom!.watchedShow,
});
Get.toNamed(
'/liveRoom?roomid=${memberInfo.liveRoom!.roomId}',
arguments: {'liveItem': liveItem},
);
},
child: Container(
padding: const EdgeInsets.fromLTRB(6, 2, 6, 2),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius:
const BorderRadius.all(Radius.circular(10)),
),
child: Row(children: [
Image.asset(
'assets/images/live.gif',
height: 10,
),
Text(
' 直播中',
style: TextStyle(
color: Colors.white,
fontSize: Theme.of(context)
.textTheme
.labelSmall!
.fontSize),
)
]),
),
),
)
],
)),
const SizedBox(width: 12),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text(
!loadingStatus
? ctr.userStat!['following'].toString()
: '-',
style:
const TextStyle(fontWeight: FontWeight.bold),
),
Text(
'关注',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
Column(
children: [
Text(
!loadingStatus
? Utils.numFormat(
ctr.userStat!['follower'],
)
: '-',
style: const TextStyle(
fontWeight: FontWeight.bold)),
Text('粉丝',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize))
],
),
Column(
children: [
const Text('-',
style: TextStyle(fontWeight: FontWeight.bold)),
Text(
'获赞',
style: TextStyle(
fontSize: Theme.of(context)
.textTheme
.labelMedium!
.fontSize),
)
],
),
],
),
),
const SizedBox(height: 10),
if (ctr.ownerMid != ctr.mid) ...[
Row(
children: [
TextButton(
onPressed: () => ctr.actionRelationMod(),
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 42, right: 42),
foregroundColor:
!loadingStatus && memberInfo.isFollowed!
? Theme.of(context).colorScheme.outline
: Theme.of(context).colorScheme.onPrimary,
backgroundColor: !loadingStatus &&
memberInfo.isFollowed!
? Theme.of(context).colorScheme.onInverseSurface
: Theme.of(context)
.colorScheme
.primary, // 设置按钮背景色
),
child: Text(!loadingStatus && memberInfo.isFollowed!
? '取关'
: '关注'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 42, right: 42),
backgroundColor:
Theme.of(context).colorScheme.onInverseSurface,
),
child: const Text('发消息'),
)
],
)
] else ...[
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
padding: const EdgeInsets.only(left: 80, right: 80),
foregroundColor:
Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).colorScheme.primary,
),
child: const Text('编辑资料'),
)
]
],
),
),
],
),
);
}),
);
}

View File

@ -7,6 +7,7 @@ import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/play/url.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
@ -87,6 +88,8 @@ class VideoDetailController extends GetxController
},
firstFloor: firstFloor,
paddingTop: paddingTop,
replyType: ReplyType.video,
source: 'videoDetail',
);
});
ctr?.closed.then((value) {
@ -95,6 +98,7 @@ class VideoDetailController extends GetxController
}
playerInit(source, audioSource, {Duration defaultST = Duration.zero}) {
meeduPlayerController.onVideoFitChange(BoxFit.cover);
meeduPlayerController.setDataSource(
DataSource(
type: DataSourceType.network,
@ -157,7 +161,7 @@ class VideoDetailController extends GetxController
@override
void onClose() {
markHeartBeat();
if (timer!.isActive) {
if (timer != null && timer!.isActive) {
timer!.cancel();
}
super.onClose();

View File

@ -84,11 +84,11 @@ class VideoIntroController extends GetxController {
Get.find<VideoDetailController>(tag: Get.arguments['heroTag'])
.tabs
.value = ['简介', '评论 ${result['data']!.stat!.reply}'];
// 获取到粉丝数再返回
await queryUserStat();
} else {
responseMsg = result['msg'];
}
// 获取到粉丝数再返回
await queryUserStat();
if (userLogin) {
// 获取点赞状态
queryHasLikeVideo();
@ -99,13 +99,13 @@ class VideoIntroController extends GetxController {
//
queryFollowStatus();
}
return result;
}
// 获取up主粉丝数
Future queryUserStat() async {
var result = await UserHttp.userStat(mid: videoDetail.value.owner!.mid!);
print('🌹:$result');
if (result['status']) {
userStat = result['data'];
}

View File

@ -65,7 +65,7 @@ class _VideoIntroPanelState extends State<VideoIntroPanel>
// 请求错误
return HttpError(
errMsg: snapshot.data['msg'],
fn: () => setState(() {}),
fn: () => Get.back(),
);
}
} else {
@ -231,7 +231,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return SliverPadding(
padding: const EdgeInsets.only(left: 12, right: 12, top: 20),
padding: const EdgeInsets.only(left: 12, right: 12, top: 10),
sliver: SliverToBoxAdapter(
child: !widget.loadingStatus || videoItem.isNotEmpty
? Column(
@ -248,6 +248,8 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
style: Theme.of(context).textTheme.titleMedium!.copyWith(
letterSpacing: 0.5,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
InkWell(

View File

@ -1,4 +0,0 @@
library video_player;
export './controller.dart';
export './view.dart';

View File

@ -6,6 +6,7 @@ import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_reply.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/replyNew/index.dart';
import 'package:pilipala/utils/id_utils.dart';
import 'controller.dart';
@ -52,9 +53,10 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
tag: widget.rpid.toString());
_videoReplyController.rPid = widget.rpid;
} else {
int oid = Get.parameters['bvid'] != null
? IdUtils.bv2av(Get.parameters['bvid']!)
: 0;
// fix 评论加载不对称
// int oid = Get.parameters['bvid'] != null
// ? IdUtils.bv2av(Get.parameters['bvid']!)
// : 0;
_videoReplyController = Get.put(VideoReplyController(oid, '', '1'),
tag: Get.arguments['heroTag']);
}
@ -113,6 +115,16 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
_videoReplyController.wakeUpReply();
}
// 展示二级回复
void replyReply(replyItem, paddingTop) {
VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
videoDetailCtr.oid = replyItem.replies!.first.oid;
videoDetailCtr.fRpid = replyItem.rpid!;
videoDetailCtr.firstFloor = replyItem;
videoDetailCtr.showReplyReplyPanel(paddingTop);
}
@override
void dispose() {
super.dispose();
@ -164,10 +176,13 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
);
} else {
return ReplyItem(
replyItem:
_videoReplyController.replyList[index],
showReplyRow: true,
replyLevel: replyLevel);
replyItem:
_videoReplyController.replyList[index],
showReplyRow: true,
replyLevel: replyLevel,
replyReply: (replyItem, paddingTop) =>
replyReply(replyItem, paddingTop),
);
}
},
childCount:

View File

@ -6,6 +6,7 @@ import 'package:flutter_meedu_media_kit/meedu_player.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/controller.dart';
import 'package:pilipala/pages/video/detail/reply/index.dart';
@ -14,16 +15,21 @@ import 'package:pilipala/pages/video/detail/replyReply/index.dart';
import 'package:pilipala/utils/utils.dart';
class ReplyItem extends StatelessWidget {
ReplyItem(
{super.key,
this.replyItem,
this.addReply,
this.replyLevel,
this.showReplyRow});
ReplyItem({
super.key,
this.replyItem,
this.addReply,
this.replyLevel,
this.showReplyRow,
this.replyReply,
this.replyType,
});
ReplyItemModel? replyItem;
Function? addReply;
String? replyLevel;
bool? showReplyRow = true;
Function? replyReply;
ReplyType? replyType;
@override
Widget build(BuildContext context) {
@ -212,6 +218,7 @@ class ReplyItem extends StatelessWidget {
replyControl: replyItem!.replyControl,
f_rpid: replyItem!.rpid,
replyItem: replyItem,
replyReply: replyReply,
),
),
],
@ -272,11 +279,13 @@ class ReplyItem extends StatelessWidget {
isScrollControlled: true,
builder: (builder) {
return VideoReplyNewDialog(
replyLevel: replyLevel,
oid: replyItem!.oid,
root: replyItem!.rpid,
parent: replyItem!.rpid,
paddingTop: paddingTop);
replyLevel: replyLevel,
oid: replyItem!.oid,
root: replyItem!.rpid,
parent: replyItem!.rpid,
paddingTop: paddingTop,
replyType: replyType,
);
},
).then((value) => {
// 完成评论,数据添加
@ -320,21 +329,25 @@ class ReplyItem extends StatelessWidget {
// ignore: must_be_immutable
class ReplyItemRow extends StatelessWidget {
ReplyItemRow(
{super.key,
this.replies,
this.replyControl,
this.f_rpid,
this.replyItem});
ReplyItemRow({
super.key,
this.replies,
this.replyControl,
this.f_rpid,
this.replyItem,
this.replyReply,
});
List? replies;
ReplyControl? replyControl;
int? f_rpid;
ReplyItemModel? replyItem;
Function? replyReply;
@override
Widget build(BuildContext context) {
bool isShow = replyControl!.isShow!;
int extraRow = replyControl != null && isShow ? 1 : 0;
double paddingTop = MediaQuery.of(context).padding.top;
return Container(
margin: const EdgeInsets.only(left: 42, right: 4, top: 0),
child: Material(
@ -347,8 +360,7 @@ class ReplyItemRow extends StatelessWidget {
children: [
for (var i = 0; i < replies!.length; i++) ...[
InkWell(
onTap: () =>
replyReply(replyItem, MediaQuery.of(context).padding.top),
onTap: () => replyReply!(replyItem, paddingTop),
child: Container(
width: double.infinity,
padding: EdgeInsets.fromLTRB(
@ -398,8 +410,7 @@ class ReplyItemRow extends StatelessWidget {
],
if (extraRow == 1)
InkWell(
onTap: () =>
replyReply(replyItem, MediaQuery.of(context).padding.top),
onTap: () => replyReply!(replyItem, paddingTop),
child: Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(8, 5, 8, 8),
@ -428,16 +439,6 @@ class ReplyItemRow extends StatelessWidget {
),
);
}
void replyReply(replyItem, paddingTop) {
// replyItem 楼主评论
VideoDetailController videoDetailCtr =
Get.find<VideoDetailController>(tag: Get.arguments['heroTag']);
videoDetailCtr.oid = replies!.first.oid;
videoDetailCtr.fRpid = f_rpid!;
videoDetailCtr.firstFloor = replyItem;
videoDetailCtr.showReplyReplyPanel(paddingTop);
}
}
InlineSpan buildContent(BuildContext context, content) {

View File

@ -13,6 +13,7 @@ class VideoReplyNewDialog extends StatefulWidget {
String? replyLevel;
int? parent;
double? paddingTop;
ReplyType? replyType;
VideoReplyNewDialog({
this.oid,
@ -20,6 +21,7 @@ class VideoReplyNewDialog extends StatefulWidget {
this.replyLevel,
this.parent,
this.paddingTop,
this.replyType,
});
@override
@ -64,7 +66,7 @@ class _VideoReplyNewDialogState extends State<VideoReplyNewDialog>
Future submitReplyAdd() async {
String message = _replyContentController.text;
var result = await VideoHttp.replyAdd(
type: ReplyType.video,
type: widget.replyType!,
oid: widget.oid!,
root: widget.root!,
parent: widget.parent!,

View File

@ -1,16 +1,18 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/reply.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/data.dart';
import 'package:pilipala/models/video/reply/item.dart';
class VideoReplyReplyController extends GetxController {
VideoReplyReplyController(this.aid, this.rpid);
VideoReplyReplyController(this.aid, this.rpid, this.replyType);
final ScrollController scrollController = ScrollController();
// 视频aid 请求时使用的oid
int? aid;
// rpid 请求楼中楼回复
String? rpid;
ReplyType replyType = ReplyType.video;
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
// 当前页
int currentPage = 0;
@ -41,7 +43,10 @@ class VideoReplyReplyController extends GetxController {
}
isLoadingMore = true;
var res = await ReplyHttp.replyReplyList(
oid: aid!, root: rpid!, pageNum: currentPage + 1, type: 1);
oid: aid!,
root: rpid!,
pageNum: currentPage + 1,
type: replyType.index);
if (res['status']) {
res['data'] = ReplyData.fromJson(res['data']);
if (res['data'].replies.isNotEmpty) {

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_reply.dart';
import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart';
@ -13,6 +14,8 @@ class VideoReplyReplyPanel extends StatefulWidget {
Function? closePanel;
ReplyItemModel? firstFloor;
double? paddingTop;
String? source;
ReplyType? replyType;
VideoReplyReplyPanel({
this.oid,
@ -20,6 +23,8 @@ class VideoReplyReplyPanel extends StatefulWidget {
this.closePanel,
this.firstFloor,
this.paddingTop,
this.source,
this.replyType,
super.key,
});
@ -34,7 +39,8 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
@override
void initState() {
_videoReplyReplyController = Get.put(
VideoReplyReplyController(widget.oid, widget.rpid.toString()),
VideoReplyReplyController(
widget.oid, widget.rpid.toString(), widget.replyType!),
tag: widget.rpid.toString());
super.initState();
@ -62,34 +68,37 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height -
MediaQuery.of(context).size.width * 9 / 16 -
widget.paddingTop!,
height: widget.source == 'videoDetail'
? MediaQuery.of(context).size.height -
MediaQuery.of(context).size.width * 9 / 16 -
widget.paddingTop!
: null,
color: Theme.of(context).colorScheme.background,
child: Column(
children: [
Container(
height: 45,
padding: const EdgeInsets.only(left: 14, right: 14),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'评论详情',
style: Theme.of(context).textTheme.titleMedium,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () {
_videoReplyReplyController.currentPage = 0;
_videoReplyReplyController.rPid = 0;
widget.closePanel!();
Navigator.pop(context);
},
),
],
if (widget.source == 'videoDetail')
Container(
height: 45,
padding: const EdgeInsets.only(left: 14, right: 14),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'评论详情',
style: Theme.of(context).textTheme.titleMedium,
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () {
_videoReplyReplyController.currentPage = 0;
_videoReplyReplyController.rPid = 0;
widget.closePanel!();
Navigator.pop(context);
},
),
],
),
),
),
Divider(
height: 1,
color: Theme.of(context).dividerColor.withOpacity(0.1),
@ -188,7 +197,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
delegate:
SliverChildBuilderDelegate((context, index) {
return const VideoReplySkeleton();
}, childCount: 5),
}, childCount: 8),
);
}
},

View File

@ -90,14 +90,16 @@ class _VideoDetailPageState extends State<VideoDetailPage>
@override
void dispose() {
videoDetailController.meeduPlayerController.dispose();
videoDetailController.timer!.cancel();
if (videoDetailController.timer != null) {
videoDetailController.timer!.cancel();
}
super.dispose();
}
@override
// 离开当前页面时
void didPushNext() async {
if (!_meeduPlayerController!.pipEnabled) {
if (!_meeduPlayerController!.pipAvailable.value) {
_meeduPlayerController!.pause();
}
if (videoDetailController.timer!.isActive) {
@ -232,7 +234,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
children: [
Container(
width: double.infinity,
height: 0,
height: 45,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
@ -252,8 +254,8 @@ class _VideoDetailPageState extends State<VideoDetailPage>
() => TabBar(
controller: videoDetailController.tabCtr,
dividerColor: Colors.transparent,
indicatorColor:
Theme.of(context).colorScheme.background,
// indicatorColor:
// Theme.of(context).colorScheme.background,
tabs: videoDetailController.tabs
.map((String name) => Tab(text: name))
.toList(),
@ -278,7 +280,9 @@ class _VideoDetailPageState extends State<VideoDetailPage>
);
},
),
VideoReplyPanel()
VideoReplyPanel(
bvid: videoDetailController.bvid,
)
],
),
),

View File

@ -15,6 +15,7 @@ import 'package:pilipala/pages/preview/index.dart';
import 'package:pilipala/pages/search/index.dart';
import 'package:pilipala/pages/searchResult/index.dart';
import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/replyReply/index.dart';
import 'package:pilipala/pages/webview/index.dart';
import 'package:pilipala/pages/setting/index.dart';
import 'package:pilipala/pages/media/index.dart';
@ -65,5 +66,7 @@ class Routes {
GetPage(name: '/liveRoom', page: () => const LiveRoomPage()),
// 用户中心
GetPage(name: '/member', page: () => const MemberPage()),
// 二级回复
GetPage(name: '/replyReply', page: () => VideoReplyReplyPanel()),
];
}

View File

@ -12,6 +12,7 @@ class GStrorage {
static late final Box userInfo;
static late final Box hotKeyword;
static late final Box historyword;
static late final Box localCache;
static Future<void> init() async {
final dir = await getApplicationDocumentsDirectory();
@ -28,6 +29,8 @@ class GStrorage {
hotKeyword = await Hive.openBox('hotKeyword');
// 搜索历史
historyword = await Hive.openBox('historyWord');
// 本地缓存
localCache = await Hive.openBox('localCache');
}
static regAdapter() {

View File

@ -2,11 +2,15 @@
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/misc/sign/wbi.md
// import md5 from 'md5'
// import axios from 'axios'
import 'package:hive/hive.dart';
import 'package:pilipala/http/index.dart';
import 'package:crypto/crypto.dart';
import 'dart:convert';
import 'package:pilipala/utils/storage.dart';
class WbiSign {
static Box localCache = GStrorage.user;
List mixinKeyEncTab = [
46,
47,
@ -83,7 +87,7 @@ class WbiSign {
}
// 为请求参数进行 wbi 签名
String encWbi(params, imgKey, subKey) {
Map<String, dynamic> encWbi(params, imgKey, subKey) {
String mixinKey = getMixinKey(imgKey + subKey);
DateTime now = DateTime.now();
int currTime = (now.millisecondsSinceEpoch / 1000).round();
@ -99,19 +103,25 @@ class WbiSign {
String queryStr = query.join('&');
String wbiSign =
md5.convert(utf8.encode(queryStr + mixinKey)).toString(); // 计算 w_rid
print('w_rid: $wbiSign');
return '$queryStr&w_rid=$wbiSign';
return {'wts': currTime.toString(), 'w_rid': wbiSign};
}
// 获取最新的 img_key 和 sub_key
// 获取最新的 img_key 和 sub_key 可以从缓存中获取
static Future<Map<String, dynamic>> getWbiKeys() async {
DateTime nowDate = DateTime.now();
if (localCache.get('wbiKeys') != null &&
DateTime.fromMillisecondsSinceEpoch(localCache.get('timeStamp')).day ==
nowDate.day) {
Map cacheWbiKeys = localCache.get('wbiKeys');
return Map<String, dynamic>.from(cacheWbiKeys);
}
var resp =
await Request().get('https://api.bilibili.com/x/web-interface/nav');
var jsonContent = resp.data['data'];
String imgUrl = jsonContent['wbi_img']['img_url'];
String subUrl = jsonContent['wbi_img']['sub_url'];
return {
Map<String, dynamic> wbiKeys = {
'imgKey': imgUrl
.substring(imgUrl.lastIndexOf('/') + 1, imgUrl.length)
.split('.')[0],
@ -119,12 +129,16 @@ class WbiSign {
.substring(subUrl.lastIndexOf('/') + 1, subUrl.length)
.split('.')[0]
};
localCache.put('wbiKeys', wbiKeys);
localCache.put('timeStamp', nowDate.millisecondsSinceEpoch);
return wbiKeys;
}
makSign(Map<String, dynamic> params) async {
// params 为需要加密的请求参数
Map<String, dynamic> wbiKeys = await getWbiKeys();
String query = encWbi(params, wbiKeys['imgKey'], wbiKeys['subKey']);
Map<String, dynamic> query = params
..addAll(encWbi(params, wbiKeys['imgKey'], wbiKeys['subKey']));
return query;
}
}

View File

@ -17,7 +17,6 @@ import screen_retriever
import share_plus
import shared_preferences_foundation
import sqflite
import url_launcher_macos
import wakelock_macos
import window_manager
@ -34,7 +33,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}

View File

@ -213,10 +213,10 @@ packages:
dependency: "direct main"
description:
name: crypto
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.3"
cupertino_icons:
dependency: "direct main"
description:
@ -301,10 +301,10 @@ packages:
dependency: "direct main"
description:
name: dynamic_color
sha256: bbebb1b7ebed819e0ec83d4abdc2a8482d934f6a85289ffc1c6acf7589fa2aad
sha256: de4798a7069121aee12d5895315680258415de9b00e717723a1bd73d58f0126d
url: "https://pub.dev"
source: hosted
version: "1.6.3"
version: "1.6.6"
extended_image:
dependency: "direct main"
description:
@ -424,7 +424,7 @@ packages:
description:
path: package
ref: feature-custom
resolved-ref: ea73de29401ab35126ef3eac68270b2e6c3ef4e0
resolved-ref: d2bc690f3bf601feaa06085479abc384f0dc5168
url: "https://github.com/guozhigq/flutter_meedu_media_kit.git"
source: git
version: "4.2.12"
@ -432,10 +432,10 @@ packages:
dependency: "direct main"
description:
name: flutter_smart_dialog
sha256: da7ed8fe78e301e3c2cdaa533d13ed3edcf1853c1ba1a7383b481739569f7b2a
sha256: "8ba9eeb5b0b380bec368c5c8a324e1dab0cd88965c2dd83e64237441140bc599"
url: "https://pub.dev"
source: hosted
version: "4.9.0+6"
version: "4.9.3+2"
flutter_spinkit:
dependency: transitive
description:
@ -926,6 +926,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.3"
pull_to_refresh_notification:
dependency: "direct main"
description:
name: pull_to_refresh_notification
sha256: "3f27b9695c98770db3f9f50550e5ab44a6d946d022311a55bbe6d5cd4c69a1ad"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
rxdart:
dependency: transitive
description:
@ -1219,30 +1227,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.2"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e"
url: "https://pub.dev"
source: hosted
version: "6.1.10"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "22f8db4a72be26e9e3a4aa3f194b1f7afbc76d20ec141f84be1d787db2155cbd"
url: "https://pub.dev"
source: hosted
version: "6.0.31"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
url_launcher_linux:
dependency: transitive
description:
@ -1251,14 +1235,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.5"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
url_launcher_platform_interface:
dependency: transitive
description:

View File

@ -34,9 +34,9 @@ dependencies:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
cupertino_icons: ^1.0.5
# 动态取色
dynamic_color: ^1.6.2
dynamic_color: ^1.6.6
get: ^4.6.5
@ -63,18 +63,20 @@ dependencies:
permission_handler: ^10.2.0
# 分享
share_plus: ^6.3.1
# webView
url_launcher: ^6.1.9
# cookie 管理
webview_cookie_manager: ^2.0.6
# 浏览器
webview_flutter: ^4.2.0
# 解决sliver滑动不同步
extended_nested_scroll_view: ^6.0.0
# 上拉加载
loading_more_list: ^5.0.3
# 下拉刷新
pull_to_refresh_notification: ^3.0.1
# 图标
font_awesome_flutter: ^10.4.0
# toast
flutter_smart_dialog: ^4.9.0+6
flutter_smart_dialog: ^4.9.3+2
# 下滑关闭
dismissible_page: ^1.0.2
# 媒体播放
@ -85,8 +87,8 @@ dependencies:
ref: feature-custom
path: package
custom_sliding_segmented_control: ^1.7.5
loading_more_list: ^5.0.3
crypto: any
# 加密
crypto: ^3.0.3
dev_dependencies:
flutter_test: