diff --git a/README.md b/README.md
index 56c748ec..a5274cc1 100644
--- a/README.md
+++ b/README.md
@@ -74,12 +74,14 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码
- [ ] 弹幕
- [ ] 字幕
- [x] 记忆播放
+ - [x] 视频比例:高度/宽度适应、填充、包含等
- [x] 搜索相关
- [x] 热搜
- [x] 搜索历史
- [x] 默认搜索词
- [x] 投稿、番剧、直播间、用户搜索
+ - [x] 视频搜索排序、按时长筛选
- [x] 视频详情页相关
- [x] 视频选集(分p)切换
diff --git a/assets/images/error.svg b/assets/images/error.svg
new file mode 100644
index 00000000..ef27bea4
--- /dev/null
+++ b/assets/images/error.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/change_log/1.0.2.0819.md b/change_log/1.0.2.0819.md
new file mode 100644
index 00000000..b270775f
--- /dev/null
+++ b/change_log/1.0.2.0819.md
@@ -0,0 +1,19 @@
+## 1.0.2
+
+### 新功能
++ 自动检查更新
++ 封面图片保存
++ 动态跳转番剧
++ 历史记录番剧记忆播放
++ 一键清空稍后再看
+
+### 修复
++ 切换分P cid未切换
++ cookie存储问题
++ 登录/退出登录问题
+
+### 优化
++ 页面空/异常状态样式
++ 退出登录提示
++ 请求节流
++ 全屏播放
\ No newline at end of file
diff --git a/change_log/1.0.3.0821.md b/change_log/1.0.3.0821.md
new file mode 100644
index 00000000..4c845976
--- /dev/null
+++ b/change_log/1.0.3.0821.md
@@ -0,0 +1,19 @@
+## 1.0.3
+
+建议卸载1.0.2版本,重新安装
+### 新功能
++ 底部播放进度条设置
++ 复制图片链接
+
+
+### 修复
++ 用户数据格式修改
++ video Fit
++ 没有audio 资源的视频异常
++ 评论区域图片无法点击
++ 视频进度条拖动问题
+
+### 优化
++ 页面空/异常状态样式
++ 部分页面样式
++ 图片预览页面样式
\ No newline at end of file
diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart
index 3295721d..b02182c6 100644
--- a/lib/common/widgets/http_error.dart
+++ b/lib/common/widgets/http_error.dart
@@ -1,31 +1,41 @@
import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
class HttpError extends StatelessWidget {
- const HttpError({required this.errMsg, required this.fn, super.key});
+ const HttpError(
+ {required this.errMsg, required this.fn, this.btnText, super.key});
final String? errMsg;
final Function()? fn;
+ final String? btnText;
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: SizedBox(
- height: 150,
+ height: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
+ SvgPicture.asset(
+ "assets/images/error.svg",
+ height: 200,
+ ),
+ const SizedBox(height: 20),
Text(
errMsg ?? '请求异常',
textAlign: TextAlign.center,
- style: Theme.of(context).textTheme.titleMedium,
+ style: Theme.of(context).textTheme.titleSmall,
),
- const SizedBox(height: 10),
- ElevatedButton(
- onPressed: () {
- fn!();
- },
- child: const Text('点击重试'))
+ const SizedBox(height: 30),
+ OutlinedButton.icon(
+ onPressed: () {
+ fn!();
+ },
+ icon: const Icon(Icons.arrow_forward_outlined, size: 20),
+ label: Text(btnText ?? '点击重试'),
+ )
],
),
),
diff --git a/lib/common/widgets/no_data.dart b/lib/common/widgets/no_data.dart
new file mode 100644
index 00000000..8b6a8214
--- /dev/null
+++ b/lib/common/widgets/no_data.dart
@@ -0,0 +1,31 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class NoData extends StatelessWidget {
+ const NoData({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SliverToBoxAdapter(
+ child: SizedBox(
+ height: 400,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset(
+ "assets/images/error.svg",
+ height: 200,
+ ),
+ const SizedBox(height: 20),
+ Text(
+ '没有数据',
+ textAlign: TextAlign.center,
+ style: Theme.of(context).textTheme.titleSmall,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart
index 4cfce1ef..ed7b299e 100644
--- a/lib/common/widgets/video_card_h.dart
+++ b/lib/common/widgets/video_card_h.dart
@@ -55,7 +55,7 @@ class VideoCardH extends StatelessWidget {
},
child: Padding(
padding: const EdgeInsets.fromLTRB(
- StyleString.safeSpace, 7, StyleString.safeSpace, 7),
+ StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (context, boxConstraints) {
double width =
diff --git a/lib/http/init.dart b/lib/http/init.dart
index 05bf5e28..5f9e1e2b 100644
--- a/lib/http/init.dart
+++ b/lib/http/init.dart
@@ -20,7 +20,7 @@ class Request {
/// 设置cookie
static setCookie() async {
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
var cookiePath = await Utils.getCookiePath();
var cookieJar = PersistCookieJar(
ignoreExpires: true,
@@ -30,7 +30,8 @@ class Request {
dio.interceptors.add(cookieManager);
var cookie = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.baseUrl));
- if (user.get(UserBoxKey.userMid) != null) {
+ var userInfo = userInfoCache.get('userInfoCache');
+ if (userInfo != null && userInfo.mid != null) {
var cookie2 = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.tUrl));
if (cookie2.isEmpty) {
@@ -86,9 +87,10 @@ class Request {
},
);
- Box user = GStrorage.user;
- if (user.get(UserBoxKey.userMid) != null) {
- options.headers['x-bili-mid'] = user.get(UserBoxKey.userMid).toString();
+ Box userInfoCache = GStrorage.userInfo;
+ var userInfo = userInfoCache.get('userInfoCache');
+ if (userInfo != null && userInfo.mid != null) {
+ options.headers['x-bili-mid'] = userInfo.mid.toString();
options.headers['env'] = 'prod';
options.headers['app-key'] = 'android64';
options.headers['x-bili-aurora-eid'] = 'UlMFQVcABlAH';
diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart
index 2521d02d..9a86fbb9 100644
--- a/lib/http/interceptor.dart
+++ b/lib/http/interceptor.dart
@@ -17,7 +17,7 @@ class ApiInterceptor extends Interceptor {
handler.next(options);
}
- Box user = GStrorage.user;
+ Box localCache = GStrorage.localCache;
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
@@ -29,7 +29,8 @@ class ApiInterceptor extends Interceptor {
final uri = Uri.parse(locations.first);
final accessKey = uri.queryParameters['access_key'];
final mid = uri.queryParameters['mid'];
- user.put(UserBoxKey.accessKey, {'mid': mid, 'value': accessKey});
+ localCache
+ .put(LocalCacheKey.accessKey, {'mid': mid, 'value': accessKey});
}
}
}
diff --git a/lib/http/search.dart b/lib/http/search.dart
index 4a1a4fdd..5d99e3e0 100644
--- a/lib/http/search.dart
+++ b/lib/http/search.dart
@@ -46,14 +46,19 @@ class SearchHttp {
required SearchType searchType,
required String keyword,
required page,
+ String? order,
+ int? duration,
}) async {
- var res = await Request().get(Api.searchByType, data: {
+ var reqData = {
'search_type': searchType.type,
'keyword': keyword,
// 'order_sort': 0,
// 'user_type': 0,
- 'page': page
- });
+ 'page': page,
+ if (order != null) 'order': order,
+ if (duration != null) 'duration': duration,
+ };
+ var res = await Request().get(Api.searchByType, data: reqData);
if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) {
Object data;
switch (searchType) {
diff --git a/lib/http/user.dart b/lib/http/user.dart
index 050470fb..26739b02 100644
--- a/lib/http/user.dart
+++ b/lib/http/user.dart
@@ -1,3 +1,4 @@
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart';
@@ -197,8 +198,12 @@ class UserHttp {
'sign': Constants.thirdSign,
},
);
- if (res.data['code'] == 0 && res.data['data']['has_login'] == 1) {
- Request().get(res.data['data']['confirm_uri']);
+ try {
+ if (res.data['code'] == 0 && res.data['data']['has_login'] == 1) {
+ Request().get(res.data['data']['confirm_uri']);
+ }
+ } catch (err) {
+ SmartDialog.showNotify(msg: '获取用户凭证: $err', notifyType: NotifyType.error);
}
}
diff --git a/lib/http/video.dart b/lib/http/video.dart
index 18048377..f67702f0 100644
--- a/lib/http/video.dart
+++ b/lib/http/video.dart
@@ -18,7 +18,7 @@ import 'package:pilipala/utils/storage.dart';
/// 返回{'status': bool, 'data': List}
/// view层根据 status 判断渲染逻辑
class VideoHttp {
- static Box user = GStrorage.user;
+ static Box localCache = GStrorage.localCache;
static Box setting = GStrorage.setting;
// 首页推荐视频
@@ -61,8 +61,9 @@ class VideoHttp {
'device_name': 'vivo',
'pull': freshIdx == 0 ? 'true' : 'false',
'appkey': Constants.appKey,
- 'access_key':
- user.get(UserBoxKey.accessKey, defaultValue: {})['value'] ?? ''
+ 'access_key': localCache
+ .get(LocalCacheKey.accessKey, defaultValue: {})['value'] ??
+ ''
},
);
if (res.data['code'] == 0) {
@@ -137,7 +138,12 @@ class VideoHttp {
'data': PlayUrlModel.fromJson(res.data['data'])
};
} else {
- return {'status': false, 'data': []};
+ return {
+ 'status': false,
+ 'data': [],
+ 'code': res.data['code'],
+ 'msg': res.data['message'],
+ };
}
} catch (err) {
return {'status': false, 'data': [], 'msg': err};
@@ -154,13 +160,14 @@ class VideoHttp {
Map errMap = {
-400: '请求错误',
-403: '权限不足',
- -404: '无视频',
+ -404: '视频资源失效',
62002: '稿件不可见',
62004: '稿件审核中',
};
return {
'status': false,
'data': null,
+ 'code': result.code,
'msg': errMap[result.code] ?? '请求异常',
};
}
diff --git a/lib/models/common/search_type.dart b/lib/models/common/search_type.dart
index b4caecc1..491ee7b4 100644
--- a/lib/models/common/search_type.dart
+++ b/lib/models/common/search_type.dart
@@ -27,3 +27,20 @@ extension SearchTypeExtension on SearchType {
['video', 'media_bangumi', 'live_room', 'bili_user'][index];
String get label => ['视频', '番剧', '直播间', '用户'][index];
}
+
+// 搜索类型为视频、专栏及相簿时
+enum ArchiveFilterType {
+ totalrank,
+ click,
+ pubdate,
+ dm,
+ stow,
+ scores,
+ // 专栏
+ // attention,
+}
+
+extension ArchiveFilterTypeExtension on ArchiveFilterType {
+ String get description =>
+ ['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index];
+}
diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart
index 78991418..79a385b3 100644
--- a/lib/models/dynamics/result.dart
+++ b/lib/models/dynamics/result.dart
@@ -408,6 +408,7 @@ class DynamicMajorModel {
this.live,
this.none,
this.type,
+ this.courses,
});
DynamicArchiveModel? archive;
@@ -422,6 +423,7 @@ class DynamicMajorModel {
// MAJOR_TYPE_ARCHIVE 视频
// MAJOR_TYPE_OPUS 图文/文章
String? type;
+ Map? courses;
DynamicMajorModel.fromJson(Map json) {
archive = json['archive'] != null
@@ -444,6 +446,7 @@ class DynamicMajorModel {
none =
json['none'] != null ? DynamicNoneModel.fromJson(json['none']) : null;
type = json['type'];
+ courses = json['courses'] ?? {};
}
}
diff --git a/lib/models/dynamics/up.dart b/lib/models/dynamics/up.dart
index acc61bc1..cfd1fa7d 100644
--- a/lib/models/dynamics/up.dart
+++ b/lib/models/dynamics/up.dart
@@ -8,7 +8,9 @@ class FollowUpModel {
List? upList;
FollowUpModel.fromJson(Map json) {
- liveUsers = LiveUsers.fromJson(json['live_users']);
+ liveUsers = json['live_users'] != null
+ ? LiveUsers.fromJson(json['live_users'])
+ : null;
upList = json['up_list'] != null
? json['up_list'].map((e) => UpItem.fromJson(e)).toList()
: [];
diff --git a/lib/models/github/latest.dart b/lib/models/github/latest.dart
index 1b2d0706..8730a4ba 100644
--- a/lib/models/github/latest.dart
+++ b/lib/models/github/latest.dart
@@ -4,12 +4,14 @@ class LatestDataModel {
this.tagName,
this.createdAt,
this.assets,
+ this.body,
});
String? url;
String? tagName;
String? createdAt;
List? assets;
+ String? body;
LatestDataModel.fromJson(Map json) {
url = json['url'];
@@ -17,6 +19,7 @@ class LatestDataModel {
createdAt = json['created_at'];
assets =
json['assets'].map((e) => AssetItem.fromJson(e)).toList();
+ body = json['body'];
}
}
diff --git a/lib/models/search/suggest.dart b/lib/models/search/suggest.dart
index eff7cb50..ff161476 100644
--- a/lib/models/search/suggest.dart
+++ b/lib/models/search/suggest.dart
@@ -1,3 +1,6 @@
+import 'package:flutter/material.dart';
+import 'package:get/get.dart';
+
class SearchSuggestModel {
SearchSuggestModel({
this.tag,
@@ -19,32 +22,74 @@ class SearchSuggestItem {
SearchSuggestItem({
this.value,
this.term,
- this.name,
this.spid,
+ this.textRich,
});
String? value;
String? term;
- List? name;
int? spid;
+ Widget? textRich;
SearchSuggestItem.fromJson(Map json, String inputTerm) {
value = json['value'];
term = json['term'];
- String reg = '$inputTerm';
- try {
- if (json['name'].indexOf(inputTerm) != -1) {
- String str = json['name'].replaceAll(reg, '^');
- List arr = str.split('^');
- arr.insert(arr.length - 1, inputTerm);
- name = arr;
- } else {
- name = ['', '', json['term']];
- }
- } catch (err) {
- name = ['', '', json['term']];
- }
-
- spid = json['spid'];
+ textRich = highlightText(json['name']);
}
}
+
+Widget highlightText(String str) {
+ // 创建正则表达式,匹配 ... 格式的文本
+ RegExp regex = RegExp(r'(.*?)<\/em>');
+
+ // 用于存储每个匹配项的列表
+ List children = [];
+
+ // 获取所有匹配项
+ Iterable matches = regex.allMatches(str);
+
+ // 当前索引位置
+ int currentIndex = 0;
+
+ // 遍历每个匹配项
+ for (var match in matches) {
+ // 获取当前匹配项之前的普通文本部分
+ String normalText = str.substring(currentIndex, match.start);
+
+ // 获取需要高亮显示的文本部分
+ String highlightedText = match.group(1)!;
+
+ // 如果普通文本部分不为空,则将其添加到 children 列表中
+ if (normalText.isNotEmpty) {
+ children.add(TextSpan(
+ text: normalText,
+ style: DefaultTextStyle.of(Get.context!).style,
+ ));
+ }
+
+ // 将需要高亮显示的文本部分添加到 children 列表中,并设置相应样式
+ children.add(TextSpan(
+ text: highlightedText,
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ color: Theme.of(Get.context!).colorScheme.primary),
+ ));
+
+ // 更新当前索引位置
+ currentIndex = match.end;
+ }
+
+ // 如果当前索引位置小于文本长度,表示还有剩余的普通文本部分
+ if (currentIndex < str.length) {
+ String remainingText = str.substring(currentIndex);
+
+ // 将剩余的普通文本部分添加到 children 列表中
+ children.add(TextSpan(
+ text: remainingText,
+ style: DefaultTextStyle.of(Get.context!).style,
+ ));
+ }
+
+ // 使用 Text.rich 创建包含高亮显示的富文本小部件,并返回
+ return Text.rich(TextSpan(children: children));
+}
diff --git a/lib/models/user/history.dart b/lib/models/user/history.dart
index 19698aa9..669874b4 100644
--- a/lib/models/user/history.dart
+++ b/lib/models/user/history.dart
@@ -121,7 +121,7 @@ class HisListItem {
viewAt = json['view_at'];
progress = json['progress'];
badge = json['badge'];
- showTitle = json['show_title'];
+ showTitle = json['show_title'] == '' ? null : json['show_title'];
duration = json['duration'];
current = json['current'];
total = json['total'];
diff --git a/lib/models/user/info.dart b/lib/models/user/info.dart
index 143c4db2..c520443f 100644
--- a/lib/models/user/info.dart
+++ b/lib/models/user/info.dart
@@ -43,7 +43,7 @@ class UserInfoData {
@HiveField(5)
int? mobileVerified;
@HiveField(6)
- int? money;
+ double? money;
@HiveField(7)
int? moral;
@HiveField(8)
@@ -88,7 +88,7 @@ class UserInfoData {
: LevelInfo();
mid = json['mid'];
mobileVerified = json['mobile_verified'];
- money = json['money'];
+ money = json['money'] is int ? json['money'].toDouble() : json['money'];
moral = json['moral'];
official = json['official'];
officialVerify = json['officialVerify'];
@@ -130,6 +130,7 @@ class LevelInfo {
currentLevel = json['current_level'];
currentMin = json['current_min'];
currentExp = json['current_exp'];
- nextExp = json['next_exp'];
+ nextExp =
+ json['current_level'] == 6 ? json['current_exp'] : json['next_exp'];
}
}
diff --git a/lib/models/user/info.g.dart b/lib/models/user/info.g.dart
index ed3aa62c..a2eae152 100644
--- a/lib/models/user/info.g.dart
+++ b/lib/models/user/info.g.dart
@@ -23,7 +23,7 @@ class UserInfoDataAdapter extends TypeAdapter {
levelInfo: fields[3] as LevelInfo?,
mid: fields[4] as int?,
mobileVerified: fields[5] as int?,
- money: fields[6] as int?,
+ money: fields[6] as double?,
moral: fields[7] as int?,
official: (fields[8] as Map?)?.cast(),
officialVerify: (fields[9] as Map?)?.cast(),
diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart
index 66c66abb..04a5efec 100644
--- a/lib/pages/about/index.dart
+++ b/lib/pages/about/index.dart
@@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart';
-import 'package:path_provider/path_provider.dart';
import 'package:pilipala/http/index.dart';
import 'package:pilipala/models/github/latest.dart';
import 'package:pilipala/utils/utils.dart';
@@ -165,7 +164,7 @@ class AboutController extends GetxController {
}
}
- // 获取啊当前版本
+ // 获取当前版本
Future getCurrentApp() async {
var result = await PackageInfo.fromPlatform();
currentVersion.value = result.version;
diff --git a/lib/pages/bangumi/controller.dart b/lib/pages/bangumi/controller.dart
index 7f9f6a61..09afc43a 100644
--- a/lib/pages/bangumi/controller.dart
+++ b/lib/pages/bangumi/controller.dart
@@ -11,17 +11,19 @@ class BangumiController extends GetxController {
RxList bangumiFollowList = [BangumiListItemModel()].obs;
int _currentPage = 1;
bool isLoadingMore = true;
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
RxBool userLogin = false.obs;
late int mid;
+ var userInfo;
@override
void onInit() {
super.onInit();
- if (user.get(UserBoxKey.userMid) != null) {
- mid = int.parse(user.get(UserBoxKey.userMid).toString());
+ userInfo = userInfoCache.get('userInfoCache');
+ if (userInfo != null) {
+ mid = userInfo.mid;
}
- userLogin.value = user.get(UserBoxKey.userLogin) != null;
+ userLogin.value = userInfo != null;
}
Future queryBangumiListFeed({type = 'init'}) async {
@@ -48,7 +50,11 @@ class BangumiController extends GetxController {
// 我的订阅
Future queryBangumiFollow() async {
- var result = await BangumiHttp.bangumiFollow(mid: 17340771);
+ userInfo = userInfo ?? userInfoCache.get('userInfoCache');
+ if (userInfo == null) {
+ return;
+ }
+ var result = await BangumiHttp.bangumiFollow(mid: userInfo.mid);
if (result['status']) {
bangumiFollowList.value = result['data'].list;
} else {}
diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart
index b0ce9452..e63e797d 100644
--- a/lib/pages/bangumi/introduction/controller.dart
+++ b/lib/pages/bangumi/introduction/controller.dart
@@ -49,7 +49,7 @@ class BangumiIntroController extends GetxController {
RxBool hasCoin = false.obs;
// 是否收藏
RxBool hasFav = false.obs;
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
bool userLogin = false;
Rx favFolderData = FavFolderData().obs;
List addMediaIdsNew = [];
@@ -57,6 +57,7 @@ class BangumiIntroController extends GetxController {
// 关注状态 默认未关注
RxMap followStatus = {}.obs;
int _tempThemeValue = -1;
+ var userInfo;
@override
void onInit() {
@@ -82,7 +83,8 @@ class BangumiIntroController extends GetxController {
// videoItem!['owner'] = args.owner;
}
}
- userLogin = user.get(UserBoxKey.userLogin) != null;
+ userInfo = userInfoCache.get('userInfoCache');
+ userLogin = userInfo != null;
}
// 获取番剧简介&选集
@@ -142,7 +144,7 @@ class BangumiIntroController extends GetxController {
// 投币
Future actionCoinVideo() async {
- if (user.get(UserBoxKey.userMid) == null) {
+ if (userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
@@ -283,7 +285,7 @@ class BangumiIntroController extends GetxController {
Future queryVideoInFolder() async {
var result = await VideoHttp.videoInFolder(
- mid: user.get(UserBoxKey.userMid), rid: IdUtils.bv2av(bvid));
+ mid: userInfo.mid, rid: IdUtils.bv2av(bvid));
if (result['status']) {
favFolderData.value = result['data'];
}
diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart
index 456f532c..7d31c108 100644
--- a/lib/pages/bangumi/introduction/view.dart
+++ b/lib/pages/bangumi/introduction/view.dart
@@ -121,7 +121,7 @@ class _BangumiInfoState extends State {
// 收藏
showFavBottomSheet() {
- if (bangumiIntroController.user.get(UserBoxKey.userMid) == null) {
+ if (bangumiIntroController.userInfo.mid == null) {
SmartDialog.showToast('账号未登录');
return;
}
diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart
index 6c25e508..3032bb4d 100644
--- a/lib/pages/bangumi/view.dart
+++ b/lib/pages/bangumi/view.dart
@@ -1,5 +1,6 @@
import 'dart:async';
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@@ -22,24 +23,28 @@ class _BangumiPageState extends State
with AutomaticKeepAliveClientMixin {
final BangumiController _bangumidController = Get.put(BangumiController());
late Future? _futureBuilderFuture;
+ late Future? _futureBuilderFutureFollow;
+ late ScrollController scrollController;
+
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
- ScrollController scrollController = _bangumidController.scrollController;
+ scrollController = _bangumidController.scrollController;
StreamController mainStream =
Get.find().bottomBarStream;
_futureBuilderFuture = _bangumidController.queryBangumiListFeed();
+ _futureBuilderFutureFollow = _bangumidController.queryBangumiFollow();
scrollController.addListener(
() async {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
- if (!_bangumidController.isLoadingMore) {
+ EasyThrottle.throttle('my-throttler', const Duration(seconds: 1), () {
_bangumidController.isLoadingMore = true;
- await _bangumidController.onLoad();
- }
+ _bangumidController.onLoad();
+ });
}
final ScrollDirection direction =
@@ -53,6 +58,12 @@ class _BangumiPageState extends State
);
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
super.build(context);
@@ -80,43 +91,61 @@ class _BangumiPageState extends State
'最近追番',
style: Theme.of(context).textTheme.titleMedium,
),
+ IconButton(
+ onPressed: () {
+ setState(() {
+ _futureBuilderFutureFollow =
+ _bangumidController.queryBangumiFollow();
+ });
+ },
+ icon: const Icon(
+ Icons.refresh,
+ size: 20,
+ ),
+ ),
],
),
),
SizedBox(
height: 258,
child: FutureBuilder(
- future: _bangumidController.queryBangumiFollow(),
+ future: _futureBuilderFutureFollow,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
Map data = snapshot.data as Map;
+ List list = _bangumidController.bangumiFollowList;
if (data['status']) {
return Obx(
- () => ListView.builder(
- scrollDirection: Axis.horizontal,
- itemCount: _bangumidController
- .bangumiFollowList.length,
- itemBuilder: (context, index) {
- return Container(
- width: Get.size.width / 3,
- height: 254,
- margin: EdgeInsets.only(
- left: StyleString.safeSpace,
- right: index ==
- _bangumidController
- .bangumiFollowList
- .length -
- 1
- ? StyleString.safeSpace
- : 0),
- child: BangumiCardV(
- bangumiItem: _bangumidController
- .bangumiFollowList[index],
+ () => list.isNotEmpty
+ ? ListView.builder(
+ scrollDirection: Axis.horizontal,
+ itemCount: list.length,
+ itemBuilder: (context, index) {
+ return Container(
+ width: Get.size.width / 3,
+ height: 254,
+ margin: EdgeInsets.only(
+ left: StyleString.safeSpace,
+ right: index ==
+ _bangumidController
+ .bangumiFollowList
+ .length -
+ 1
+ ? StyleString.safeSpace
+ : 0),
+ child: BangumiCardV(
+ bangumiItem: _bangumidController
+ .bangumiFollowList[index],
+ ),
+ );
+ },
+ )
+ : const SizedBox(
+ child: Center(
+ child: Text('还没有追番'),
+ ),
),
- );
- },
- ),
);
} else {
return const SizedBox();
diff --git a/lib/pages/blacklist/index.dart b/lib/pages/blacklist/index.dart
index 27aa770f..63792532 100644
--- a/lib/pages/blacklist/index.dart
+++ b/lib/pages/blacklist/index.dart
@@ -46,6 +46,7 @@ class _BlackListPageState extends State {
List blackMidsList =
_blackListController.blackList.map((e) => e.mid!).toList();
setting.put(SettingBoxKey.blackMidsList, blackMidsList);
+ scrollController.removeListener(() {});
super.dispose();
}
diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart
index 5bef4794..d8417d50 100644
--- a/lib/pages/dynamics/controller.dart
+++ b/lib/pages/dynamics/controller.dart
@@ -51,29 +51,42 @@ class DynamicsController extends GetxController {
];
bool flag = false;
RxInt initialValue = 1.obs;
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
RxBool userLogin = false.obs;
+ var userInfo;
+ RxBool isLoadingDynamic = false.obs;
@override
void onInit() {
- userLogin.value = user.get(UserBoxKey.userLogin, defaultValue: false);
+ userInfo = userInfoCache.get('userInfoCache');
+ userLogin.value = userInfo != null;
super.onInit();
}
Future queryFollowDynamic({type = 'init'}) async {
if (!userLogin.value) {
- return {'status': false, 'msg': '未登录'};
+ return {'status': false, 'msg': '账号未登录'};
}
if (type == 'init') {
dynamicsList.clear();
}
+ // 下拉刷新数据渲染时会触发onLoad
+ if (type == 'onLoad' && page == 1) {
+ return;
+ }
+ isLoadingDynamic.value = true;
var res = await DynamicsHttp.followDynamic(
page: type == 'init' ? 1 : page,
type: dynamicsType.value.values,
offset: offset,
mid: mid.value,
);
+ isLoadingDynamic.value = false;
if (res['status']) {
+ if (type == 'onLoad' && res['data'].items.isEmpty) {
+ SmartDialog.showToast('没有更多了');
+ return;
+ }
if (type == 'init') {
dynamicsList.value = res['data'].items;
} else {
@@ -188,12 +201,19 @@ class DynamicsController extends GetxController {
}
Future queryFollowUp({type = 'init'}) async {
+ if (!userLogin.value) {
+ return {'status': false, 'msg': '账号未登录'};
+ }
if (type == 'init') {
- upData = FollowUpModel().obs;
+ upData.value.upList = [];
+ upData.value.liveUsers = LiveUsers();
}
var res = await DynamicsHttp.followUp();
if (res['status']) {
upData.value = res['data'];
+ if (upData.value.upList!.isEmpty) {
+ mid.value = -1;
+ }
}
return res;
}
@@ -207,7 +227,8 @@ class DynamicsController extends GetxController {
onRefresh() async {
page = 1;
- queryFollowUp();
+ print('onRefresh');
+ await queryFollowUp();
await queryFollowDynamic();
}
@@ -227,7 +248,7 @@ class DynamicsController extends GetxController {
mid.value = -1;
dynamicsType.value = DynamicsType.values[0];
initialValue.value = 1;
- SmartDialog.showToast('还原默认加载', alignment: Alignment.topCenter);
+ SmartDialog.showToast('还原默认加载');
dynamicsList.value = [DynamicItemModel()];
queryFollowDynamic();
}
diff --git a/lib/pages/dynamics/deatil/view.dart b/lib/pages/dynamics/deatil/view.dart
index 129494c9..6a779ba3 100644
--- a/lib/pages/dynamics/deatil/view.dart
+++ b/lib/pages/dynamics/deatil/view.dart
@@ -1,5 +1,6 @@
import 'dart:async';
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_reply.dart';
@@ -40,7 +41,9 @@ class _DynamicDetailPageState extends State {
} else {
oid = Get.arguments['item'].modules.moduleDynamic.major.draw.id;
}
- type = Get.arguments['item'].basic!['comment_type'];
+ int commentType = Get.arguments['item'].basic!['comment_type'] ?? 11;
+ type = (commentType == 0) ? 11 : commentType;
+
action =
Get.arguments.containsKey('action') ? Get.arguments['action'] : null;
_dynamicDetailController = Get.put(DynamicDetailController(oid, type));
@@ -56,10 +59,9 @@ class _DynamicDetailPageState extends State {
void _listen() async {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) {
- if (!_dynamicDetailController!.isLoadingMore) {
- _dynamicDetailController!.isLoadingMore = true;
- await _dynamicDetailController!.queryReplyList(reqType: 'onLoad');
- }
+ EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
+ _dynamicDetailController!.queryReplyList(reqType: 'onLoad');
+ });
}
if (scrollController.offset > 55 && !_visibleTitle) {
@@ -95,6 +97,12 @@ class _DynamicDetailPageState extends State {
);
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -236,6 +244,11 @@ class _DynamicDetailPageState extends State {
replyReply: (replyItem) =>
replyReply(replyItem),
replyType: ReplyType.values[type],
+ addReply: (replyItem) {
+ _dynamicDetailController!
+ .replyList[index].replies!
+ .add(replyItem);
+ },
);
}
},
diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart
index 4f6b6a40..a29e1f91 100644
--- a/lib/pages/dynamics/view.dart
+++ b/lib/pages/dynamics/view.dart
@@ -1,12 +1,14 @@
import 'dart:async';
import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/common/skeleton/dynamic_card.dart';
import 'package:pilipala/common/widgets/http_error.dart';
+import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/models/dynamics/result.dart';
import 'package:pilipala/pages/main/index.dart';
import 'package:pilipala/utils/event_bus.dart';
@@ -29,9 +31,9 @@ class _DynamicsPageState extends State
final DynamicsController _dynamicsController = Get.put(DynamicsController());
late Future _futureBuilderFuture;
late Future _futureBuilderFutureUp;
- bool _isLoadingMore = false;
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
EventBus eventBus = EventBus();
+ late ScrollController scrollController;
@override
bool get wantKeepAlive => true;
@@ -41,18 +43,17 @@ class _DynamicsPageState extends State
super.initState();
_futureBuilderFuture = _dynamicsController.queryFollowDynamic();
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
- ScrollController scrollController = _dynamicsController.scrollController;
+ scrollController = _dynamicsController.scrollController;
StreamController mainStream =
Get.find().bottomBarStream;
scrollController.addListener(
() async {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
- if (!_isLoadingMore) {
- _isLoadingMore = true;
- await _dynamicsController.queryFollowDynamic(type: 'onLoad');
- _isLoadingMore = false;
- }
+ EasyThrottle.throttle(
+ 'queryFollowDynamic', const Duration(seconds: 1), () {
+ _dynamicsController.queryFollowDynamic(type: 'onLoad');
+ });
}
final ScrollDirection direction =
@@ -74,6 +75,12 @@ class _DynamicsPageState extends State
});
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
super.build(context);
@@ -145,14 +152,12 @@ class _DynamicsPageState extends State
.textTheme
.labelMedium!
.fontSize)),
- // 4: Text(
- // '专栏',
- // style: TextStyle(
- // fontSize: Theme.of(context)
- // .textTheme
- // .labelMedium!
- // .fontSize),
- // ),
+ 4: Text('专栏',
+ style: TextStyle(
+ fontSize: Theme.of(context)
+ .textTheme
+ .labelMedium!
+ .fontSize)),
},
padding: 13.0,
decoration: BoxDecoration(
@@ -179,22 +184,22 @@ class _DynamicsPageState extends State
)
],
),
- 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),
- ),
- ),
- ),
- ),
+ // 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),
+ // ),
+ // ),
+ // ),
+ // ),
],
),
),
@@ -233,14 +238,24 @@ class _DynamicsPageState extends State
List list =
_dynamicsController.dynamicsList;
return Obx(
- () => list.isEmpty
- ? skeleton()
- : SliverList(
- delegate:
- SliverChildBuilderDelegate((context, index) {
+ () {
+ if (list.isEmpty) {
+ if (_dynamicsController.isLoadingDynamic.value) {
+ return skeleton();
+ } else {
+ return const NoData();
+ }
+ } else {
+ return SliverList(
+ delegate: SliverChildBuilderDelegate(
+ (context, index) {
return DynamicPanel(item: list[index]);
- }, childCount: list.length),
+ },
+ childCount: list.length,
),
+ );
+ }
+ },
);
} else {
return HttpError(
@@ -261,6 +276,7 @@ class _DynamicsPageState extends State
}
},
),
+ const SliverToBoxAdapter(child: SizedBox(height: 40))
],
),
),
diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart
index 34b7f6af..53c9e497 100644
--- a/lib/pages/dynamics/widgets/action_panel.dart
+++ b/lib/pages/dynamics/widgets/action_panel.dart
@@ -37,7 +37,7 @@ class _ActionPanelState extends State {
String dynamicId = item.idStr!;
// 1 已点赞 2 不喜欢 0 未操作
Like like = item.modules.moduleStat.like;
- int count = int.parse(like.count!);
+ int count = like.count == '点赞' ? 0 : int.parse(like.count ?? '0');
bool status = like.status!;
int up = status ? 2 : 1;
var res = await DynamicsHttp.likeDynamic(dynamicId: dynamicId, up: up);
@@ -47,7 +47,11 @@ class _ActionPanelState extends State {
item.modules.moduleStat.like.count = (count + 1).toString();
item.modules.moduleStat.like.status = true;
} else {
- item.modules.moduleStat.like.count = (count - 1).toString();
+ if (count == 1) {
+ item.modules.moduleStat.like.count = '点赞';
+ } else {
+ item.modules.moduleStat.like.count = (count - 1).toString();
+ }
item.modules.moduleStat.like.status = false;
}
setState(() {});
@@ -63,54 +67,63 @@ class _ActionPanelState extends State {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
- TextButton.icon(
- onPressed: () {},
- icon: const Icon(
- FontAwesomeIcons.shareFromSquare,
- size: 16,
+ Expanded(
+ flex: 1,
+ child: TextButton.icon(
+ onPressed: () {},
+ icon: const Icon(
+ FontAwesomeIcons.shareFromSquare,
+ size: 16,
+ ),
+ style: TextButton.styleFrom(
+ padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
+ foregroundColor: Theme.of(context).colorScheme.outline,
+ ),
+ label: Text(stat.forward!.count ?? '转发'),
),
- style: TextButton.styleFrom(
- padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
- foregroundColor: Theme.of(context).colorScheme.outline,
- ),
- label: Text(stat.forward!.count ?? '转发'),
),
- TextButton.icon(
- onPressed: () =>
- _dynamicsController.pushDetail(widget.item, 1, action: 'comment'),
- icon: const Icon(
- FontAwesomeIcons.comment,
- size: 16,
+ Expanded(
+ flex: 1,
+ child: TextButton.icon(
+ onPressed: () => _dynamicsController.pushDetail(widget.item, 1,
+ action: 'comment'),
+ icon: const Icon(
+ FontAwesomeIcons.comment,
+ size: 16,
+ ),
+ style: TextButton.styleFrom(
+ padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
+ foregroundColor: Theme.of(context).colorScheme.outline,
+ ),
+ label: Text(stat.comment!.count ?? '评论'),
),
- style: TextButton.styleFrom(
- padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
- foregroundColor: Theme.of(context).colorScheme.outline,
- ),
- label: Text(stat.comment!.count ?? '评论'),
),
- TextButton.icon(
- onPressed: () => onLikeDynamic(),
- icon: Icon(
- stat.like!.status!
- ? FontAwesomeIcons.solidThumbsUp
- : FontAwesomeIcons.thumbsUp,
- size: 16,
- color: stat.like!.status! ? primary : color,
- ),
- style: TextButton.styleFrom(
- padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
- foregroundColor: Theme.of(context).colorScheme.outline,
- ),
- label: AnimatedSwitcher(
- duration: const Duration(milliseconds: 400),
- transitionBuilder: (Widget child, Animation animation) {
- return ScaleTransition(scale: animation, child: child);
- },
- child: Text(
- stat.like!.count ?? '点赞',
- key: ValueKey(stat.like!.count ?? '点赞'),
- style: TextStyle(
- color: stat.like!.status! ? primary : color,
+ Expanded(
+ flex: 1,
+ child: TextButton.icon(
+ onPressed: () => onLikeDynamic(),
+ icon: Icon(
+ stat.like!.status!
+ ? FontAwesomeIcons.solidThumbsUp
+ : FontAwesomeIcons.thumbsUp,
+ size: 16,
+ color: stat.like!.status! ? primary : color,
+ ),
+ style: TextButton.styleFrom(
+ padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
+ foregroundColor: Theme.of(context).colorScheme.outline,
+ ),
+ label: AnimatedSwitcher(
+ duration: const Duration(milliseconds: 400),
+ transitionBuilder: (Widget child, Animation animation) {
+ return ScaleTransition(scale: animation, child: child);
+ },
+ child: Text(
+ stat.like!.count ?? '点赞',
+ key: ValueKey(stat.like!.count ?? '点赞'),
+ style: TextStyle(
+ color: stat.like!.status! ? primary : color,
+ ),
),
),
),
diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart
index b3d6e98e..e283fcf9 100644
--- a/lib/pages/dynamics/widgets/additional_panel.dart
+++ b/lib/pages/dynamics/widgets/additional_panel.dart
@@ -60,43 +60,47 @@ Widget addWidget(item, context, type, {floor = 1}) {
),
);
case 'ADDITIONAL_TYPE_RESERVE':
- 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),
+ return dynamicProperty[type].state != -1
+ ? 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: [
- TextSpan(text: dynamicProperty[type].desc1['text']),
- const TextSpan(text: ' '),
- TextSpan(text: dynamicProperty[type].desc2['text']),
+ 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'))
- ),
- ),
- );
+ // TextButton(onPressed: () {}, child: Text('123'))
+ ),
+ ),
+ )
+ : const SizedBox();
case 'ADDITIONAL_TYPE_GOODS':
return Padding(
padding: const EdgeInsets.only(top: 6),
diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart
index e9e290b3..55972e37 100644
--- a/lib/pages/dynamics/widgets/forward_panel.dart
+++ b/lib/pages/dynamics/widgets/forward_panel.dart
@@ -100,6 +100,7 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
// 直播
case 'DYNAMIC_TYPE_LIVE_RCMD':
return liveRcmdPanel(item, context, floor: floor);
+ // 直播
case 'DYNAMIC_TYPE_LIVE':
return livePanel(item, context, floor: floor);
// 合集
@@ -147,6 +148,7 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
return videoSeasonWidget(item, context, 'pgc', floor: floor);
case 'DYNAMIC_TYPE_PGC_UNION':
return videoSeasonWidget(item, context, 'pgc', floor: floor);
+ // 直播结束
case 'DYNAMIC_TYPE_NONE':
return Row(
children: [
@@ -158,7 +160,23 @@ Widget forWard(item, context, ctr, source, {floor = 1}) {
Text(item.modules.moduleDynamic.major.none.tips)
],
);
+ // 课堂
+ case 'DYNAMIC_TYPE_COURSES_SEASON':
+ return Row(
+ children: [
+ Expanded(
+ child: Text(
+ "课堂💪:${item.modules.moduleDynamic.major.courses['title']}",
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ )
+ ],
+ );
default:
- return const SizedBox(height: 0);
+ return const SizedBox(
+ width: double.infinity,
+ child: Text('🙏 暂未支持的类型,请联系开发者反馈 '),
+ );
}
}
diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart
index 2c0a63f7..a12956a5 100644
--- a/lib/pages/dynamics/widgets/up_panel.dart
+++ b/lib/pages/dynamics/widgets/up_panel.dart
@@ -24,24 +24,28 @@ class _UpPanelState extends State {
List upList = [];
List liveList = [];
static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0);
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
+ var userInfo;
@override
void initState() {
super.initState();
upList = widget.upData!.upList!;
- liveList = widget.upData!.liveUsers!.items!;
+ if (widget.upData!.liveUsers != null) {
+ liveList = widget.upData!.liveUsers!.items!;
+ }
upList.insert(
0,
UpItem(
face: 'https://files.catbox.moe/8uc48f.png', uname: '全部动态', mid: -1),
);
+ userInfo = userInfoCache.get('userInfoCache');
upList.insert(
1,
UpItem(
- face: user.get(UserBoxKey.userFace),
+ face: userInfo.face,
uname: '我',
- mid: user.get(UserBoxKey.userMid),
+ mid: userInfo.mid,
),
);
}
@@ -64,15 +68,20 @@ class _UpPanelState extends State {
controller: scrollController,
children: [
const SizedBox(width: 10),
- for (int i = 0; i < liveList.length; i++) ...[
- upItemBuild(liveList[i], i)
+ if (liveList.isNotEmpty) ...[
+ for (int i = 0; i < liveList.length; i++) ...[
+ upItemBuild(liveList[i], i)
+ ],
+ VerticalDivider(
+ indent: 20,
+ endIndent: 40,
+ width: 26,
+ color: Theme.of(context)
+ .colorScheme
+ .primary
+ .withOpacity(0.5),
+ ),
],
- VerticalDivider(
- indent: 20,
- endIndent: 40,
- width: 26,
- color: Theme.of(context).primaryColor.withOpacity(0.5),
- ),
for (int i = 0; i < upList.length; i++) ...[
upItemBuild(upList[i], i)
],
@@ -123,7 +132,8 @@ class _UpPanelState extends State {
double itemWidth = contentWidth + itemPadding.horizontal;
double screenWidth = MediaQuery.of(context).size.width;
double moveDistance = 0.0;
- if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
+ if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
+ } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
moveDistance =
(i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
} else {
diff --git a/lib/pages/fan/controller.dart b/lib/pages/fan/controller.dart
index cfbbc90c..4f389b92 100644
--- a/lib/pages/fan/controller.dart
+++ b/lib/pages/fan/controller.dart
@@ -5,19 +5,22 @@ import 'package:pilipala/models/fans/result.dart';
import 'package:pilipala/utils/storage.dart';
class FansController extends GetxController {
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
int pn = 1;
int total = 0;
RxList fansList = [FansItemModel()].obs;
late int mid;
late String name;
+ var userInfo;
@override
void onInit() {
super.onInit();
- mid = int.parse(
- Get.parameters['mid'] ?? user.get(UserBoxKey.userMid).toString());
- name = Get.parameters['name'] ?? user.get(UserBoxKey.userName);
+ userInfo = userInfoCache.get('userInfoCache');
+ mid = Get.parameters['mid'] != null
+ ? int.parse(Get.parameters['mid']!)
+ : userInfo.mid;
+ name = Get.parameters['name'] ?? userInfo.uname;
}
Future queryFans(type) async {
diff --git a/lib/pages/fan/view.dart b/lib/pages/fan/view.dart
index e17d87d2..a0b42528 100644
--- a/lib/pages/fan/view.dart
+++ b/lib/pages/fan/view.dart
@@ -37,6 +37,12 @@ class _FansPageState extends State {
);
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart
index 80162297..41923449 100644
--- a/lib/pages/fav/controller.dart
+++ b/lib/pages/fav/controller.dart
@@ -1,16 +1,24 @@
import 'package:get/get.dart';
+import 'package:hive/hive.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/user/fav_folder.dart';
+import 'package:pilipala/models/user/info.dart';
import 'package:pilipala/utils/storage.dart';
class FavController extends GetxController {
Rx favFolderData = FavFolderData().obs;
+ Box userInfoCache = GStrorage.userInfo;
+ UserInfoData? userInfo;
Future queryFavFolder() async {
+ userInfo = userInfoCache.get('userInfoCache');
+ if (userInfo == null) {
+ return {'status': false, 'msg': '账号未登录'};
+ }
var res = await await UserHttp.userfavFolder(
pn: 1,
ps: 10,
- mid: GStrorage.user.get(UserBoxKey.userMid) ?? 0,
+ mid: userInfo!.mid!,
);
if (res['status']) {
favFolderData.value = res['data'];
diff --git a/lib/pages/favDetail/view.dart b/lib/pages/favDetail/view.dart
index 88c2cb5f..d90d4f11 100644
--- a/lib/pages/favDetail/view.dart
+++ b/lib/pages/favDetail/view.dart
@@ -61,6 +61,7 @@ class _FavDetailPageState extends State {
SliverAppBar(
expandedHeight: 260 - MediaQuery.of(context).padding.top,
pinned: true,
+ titleSpacing: 0,
title: StreamBuilder(
stream: titleStreamC.stream,
initialData: false,
diff --git a/lib/pages/follow/controller.dart b/lib/pages/follow/controller.dart
index 6b8d80c2..a64e20f6 100644
--- a/lib/pages/follow/controller.dart
+++ b/lib/pages/follow/controller.dart
@@ -5,19 +5,22 @@ import 'package:pilipala/models/follow/result.dart';
import 'package:pilipala/utils/storage.dart';
class FollowController extends GetxController {
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
int pn = 1;
int total = 0;
RxList followList = [FollowItemModel()].obs;
late int mid;
late String name;
+ var userInfo;
@override
void onInit() {
super.onInit();
- mid = int.parse(
- Get.parameters['mid'] ?? user.get(UserBoxKey.userMid).toString());
- name = Get.parameters['name'] ?? user.get(UserBoxKey.userName);
+ userInfo = userInfoCache.get('userInfoCache');
+ mid = Get.parameters['mid'] != null
+ ? int.parse(Get.parameters['mid']!)
+ : userInfo.mid;
+ name = Get.parameters['name'] ?? userInfo.uname;
}
Future queryFollowings(type) async {
diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart
index ec46c0c5..d2a4b423 100644
--- a/lib/pages/follow/view.dart
+++ b/lib/pages/follow/view.dart
@@ -37,6 +37,12 @@ class _FollowPageState extends State {
);
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
diff --git a/lib/pages/history/controller.dart b/lib/pages/history/controller.dart
index 7dc8e3a5..ae897499 100644
--- a/lib/pages/history/controller.dart
+++ b/lib/pages/history/controller.dart
@@ -9,9 +9,10 @@ import 'package:pilipala/utils/storage.dart';
class HistoryController extends GetxController {
final ScrollController scrollController = ScrollController();
RxList historyList = [HisListItem()].obs;
- bool isLoadingMore = false;
+ RxBool isLoadingMore = false.obs;
RxBool pauseStatus = false.obs;
Box localCache = GStrorage.localCache;
+ RxBool isLoading = false.obs;
@override
void onInit() {
@@ -26,9 +27,9 @@ class HistoryController extends GetxController {
max = historyList.last.history!.oid!;
viewAt = historyList.last.viewAt!;
}
- isLoadingMore = true;
+ isLoadingMore.value = true;
var res = await UserHttp.historyList(max, viewAt);
- isLoadingMore = false;
+ isLoadingMore.value = false;
if (res['status']) {
if (type == 'onload') {
historyList.addAll(res['data'].list);
diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart
index 3de41366..c0118819 100644
--- a/lib/pages/history/view.dart
+++ b/lib/pages/history/view.dart
@@ -1,7 +1,9 @@
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
+import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/pages/history/index.dart';
import 'widgets/item.dart';
@@ -16,25 +18,33 @@ class HistoryPage extends StatefulWidget {
class _HistoryPageState extends State {
final HistoryController _historyController = Get.put(HistoryController());
Future? _futureBuilderFuture;
+ late ScrollController scrollController;
@override
void initState() {
_futureBuilderFuture = _historyController.queryHistoryList();
super.initState();
-
- _historyController.scrollController.addListener(
+ scrollController = _historyController.scrollController;
+ scrollController.addListener(
() {
- if (_historyController.scrollController.position.pixels >=
- _historyController.scrollController.position.maxScrollExtent -
- 300) {
- if (!_historyController.isLoadingMore) {
- _historyController.onLoad();
+ if (scrollController.position.pixels >=
+ scrollController.position.maxScrollExtent - 300) {
+ if (!_historyController.isLoadingMore.value) {
+ EasyThrottle.throttle('history', const Duration(seconds: 1), () {
+ _historyController.onLoad();
+ });
}
}
},
);
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -92,13 +102,8 @@ class _HistoryPageState extends State {
Map data = snapshot.data;
if (data['status']) {
return Obx(
- () => _historyController.historyList.isEmpty
- ? const SliverToBoxAdapter(
- child: Center(
- child: Text('没数据'),
- ),
- )
- : SliverList(
+ () => _historyController.historyList.isNotEmpty
+ ? SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return HistoryItem(
@@ -108,7 +113,12 @@ class _HistoryPageState extends State {
},
childCount:
_historyController.historyList.length),
- ),
+ )
+ : _historyController.isLoadingMore.value
+ ? const SliverToBoxAdapter(
+ child: Center(child: Text('加载中')),
+ )
+ : const NoData(),
);
} else {
return HttpError(
diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart
index b3dd7f0f..a368a978 100644
--- a/lib/pages/history/widgets/item.dart
+++ b/lib/pages/history/widgets/item.dart
@@ -37,20 +37,23 @@ class HistoryItem extends StatelessWidget {
'pageTitle': videoItem.title
},
);
- } else if (videoItem.history.business == 'live' &&
- videoItem.liveStatus == 1) {
- LiveItemModel liveItem = LiveItemModel.fromJson({
- 'face': videoItem.authorFace,
- 'roomid': videoItem.history.oid,
- 'pic': videoItem.cover,
- 'title': videoItem.title,
- 'uname': videoItem.authorName,
- 'cover': videoItem.cover,
- });
- Get.toNamed(
- '/liveRoom?roomid=${videoItem.history.oid}',
- arguments: {'liveItem': liveItem},
- );
+ } else if (videoItem.history.business == 'live') {
+ if (videoItem.liveStatus == 1) {
+ LiveItemModel liveItem = LiveItemModel.fromJson({
+ 'face': videoItem.authorFace,
+ 'roomid': videoItem.history.oid,
+ 'pic': videoItem.cover,
+ 'title': videoItem.title,
+ 'uname': videoItem.authorName,
+ 'cover': videoItem.cover,
+ });
+ Get.toNamed(
+ '/liveRoom?roomid=${videoItem.history.oid}',
+ arguments: {'liveItem': liveItem},
+ );
+ } else {
+ SmartDialog.showToast('直播未开播');
+ }
} else if (videoItem.badge == '番剧' ||
videoItem.tagName.contains('动画')) {
/// hack
@@ -116,7 +119,7 @@ class HistoryItem extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.fromLTRB(
- StyleString.cardSpace, 5, StyleString.cardSpace, 5),
+ StyleString.safeSpace, 5, StyleString.safeSpace, 5),
child: LayoutBuilder(
builder: (context, boxConstraints) {
double width =
diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart
index 980d7381..727cdce3 100644
--- a/lib/pages/home/controller.dart
+++ b/lib/pages/home/controller.dart
@@ -11,16 +11,17 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
late TabController tabController;
late List tabsCtrList;
late List tabsPageList;
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
RxBool userLogin = false.obs;
RxString userFace = ''.obs;
+ var userInfo;
@override
void onInit() {
super.onInit();
-
- userLogin.value = user.get(UserBoxKey.userLogin) ?? false;
- userFace.value = user.get(UserBoxKey.userFace) ?? '';
+ userInfo = userInfoCache.get('userInfoCache');
+ userLogin.value = userInfo != null;
+ userFace.value = userInfo != null ? userInfo.face : '';
// 进行tabs配置
tabs = tabsConfig;
@@ -48,7 +49,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
// 更新登录状态
void updateLoginStatus(val) {
+ userInfo = userInfoCache.get('userInfoCache');
userLogin.value = val ?? false;
- userFace.value = user.get(UserBoxKey.userFace) ?? '';
+ userFace.value = userInfo != null ? userInfo.face : '';
}
}
diff --git a/lib/pages/home/widgets/app_bar.dart b/lib/pages/home/widgets/app_bar.dart
index 198a9fa3..36920aef 100644
--- a/lib/pages/home/widgets/app_bar.dart
+++ b/lib/pages/home/widgets/app_bar.dart
@@ -6,13 +6,14 @@ import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/pages/mine/view.dart';
import 'package:pilipala/utils/storage.dart';
-Box user = GStrorage.user;
+Box userInfoCache = GStrorage.userInfo;
class HomeAppBar extends StatelessWidget {
const HomeAppBar({super.key});
@override
Widget build(BuildContext context) {
+ var userInfo = userInfoCache.get('userInfoCache');
return SliverAppBar(
// forceElevated: true,
scrolledUnderElevation: 0,
@@ -55,7 +56,7 @@ class HomeAppBar extends StatelessWidget {
const SizedBox(width: 6),
/// TODO
- if (user.get(UserBoxKey.userLogin)) ...[
+ if (userInfo != null) ...[
GestureDetector(
onTap: () => showModalBottomSheet(
context: context,
@@ -70,7 +71,7 @@ class HomeAppBar extends StatelessWidget {
type: 'avatar',
width: 32,
height: 32,
- src: user.get(UserBoxKey.userMid),
+ src: userInfo.face,
),
),
const SizedBox(width: 10),
diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart
index 191ebc0e..16ee4348 100644
--- a/lib/pages/hot/view.dart
+++ b/lib/pages/hot/view.dart
@@ -23,6 +23,7 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin {
final HotController _hotController = Get.put(HotController());
List videoList = [];
Future? _futureBuilderFuture;
+ late ScrollController scrollController;
@override
bool get wantKeepAlive => true;
@@ -31,7 +32,7 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin {
void initState() {
super.initState();
_futureBuilderFuture = _hotController.queryHotFeed('init');
- ScrollController scrollController = _hotController.scrollController;
+ scrollController = _hotController.scrollController;
StreamController mainStream =
Get.find().bottomBarStream;
scrollController.addListener(
@@ -55,6 +56,12 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin {
);
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
super.build(context);
diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart
index fa524157..7c04f8dc 100644
--- a/lib/pages/later/view.dart
+++ b/lib/pages/later/view.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_card_h.dart';
import 'package:pilipala/common/widgets/http_error.dart';
+import 'package:pilipala/common/widgets/no_data.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
import 'package:pilipala/pages/later/index.dart';
@@ -85,13 +86,11 @@ class _LaterPageState extends State {
);
}, childCount: _laterController.laterList.length),
)
- : SliverToBoxAdapter(
- child: Center(
- child: Text(_laterController.isLoading.value
- ? '加载中'
- : '没有数据'),
- ),
- ),
+ : _laterController.isLoading.value
+ ? const SliverToBoxAdapter(
+ child: Center(child: Text('加载中')),
+ )
+ : const NoData(),
);
} else {
return HttpError(
diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart
index e07950ae..385d3272 100644
--- a/lib/pages/live/view.dart
+++ b/lib/pages/live/view.dart
@@ -1,5 +1,6 @@
import 'dart:async';
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@@ -23,22 +24,23 @@ class LivePage extends StatefulWidget {
class _LivePageState extends State {
final LiveController _liveController = Get.put(LiveController());
late Future _futureBuilderFuture;
+ late ScrollController scrollController;
@override
void initState() {
super.initState();
_futureBuilderFuture = _liveController.queryLiveList('init');
- ScrollController scrollController = _liveController.scrollController;
+ scrollController = _liveController.scrollController;
StreamController mainStream =
Get.find().bottomBarStream;
scrollController.addListener(
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
- if (!_liveController.isLoadingMore) {
+ EasyThrottle.throttle('my-throttler', const Duration(seconds: 1), () {
_liveController.isLoadingMore = true;
_liveController.onLoad();
- }
+ });
}
final ScrollDirection direction =
@@ -52,6 +54,12 @@ class _LivePageState extends State {
);
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Container(
diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart
index 1d2385e0..85150349 100644
--- a/lib/pages/main/controller.dart
+++ b/lib/pages/main/controller.dart
@@ -2,9 +2,12 @@ import 'dart:async';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
+import 'package:hive/hive.dart';
import 'package:pilipala/pages/dynamics/index.dart';
import 'package:pilipala/pages/home/view.dart';
import 'package:pilipala/pages/media/index.dart';
+import 'package:pilipala/utils/storage.dart';
+import 'package:pilipala/utils/utils.dart';
class MainController extends GetxController {
List pages = [
@@ -49,4 +52,13 @@ class MainController extends GetxController {
].obs;
final StreamController bottomBarStream =
StreamController.broadcast();
+ Box setting = GStrorage.setting;
+
+ @override
+ void onInit() {
+ super.onInit();
+ if (setting.get(SettingBoxKey.autoUpdate, defaultValue: false)) {
+ Utils.checkUpdata();
+ }
+ }
}
diff --git a/lib/pages/media/controller.dart b/lib/pages/media/controller.dart
index 88fef372..688b555c 100644
--- a/lib/pages/media/controller.dart
+++ b/lib/pages/media/controller.dart
@@ -8,7 +8,7 @@ import 'package:pilipala/utils/storage.dart';
class MediaController extends GetxController {
Rx favFolderData = FavFolderData().obs;
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
RxBool userLogin = false.obs;
List list = [
{
@@ -34,21 +34,23 @@ class MediaController extends GetxController {
'onTap': () => Get.toNamed('/later'),
},
];
+ var userInfo;
@override
void onInit() {
super.onInit();
- userLogin.value = user.get(UserBoxKey.userLogin) ?? false;
+ userInfo = userInfoCache.get('userInfoCache');
+ userLogin.value = userInfo != null;
}
Future queryFavFolder() async {
- if (!userLogin.value || GStrorage.user.get(UserBoxKey.userMid) == null) {
+ if (!userLogin.value || GStrorage.userInfo.get('userInfoCache') == null) {
return {'status': false, 'data': [], 'msg': '未登录'};
}
var res = await await UserHttp.userfavFolder(
pn: 1,
ps: 5,
- mid: GStrorage.user.get(UserBoxKey.userMid),
+ mid: GStrorage.userInfo.get('userInfoCache').mid,
);
favFolderData.value = res['data'];
return res;
diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart
index 13ab30bf..3657dbb5 100644
--- a/lib/pages/media/view.dart
+++ b/lib/pages/media/view.dart
@@ -161,11 +161,25 @@ class _MediaPageState extends State
right: 14, bottom: 35),
child: Center(
child: IconButton(
+ style: ButtonStyle(
+ padding: MaterialStateProperty.all(
+ EdgeInsets.zero),
+ backgroundColor:
+ MaterialStateProperty.resolveWith(
+ (states) {
+ return Theme.of(context)
+ .colorScheme
+ .primaryContainer
+ .withOpacity(0.5);
+ }),
+ ),
onPressed: () => Get.toNamed('/fav'),
icon: Icon(
Icons.arrow_forward_ios,
size: 18,
- color: Theme.of(context).primaryColor,
+ color: Theme.of(context)
+ .colorScheme
+ .primary,
),
),
));
diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart
index eb3e4f33..db4deaae 100644
--- a/lib/pages/member/controller.dart
+++ b/lib/pages/member/controller.dart
@@ -14,16 +14,18 @@ class MemberController extends GetxController {
Map? userStat;
String? face;
String? heroTag;
- Box user = GStrorage.user;
+ Box userInfoCache = GStrorage.userInfo;
late int ownerMid;
// 投稿列表
RxList? archiveList = [VListItemModel()].obs;
+ var userInfo;
@override
void onInit() {
super.onInit();
mid = int.parse(Get.parameters['mid']!);
- ownerMid = user.get(UserBoxKey.userMid) ?? -1;
+ userInfo = userInfoCache.get('userInfoCache');
+ ownerMid = userInfo != null ? userInfo.mid : -1;
face = Get.arguments['face'] ?? '';
heroTag = Get.arguments['heroTag'] ?? '';
}
@@ -57,7 +59,7 @@ class MemberController extends GetxController {
// 关注/取关up
Future actionRelationMod() async {
- if (user.get(UserBoxKey.userMid) == null) {
+ if (userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart
index 9bf4725c..55dad4f0 100644
--- a/lib/pages/member/view.dart
+++ b/lib/pages/member/view.dart
@@ -43,6 +43,12 @@ class _MemberPageState extends State
);
}
+ @override
+ void dispose() {
+ _extendNestCtr.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
return Scaffold(
diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart
index e4118154..66813091 100644
--- a/lib/pages/mine/controller.dart
+++ b/lib/pages/mine/controller.dart
@@ -13,9 +13,8 @@ class MineController extends GetxController {
// 用户状态 动态、关注、粉丝
Rx userStat = UserStat().obs;
RxBool userLogin = false.obs;
- Box user = GStrorage.user;
- Box setting = GStrorage.setting;
Box userInfoCache = GStrorage.userInfo;
+ Box setting = GStrorage.setting;
Rx themeType = ThemeType.system.obs;
@override
@@ -24,6 +23,7 @@ class MineController extends GetxController {
if (userInfoCache.get('userInfoCache') != null) {
userInfo.value = userInfoCache.get('userInfoCache');
+ userLogin.value = true;
}
themeType.value = ThemeType.values[setting.get(SettingBoxKey.themeMode,
@@ -41,8 +41,8 @@ class MineController extends GetxController {
},
);
} else {
- int mid = user.get(UserBoxKey.userMid);
- String face = user.get(UserBoxKey.userFace);
+ int mid = userInfo.value.mid!;
+ String face = userInfo.value.face!;
Get.toNamed(
'/member?mid=$mid',
arguments: {'face': face},
@@ -51,7 +51,7 @@ class MineController extends GetxController {
}
Future queryUserInfo() async {
- if (user.get(UserBoxKey.userLogin) == null) {
+ if (!userLogin.value) {
return {'status': false};
}
var res = await UserHttp.userInfo();
@@ -59,18 +59,12 @@ class MineController extends GetxController {
if (res['data'].isLogin) {
userInfo.value = res['data'];
userInfoCache.put('userInfoCache', res['data']);
- user.put(UserBoxKey.userName, res['data'].uname);
- user.put(UserBoxKey.userFace, res['data'].face);
- user.put(UserBoxKey.userMid, res['data'].mid);
- user.put(UserBoxKey.userLogin, true);
userLogin.value = true;
- // Get.find().readuUserFace();
} else {
resetUserInfo();
}
} else {
resetUserInfo();
- // SmartDialog.showToast(res['msg']);
}
await queryUserStatOwner();
return res;
@@ -87,12 +81,8 @@ class MineController extends GetxController {
Future resetUserInfo() async {
userInfo.value = UserInfoData();
userStat.value = UserStat();
- await user.delete(UserBoxKey.userName);
- await user.delete(UserBoxKey.userFace);
- await user.delete(UserBoxKey.userMid);
- await user.delete(UserBoxKey.userLogin);
+ userInfoCache.delete('userInfoCache');
userLogin.value = false;
- // Get.find().resetLast();
}
onChangeTheme() {
diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart
index e084d96b..4c71869d 100644
--- a/lib/pages/mine/view.dart
+++ b/lib/pages/mine/view.dart
@@ -6,6 +6,7 @@ import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/common/theme_type.dart';
+import 'package:pilipala/models/user/info.dart';
import 'package:pilipala/utils/event_bus.dart';
import 'controller.dart';
@@ -160,10 +161,11 @@ class _MinePageState extends State {
]))
],
),
- const SizedBox(height: 5),
+ const SizedBox(height: 25),
if (_mineController.userInfo.value.levelInfo != null) ...[
LayoutBuilder(
builder: (context, BoxConstraints box) {
+ LevelInfo levelInfo = _mineController.userInfo.value.levelInfo;
return SizedBox(
width: box.maxWidth,
height: 24,
@@ -172,48 +174,27 @@ class _MinePageState extends State {
Positioned(
top: 0,
right: 0,
- child: SizedBox(
- height: 22,
+ bottom: 0,
+ child: Container(
+ color: Theme.of(context).colorScheme.primary,
+ height: 24,
+ constraints:
+ const BoxConstraints(minWidth: 100), // 设置最小宽度为100
width: box.maxWidth *
- (1 -
- (_mineController
- .userInfo.value.levelInfo!.currentExp! /
- _mineController
- .userInfo.value.levelInfo!.nextExp!)),
+ (1 - (levelInfo.currentExp! / levelInfo.nextExp!)),
child: Center(
child: Text(
- (_mineController
- .userInfo.value.levelInfo!.nextExp! -
- _mineController
- .userInfo.value.levelInfo!.currentExp!)
- .toString(),
+ '${levelInfo.currentExp!}/${levelInfo.nextExp!}',
style: TextStyle(
- color: Theme.of(context).colorScheme.primary,
+ color: Theme.of(context).colorScheme.onPrimary,
fontSize: 12,
),
),
),
),
),
- ],
- ),
- );
- },
- ),
- LayoutBuilder(
- builder: (context, BoxConstraints box) {
- return Container(
- width: box.maxWidth,
- height: 1,
- clipBehavior: Clip.hardEdge,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(4),
- color: Theme.of(context).colorScheme.onInverseSurface,
- ),
- child: Stack(
- children: [
Positioned(
- top: 0,
+ top: 23,
left: 0,
bottom: 0,
child: Container(
@@ -224,7 +205,6 @@ class _MinePageState extends State {
.userInfo.value.levelInfo!.nextExp!),
height: 1,
decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(4),
color: Theme.of(context).colorScheme.primary,
),
),
@@ -234,6 +214,36 @@ class _MinePageState extends State {
);
},
),
+ // LayoutBuilder(
+ // builder: (context, BoxConstraints box) {
+ // return Container(
+ // width: box.maxWidth,
+ // height: 1,
+ // color: Theme.of(context).colorScheme.onInverseSurface,
+ // child: Stack(
+ // children: [
+ // Positioned(
+ // top: 0,
+ // left: 0,
+ // bottom: 0,
+ // child: Container(
+ // width: box.maxWidth *
+ // (_mineController
+ // .userInfo.value.levelInfo!.currentExp! /
+ // _mineController
+ // .userInfo.value.levelInfo!.nextExp!),
+ // height: 1,
+ // decoration: BoxDecoration(
+ // borderRadius: BorderRadius.circular(4),
+ // color: Theme.of(context).colorScheme.primary,
+ // ),
+ // ),
+ // ),
+ // ],
+ // ),
+ // );
+ // },
+ // ),
],
const SizedBox(height: 30),
Padding(
diff --git a/lib/pages/preview/controller.dart b/lib/pages/preview/controller.dart
index 8bc938f8..500f0b1d 100644
--- a/lib/pages/preview/controller.dart
+++ b/lib/pages/preview/controller.dart
@@ -2,11 +2,9 @@ import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:get/get.dart';
-import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
-import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:share_plus/share_plus.dart';
class PreviewController extends GetxController {
@@ -17,7 +15,7 @@ class PreviewController extends GetxController {
bool storage = true;
bool videos = true;
bool photos = true;
- bool visiable = true;
+ String currentImgUrl = '';
@override
void onInit() {
@@ -26,6 +24,7 @@ class PreviewController extends GetxController {
initialPage.value = Get.arguments['initialPage']!;
currentPage.value = Get.arguments['initialPage']! + 1;
imgList.value = Get.arguments['imgList'];
+ currentImgUrl = imgList[initialPage.value];
}
}
@@ -39,22 +38,6 @@ class PreviewController extends GetxController {
// final photosInfo = statuses[Permission.photos].toString();
}
- // 图片保存
- void onSaveImg() async {
- var response = await Dio().get(imgList[initialPage.value],
- options: Options(responseType: ResponseType.bytes));
- final result = await ImageGallerySaver.saveImage(
- Uint8List.fromList(response.data),
- quality: 100,
- name: "pic_vvex${DateTime.now().toString().split('-').join()}");
- if (result != null) {
- if (result['isSuccess']) {
- // ignore: avoid_print
- print('已保存到相册');
- }
- }
- }
-
// 图片分享
void onShareImg() async {
requestPermission();
@@ -62,9 +45,15 @@ class PreviewController extends GetxController {
options: Options(responseType: ResponseType.bytes));
final temp = await getTemporaryDirectory();
String imgName =
- "pic_plpl${DateTime.now().toString().split('-').join()}.jpg";
+ "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg";
var path = '${temp.path}/$imgName';
File(path).writeAsBytesSync(response.data);
Share.shareXFiles([XFile(path)], subject: imgList[initialPage.value]);
}
+
+ void onChange(int index) {
+ initialPage.value = index;
+ currentPage.value = index + 1;
+ currentImgUrl = imgList[index];
+ }
}
diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart
index 610a3ae2..42eb0b69 100644
--- a/lib/pages/preview/view.dart
+++ b/lib/pages/preview/view.dart
@@ -2,9 +2,12 @@
import 'package:dismissible_page/dismissible_page.dart';
import 'package:flutter/services.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:extended_image/extended_image.dart';
+import 'package:pilipala/plugin/pl_player/index.dart';
+import 'package:pilipala/utils/download.dart';
import 'controller.dart';
typedef DoubleClickAnimationListener = void Function();
@@ -35,6 +38,56 @@ class _ImagePreviewState extends State
duration: const Duration(milliseconds: 250), vsync: this);
}
+ onOpenMenu() {
+ SmartDialog.show(
+ useSystem: true,
+ animationType: SmartAnimationType.centerFade_otherSlide,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ clipBehavior: Clip.hardEdge,
+ contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ListTile(
+ onTap: () {
+ _previewController.onShareImg();
+ SmartDialog.dismiss();
+ },
+ dense: true,
+ title: const Text('分享', style: TextStyle(fontSize: 14)),
+ ),
+ ListTile(
+ onTap: () {
+ Clipboard.setData(
+ ClipboardData(text: _previewController.currentImgUrl))
+ .then((value) {
+ SmartDialog.showToast('已复制到粘贴板');
+ SmartDialog.dismiss();
+ }).catchError((err) {
+ SmartDialog.showNotify(
+ msg: err.toString(),
+ notifyType: NotifyType.error,
+ );
+ });
+ },
+ dense: true,
+ title: const Text('复制链接', style: TextStyle(fontSize: 14)),
+ ),
+ ListTile(
+ onTap: () {
+ DownloadUtils.downloadImg(_previewController.currentImgUrl);
+ },
+ dense: true,
+ title: const Text('保存到手机', style: TextStyle(fontSize: 14)),
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ }
+
@override
void dispose() {
// animationController.dispose();
@@ -51,7 +104,7 @@ class _ImagePreviewState extends State
primary: false,
toolbarHeight: 0,
backgroundColor: Colors.black,
- systemOverlayStyle: SystemUiOverlayStyle.light,
+ systemOverlayStyle: SystemUiOverlayStyle.dark,
),
body: Stack(
children: [
@@ -69,19 +122,14 @@ class _ImagePreviewState extends State
tag: _previewController
.imgList[_previewController.initialPage.value],
child: GestureDetector(
- onTap: () {
- _previewController.visiable = !_previewController.visiable;
- setState(() {});
- },
+ onLongPress: () => onOpenMenu(),
child: ExtendedImageGesturePageView.builder(
controller: ExtendedPageController(
initialPage: _previewController.initialPage.value,
pageSpacing: 0,
),
- onPageChanged: (int index) {
- _previewController.initialPage.value = index;
- _previewController.currentPage.value = index + 1;
- },
+ onPageChanged: (int index) =>
+ _previewController.onChange(index),
canScrollPage: (GestureDetails? gestureDetails) =>
gestureDetails!.totalScale! <= 1.0,
preloadPagesCount: 2,
@@ -149,8 +197,10 @@ class _ImagePreviewState extends State
children: [
SizedBox(
width: 150.0,
- child:
- LinearProgressIndicator(value: progress),
+ child: LinearProgressIndicator(
+ value: progress,
+ color: Colors.white,
+ ),
),
const SizedBox(height: 10.0),
Text('${((progress ?? 0.0) * 100).toInt()}%'),
@@ -179,7 +229,6 @@ class _ImagePreviewState extends State
right: 0,
bottom: 0,
child: Container(
- // height: 45,
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom, top: 20),
decoration: const BoxDecoration(
@@ -193,36 +242,18 @@ class _ImagePreviewState extends State
tileMode: TileMode.mirror,
),
),
- child: Padding(
- padding: const EdgeInsets.only(left: 20, right: 12),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Obx(
- () => Text.rich(
+ child: Obx(
+ () => Text.rich(
+ textAlign: TextAlign.center,
+ TextSpan(
+ style: const TextStyle(color: Colors.white, fontSize: 15),
+ children: [
TextSpan(
- style: const TextStyle(
- color: Colors.white, fontSize: 18),
- children: [
- TextSpan(
- text: _previewController.currentPage
- .toString()),
- const TextSpan(text: ' / '),
- TextSpan(
- text: _previewController.imgList.length
- .toString()),
- ]),
- ),
- ),
- const Spacer(),
- ElevatedButton(
- onPressed: () => _previewController.onShareImg(),
- child: const Text('分享')),
- const SizedBox(width: 10),
- ElevatedButton(
- onPressed: () => _previewController.onSaveImg(),
- child: const Text('保存'))
- ],
+ text: _previewController.currentPage.toString()),
+ const TextSpan(text: ' / '),
+ TextSpan(
+ text: _previewController.imgList.length.toString()),
+ ]),
),
),
),
diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart
index 6cead2df..d8d7c57a 100644
--- a/lib/pages/rcmd/view.dart
+++ b/lib/pages/rcmd/view.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
@@ -40,12 +41,11 @@ class _RcmdPageState extends State
() {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 200) {
- if (!_rcmdController.isLoadingMore) {
+ EasyThrottle.throttle(
+ 'my-throttler', const Duration(milliseconds: 500), () {
_rcmdController.isLoadingMore = true;
- WidgetsBinding.instance.addPostFrameCallback((_) async {
- _rcmdController.onLoad();
- });
- }
+ _rcmdController.onLoad();
+ });
}
final ScrollDirection direction =
@@ -59,6 +59,12 @@ class _RcmdPageState extends State
);
}
+ @override
+ void dispose() {
+ _rcmdController.scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
super.build(context);
diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart
index 9b3f7dad..20277b89 100644
--- a/lib/pages/search/controller.dart
+++ b/lib/pages/search/controller.dart
@@ -12,8 +12,7 @@ class SSearchController extends GetxController {
final FocusNode searchFocusNode = FocusNode();
RxString searchKeyWord = ''.obs;
Rx controller = TextEditingController().obs;
- List hotSearchList = [];
- Box hotKeyword = GStrorage.hotKeyword;
+ RxList hotSearchList = [HotSearchItem()].obs;
Box histiryWord = GStrorage.historyword;
List historyCacheList = [];
RxList historyList = [].obs;
@@ -27,14 +26,6 @@ class SSearchController extends GetxController {
void onInit() {
super.onInit();
searchDefault();
- if (hotKeyword.get('cacheList') != null &&
- hotKeyword.get('cacheList').isNotEmpty) {
- List list = [];
- for (var i in hotKeyword.get('cacheList')) {
- list.add(i);
- }
- hotSearchList = list;
- }
// 其他页面跳转过来
if (Get.parameters.keys.isNotEmpty) {
if (Get.parameters['keyword'] != null) {
@@ -89,8 +80,7 @@ class SSearchController extends GetxController {
// 获取热搜关键词
Future queryHotSearchList() async {
var result = await SearchHttp.hotSearchList();
- hotSearchList = result['data'].list;
- hotKeyword.put('cacheList', result['data'].list);
+ hotSearchList.value = result['data'].list;
return result;
}
diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart
index d4190e47..fdd18352 100644
--- a/lib/pages/search/view.dart
+++ b/lib/pages/search/view.dart
@@ -45,6 +45,11 @@ class _SearchPageState extends State with RouteAware {
return OpenContainer(
closedElevation: 0,
openElevation: 0,
+ onClosed: (_) async {
+ // 在 openBuilder 关闭时触发的回调函数
+ await Future.delayed(const Duration(milliseconds: 500));
+ _searchController.onClear();
+ },
openColor: Theme.of(context).colorScheme.background,
middleColor: Theme.of(context).colorScheme.background,
closedColor: Theme.of(context).colorScheme.background,
@@ -145,7 +150,7 @@ class _SearchPageState extends State with RouteAware {
// 搜索建议
_searchSuggest(),
// 热搜
- hotSearch(),
+ hotSearch(_searchController),
// 搜索历史
_history()
],
@@ -176,25 +181,7 @@ class _SearchPageState extends State with RouteAware {
// child: Text(
// _searchController.searchSuggestList[index].term!,
// ),
- child: Text.rich(
- TextSpan(
- children: [
- TextSpan(
- text: _searchController
- .searchSuggestList[index].name![0]),
- TextSpan(
- text: _searchController
- .searchSuggestList[index].name![1],
- style: TextStyle(
- color: Theme.of(context).colorScheme.primary,
- fontWeight: FontWeight.bold),
- ),
- TextSpan(
- text: _searchController
- .searchSuggestList[index].name![2]),
- ],
- ),
- ),
+ child: _searchController.searchSuggestList[index].textRich,
),
);
},
@@ -203,20 +190,37 @@ class _SearchPageState extends State with RouteAware {
);
}
- Widget hotSearch() {
+ Widget hotSearch(ctr) {
return Padding(
padding: const EdgeInsets.fromLTRB(10, 14, 4, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
- padding: const EdgeInsets.fromLTRB(6, 0, 0, 6),
- child: Text(
- '大家都在搜',
- style: Theme.of(context)
- .textTheme
- .titleMedium!
- .copyWith(fontWeight: FontWeight.bold),
+ padding: const EdgeInsets.fromLTRB(6, 0, 6, 6),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ '大家都在搜',
+ style: Theme.of(context)
+ .textTheme
+ .titleMedium!
+ .copyWith(fontWeight: FontWeight.bold),
+ ),
+ SizedBox(
+ height: 34,
+ child: TextButton.icon(
+ style: ButtonStyle(
+ padding: MaterialStateProperty.all(const EdgeInsets.only(
+ left: 10, top: 6, bottom: 6, right: 10)),
+ ),
+ onPressed: () => ctr.queryHotSearchList(),
+ icon: const Icon(Icons.refresh_outlined, size: 18),
+ label: const Text('刷新'),
+ ),
+ ),
+ ],
),
),
LayoutBuilder(
@@ -228,15 +232,17 @@ class _SearchPageState extends State with RouteAware {
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data as Map;
if (data['status']) {
- return HotKeyword(
- width: width,
- hotSearchList: _searchController.hotSearchList,
- onClick: (keyword) async {
- _searchController.searchFocusNode.unfocus();
- await Future.delayed(
- const Duration(milliseconds: 150));
- _searchController.onClickKeyword(keyword);
- },
+ return Obx(
+ () => HotKeyword(
+ width: width,
+ hotSearchList: _searchController.hotSearchList.value,
+ onClick: (keyword) async {
+ _searchController.searchFocusNode.unfocus();
+ await Future.delayed(
+ const Duration(milliseconds: 150));
+ _searchController.onClickKeyword(keyword);
+ },
+ ),
);
} else {
return HttpError(
diff --git a/lib/pages/searchPanel/controller.dart b/lib/pages/searchPanel/controller.dart
index b8e4a166..826fdacc 100644
--- a/lib/pages/searchPanel/controller.dart
+++ b/lib/pages/searchPanel/controller.dart
@@ -12,17 +12,25 @@ class SearchPanelController extends GetxController {
SearchType? searchType;
RxInt page = 1.obs;
RxList resultList = [].obs;
+ // 结果排序方式 搜索类型为视频、专栏及相簿时
+ RxString order = ''.obs;
+ // 视频时长筛选 仅用于搜索视频
+ RxInt duration = 0.obs;
Future onSearch({type = 'init'}) async {
var result = await SearchHttp.searchByType(
- searchType: searchType!, keyword: keyword!, page: page.value);
+ searchType: searchType!,
+ keyword: keyword!,
+ page: page.value,
+ order: searchType!.type != 'video' ? null : order.value,
+ duration: searchType!.type != 'video' ? null : duration.value);
if (result['status']) {
- if (type == 'init' || type == 'onLoad') {
- page.value++;
- resultList.addAll(result['data'].list);
- } else if (type == 'onRefresh') {
+ if (type == 'onRefresh') {
resultList.value = result['data'].list;
+ } else {
+ resultList.addAll(result['data'].list);
}
+ page.value++;
onPushDetail(keyword, resultList);
}
return result;
@@ -30,7 +38,7 @@ class SearchPanelController extends GetxController {
Future onRefresh() async {
page.value = 1;
- onSearch(type: 'onRefresh');
+ await onSearch(type: 'onRefresh');
}
// 返回顶部并刷新
diff --git a/lib/pages/searchPanel/view.dart b/lib/pages/searchPanel/view.dart
index 8e1cf3c4..346e5048 100644
--- a/lib/pages/searchPanel/view.dart
+++ b/lib/pages/searchPanel/view.dart
@@ -1,3 +1,4 @@
+import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/media_bangumi.dart';
@@ -27,8 +28,8 @@ class _SearchPanelState extends State
with AutomaticKeepAliveClientMixin {
late SearchPanelController _searchPanelController;
- bool _isLoadingMore = false;
late Future _futureBuilderFuture;
+ late ScrollController scrollController;
@override
bool get wantKeepAlive => true;
@@ -43,20 +44,24 @@ class _SearchPanelState extends State
),
tag: widget.searchType!.type,
);
- ScrollController scrollController = _searchPanelController.scrollController;
+ scrollController = _searchPanelController.scrollController;
scrollController.addListener(() async {
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 100) {
- if (!_isLoadingMore) {
- _isLoadingMore = true;
- await _searchPanelController.onSearch(type: 'onLoad');
- _isLoadingMore = false;
- }
+ EasyThrottle.throttle('history', const Duration(seconds: 1), () {
+ _searchPanelController.onSearch(type: 'onLoad');
+ });
}
});
_futureBuilderFuture = _searchPanelController.onSearch();
}
+ @override
+ void dispose() {
+ scrollController.removeListener(() {});
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
super.build(context);
@@ -70,12 +75,15 @@ class _SearchPanelState extends State
if (snapshot.connectionState == ConnectionState.done) {
Map data = snapshot.data;
var ctr = _searchPanelController;
- List list = ctr.resultList;
+ RxList list = ctr.resultList;
if (data['status']) {
return Obx(() {
switch (widget.searchType) {
case SearchType.video:
- return searchVideoPanel(context, ctr, list);
+ return SearchVideoPanel(
+ ctr: _searchPanelController,
+ list: list.value,
+ );
case SearchType.media_bangumi:
return searchMbangumiPanel(context, ctr, list);
case SearchType.bili_user:
diff --git a/lib/pages/searchPanel/widgets/video_panel.dart b/lib/pages/searchPanel/widgets/video_panel.dart
index ca6b09fb..6cdc7868 100644
--- a/lib/pages/searchPanel/widgets/video_panel.dart
+++ b/lib/pages/searchPanel/widgets/video_panel.dart
@@ -1,15 +1,217 @@
import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+import 'package:get/get.dart';
import 'package:pilipala/common/widgets/video_card_h.dart';
+import 'package:pilipala/models/common/search_type.dart';
+import 'package:pilipala/pages/searchPanel/index.dart';
-Widget searchVideoPanel(BuildContext context, ctr, list) {
- return ListView.builder(
- controller: ctr!.scrollController,
- addAutomaticKeepAlives: false,
- addRepaintBoundaries: false,
- itemCount: list!.length,
- itemBuilder: (context, index) {
- var i = list![index];
- return VideoCardH(videoItem: i);
- },
- );
+class SearchVideoPanel extends StatelessWidget {
+ SearchVideoPanel({
+ this.ctr,
+ this.list,
+ Key? key,
+ }) : super(key: key);
+
+ final SearchPanelController? ctr;
+ final List? list;
+
+ final VideoPanelController controller = Get.put(VideoPanelController());
+
+ @override
+ Widget build(BuildContext context) {
+ return Stack(
+ alignment: Alignment.topCenter,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 36),
+ child: ListView.builder(
+ controller: ctr!.scrollController,
+ addAutomaticKeepAlives: false,
+ addRepaintBoundaries: false,
+ itemCount: list!.length,
+ itemBuilder: (context, index) {
+ var i = list![index];
+ return Padding(
+ padding: index == 0
+ ? const EdgeInsets.only(top: 2)
+ : EdgeInsets.zero,
+ child: VideoCardH(videoItem: i),
+ );
+ },
+ ),
+ ),
+ // 分类筛选
+ Container(
+ width: double.infinity,
+ height: 36,
+ padding: const EdgeInsets.only(left: 8, top: 0, right: 12),
+ // decoration: BoxDecoration(
+ // border: Border(
+ // bottom: BorderSide(
+ // color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
+ // ),
+ // ),
+ // ),
+ child: Row(
+ children: [
+ Expanded(
+ child: SingleChildScrollView(
+ scrollDirection: Axis.horizontal,
+ child: Obx(
+ () => Wrap(
+ // spacing: ,
+ children: [
+ for (var i in controller.filterList) ...[
+ CustomFilterChip(
+ label: i['label'],
+ type: i['type'],
+ selectedType: controller.selectedType.value,
+ callFn: (bool selected) async {
+ controller.selectedType.value = i['type'];
+ ctr!.order.value =
+ i['type'].toString().split('.').last;
+ SmartDialog.showLoading(msg: 'loooad');
+ await ctr!.onRefresh();
+ SmartDialog.dismiss();
+ },
+ ),
+ ]
+ ],
+ ),
+ ),
+ ),
+ ),
+ const VerticalDivider(indent: 7, endIndent: 8),
+ const SizedBox(width: 3),
+ SizedBox(
+ width: 32,
+ height: 32,
+ child: IconButton(
+ style: ButtonStyle(
+ padding: MaterialStateProperty.all(EdgeInsets.zero),
+ ),
+ onPressed: () => controller.onShowFilterDialog(),
+ icon: Icon(
+ Icons.filter_list_outlined,
+ size: 18,
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ), // 放置在ListView.builder()上方的组件
+ ],
+ );
+ }
+}
+
+class CustomFilterChip extends StatelessWidget {
+ const CustomFilterChip({
+ this.label,
+ this.type,
+ this.selectedType,
+ this.callFn,
+ Key? key,
+ }) : super(key: key);
+
+ final String? label;
+ final ArchiveFilterType? type;
+ final ArchiveFilterType? selectedType;
+ final Function? callFn;
+
+ @override
+ Widget build(BuildContext context) {
+ return SizedBox(
+ height: 34,
+ child: FilterChip(
+ padding: const EdgeInsets.only(left: 11, right: 11),
+ labelPadding: EdgeInsets.zero,
+ label: Text(
+ label!,
+ style: const TextStyle(fontSize: 13),
+ ),
+ labelStyle: TextStyle(
+ color: type == selectedType
+ ? Theme.of(context).colorScheme.primary
+ : Theme.of(context).colorScheme.outline),
+ selected: type == selectedType,
+ showCheckmark: false,
+ shape: ContinuousRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ selectedColor: Colors.transparent,
+ // backgroundColor:
+ // Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
+ backgroundColor: Colors.transparent,
+ side: BorderSide.none,
+ onSelected: (bool selected) => callFn!(selected),
+ ),
+ );
+ }
+}
+
+class VideoPanelController extends GetxController {
+ RxList