merge mine & media

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-07-28 14:27:18 +08:00
parent 6ca7efe8d1
commit 65b432ed2c
40 changed files with 903 additions and 1038 deletions

View File

@@ -56,7 +56,7 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
initPubState();
if (widget.autofocus) {
Future.delayed(const Duration(milliseconds: 300)).whenComplete(() {
Future.delayed(const Duration(milliseconds: 300), () {
if (mounted) {
focusNode.requestFocus();
}
@@ -77,9 +77,8 @@ abstract class CommonPublishPageState<T extends CommonPublishPage>
super.dispose();
}
Future<void> _requestFocus() async {
await Future.delayed(const Duration(microseconds: 200));
focusNode.requestFocus();
void _requestFocus() {
Future.delayed(const Duration(microseconds: 200), focusNode.requestFocus);
}
@override

View File

@@ -68,6 +68,7 @@ class DynamicsController extends GetxController
vmid: accountService.mid,
pn: allFollowedUpsPage,
orderType: 'attention',
ps: 50,
);
if (res.isSuccess) {
upData.value.upList ??= <UpItem>[];
@@ -108,6 +109,7 @@ class DynamicsController extends GetxController
vmid: accountService.mid,
pn: allFollowedUpsPage,
orderType: 'attention',
ps: 50,
);
final res0 = await f1;
if (!res0.isSuccess) {

View File

@@ -34,9 +34,9 @@ class _DynamicsPageState extends State<DynamicsPage>
tooltip: '发布动态',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.resolveWith((states) {
return theme.colorScheme.secondaryContainer;
}),
backgroundColor: WidgetStatePropertyAll(
theme.colorScheme.secondaryContainer,
),
),
onPressed: () {
if (_dynamicsController.accountService.isLogin.value) {

View File

@@ -301,10 +301,8 @@ class _CreateDynPanelState extends CommonRichTextPubPageState<CreateDynPanel> {
tooltip: '返回',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.resolveWith(
(states) {
return theme.colorScheme.secondaryContainer;
},
backgroundColor: WidgetStatePropertyAll(
theme.colorScheme.secondaryContainer,
),
),
onPressed: Get.back,

View File

@@ -285,11 +285,9 @@ class _RepostPanelState extends CommonRichTextPubPageState<RepostPanel> {
tooltip: '返回',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.resolveWith((
states,
) {
return theme.colorScheme.secondaryContainer;
}),
backgroundColor: WidgetStatePropertyAll(
theme.colorScheme.secondaryContainer,
),
),
onPressed: Get.back,
icon: Icon(

View File

@@ -28,7 +28,7 @@ class DynamicsTabController
@override
Future<void> onRefresh() {
if (dynamicsType == DynamicsTabType.all) {
mainController.setCount();
mainController.setDynCount();
}
offset = '';
return super.onRefresh();

View File

@@ -141,9 +141,7 @@ class _EpisodePanelState extends CommonSlidePageState<EpisodePanel> {
widget.initialTabIndex,
duration: const Duration(milliseconds: 200),
);
Future.delayed(const Duration(milliseconds: 300)).whenComplete(() {
jumpToCurrent();
});
Future.delayed(const Duration(milliseconds: 300), jumpToCurrent);
} else {
jumpToCurrent();
}

View File

@@ -66,56 +66,59 @@ class FavNoteItem extends StatelessWidget {
Positioned.fill(
child: IgnorePointer(
child: LayoutBuilder(
builder: (context, constraints) => AnimatedOpacity(
opacity: item.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
color: Colors.black.withValues(
alpha: 0.6,
builder: (context, constraints) =>
AnimatedOpacity(
opacity: item.checked == true ? 1 : 0,
duration: const Duration(
milliseconds: 200,
),
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true ? 1 : 0,
duration: const Duration(
milliseconds: 250,
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
color: Colors.black.withValues(
alpha: 0.6,
),
),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding: WidgetStateProperty.all(
EdgeInsets.zero,
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true ? 1 : 0,
duration: const Duration(
milliseconds: 250,
),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding:
WidgetStateProperty.all(
EdgeInsets.zero,
),
backgroundColor:
WidgetStatePropertyAll(
theme.colorScheme.surface
.withValues(
alpha: 0.8,
),
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color:
theme.colorScheme.primary,
),
),
backgroundColor:
WidgetStateProperty.resolveWith(
(states) {
return theme
.colorScheme
.surface
.withValues(alpha: 0.8);
},
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color: theme.colorScheme.primary,
),
),
),
),
),
),
),
),
),

View File

@@ -55,98 +55,103 @@ class FavPgcItem extends StatelessWidget {
AspectRatio(
aspectRatio: 3 / 4,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints boxConstraints) {
return Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
radius: 4,
src: item.cover,
width: boxConstraints.maxWidth,
height: boxConstraints.maxHeight,
),
PBadge(
right: 4,
top: 4,
text: item.badge,
size: PBadgeSize.small,
fontSize: 10,
padding: const EdgeInsets.symmetric(
horizontal: 2,
vertical: 1,
),
),
Positioned.fill(
child: IgnorePointer(
child: LayoutBuilder(
builder: (context, constraints) =>
AnimatedOpacity(
opacity: item.checked == true ? 1 : 0,
duration: const Duration(
milliseconds: 200,
),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(
Radius.circular(4),
),
color: Colors.black.withValues(
alpha: 0.6,
),
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true
? 1
: 0,
duration: const Duration(
milliseconds: 250,
),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding:
WidgetStateProperty.all(
EdgeInsets.zero,
),
backgroundColor:
WidgetStateProperty.resolveWith(
(states) {
return theme
.colorScheme
.surface
.withValues(
alpha: 0.8,
);
},
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color:
theme.colorScheme.primary,
),
),
),
),
),
),
builder:
(
BuildContext context,
BoxConstraints boxConstraints,
) {
return Stack(
clipBehavior: Clip.none,
children: [
NetworkImgLayer(
radius: 4,
src: item.cover,
width: boxConstraints.maxWidth,
height: boxConstraints.maxHeight,
),
),
),
],
);
},
PBadge(
right: 4,
top: 4,
text: item.badge,
size: PBadgeSize.small,
fontSize: 10,
padding: const EdgeInsets.symmetric(
horizontal: 2,
vertical: 1,
),
),
Positioned.fill(
child: IgnorePointer(
child: LayoutBuilder(
builder: (context, constraints) =>
AnimatedOpacity(
opacity: item.checked == true
? 1
: 0,
duration: const Duration(
milliseconds: 200,
),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(
Radius.circular(4),
),
color: Colors.black.withValues(
alpha: 0.6,
),
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true
? 1
: 0,
duration: const Duration(
milliseconds: 250,
),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding:
WidgetStateProperty.all(
EdgeInsets.zero,
),
backgroundColor:
WidgetStatePropertyAll(
theme
.colorScheme
.surface
.withValues(
alpha: 0.8,
),
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color: theme
.colorScheme
.primary,
),
),
),
),
),
),
),
),
),
],
);
},
),
),
const SizedBox(width: 10),

View File

@@ -208,9 +208,7 @@ class FavDetailController
var res = await FavHttp.cleanFav(mediaId: mediaId);
if (res['status']) {
SmartDialog.showToast('清除成功');
Future.delayed(const Duration(milliseconds: 200), () {
onReload();
});
Future.delayed(const Duration(milliseconds: 200), onReload);
} else {
SmartDialog.showToast(res['msg']);
}

View File

@@ -541,53 +541,52 @@ class _FavDetailPageState extends State<FavDetailPage> {
bottom: 5,
child: IgnorePointer(
child: LayoutBuilder(
builder: (context, constraints) => AnimatedOpacity(
opacity: item.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
color: Colors.black.withValues(alpha: 0.6),
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true ? 1 : 0,
duration: const Duration(
milliseconds: 250,
),
curve: Curves.easeInOut,
child: IconButton(
style: ButtonStyle(
padding: WidgetStateProperty.all(
EdgeInsets.zero,
),
backgroundColor:
WidgetStateProperty.resolveWith(
(states) {
return theme
.colorScheme
.surface
.withValues(alpha: 0.8);
},
),
builder: (context, constraints) =>
AnimatedOpacity(
opacity: item.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
color: Colors.black.withValues(
alpha: 0.6,
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color: theme.colorScheme.primary,
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: item.checked == true ? 1 : 0,
duration: const Duration(
milliseconds: 250,
),
curve: Curves.easeInOut,
child: IconButton(
style: ButtonStyle(
padding: WidgetStateProperty.all(
EdgeInsets.zero,
),
backgroundColor:
WidgetStatePropertyAll(
theme.colorScheme.surface
.withValues(alpha: 0.8),
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color: theme.colorScheme.primary,
),
),
),
),
),
),
),
),
),
),
),

View File

@@ -51,7 +51,7 @@ class FollowChildController
return MemberHttp.followUpGroup(mid: mid, tagid: tagid, pn: page);
}
return FollowHttp.followingsNew(
return FollowHttp.followings(
vmid: mid,
pn: page,
orderType: orderType.value.type,

View File

@@ -195,13 +195,9 @@ class HistoryItem extends StatelessWidget {
EdgeInsets.zero,
),
backgroundColor:
WidgetStateProperty.resolveWith(
(states) {
return theme
.colorScheme
.surface
.withValues(alpha: 0.8);
},
WidgetStatePropertyAll(
theme.colorScheme.surface
.withValues(alpha: 0.8),
),
),
onPressed: () {

View File

@@ -5,9 +5,7 @@ import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/models/common/home_tab_type.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
import 'package:PiliPlus/pages/mine/view.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart';
@@ -83,17 +81,6 @@ class HomeController extends GetxController
} catch (_) {}
}
void showUserInfoDialog(BuildContext context) {
feedBack();
showDialog(
context: context,
useSafeArea: true,
builder: (context) => const Dialog(
child: MinePage(),
),
);
}
@override
void onClose() {
searchBarStream?.close();

View File

@@ -104,8 +104,7 @@ class _HomePageState extends State<HomePage>
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () =>
_homeController.showUserInfoDialog(context),
onTap: _mainController.toMinePage,
splashColor: theme.colorScheme.primaryContainer
.withValues(alpha: 0.3),
customBorder: const CircleBorder(),
@@ -141,8 +140,7 @@ class _HomePageState extends State<HomePage>
)
: defaultUser(
theme: theme,
onPressed: () =>
_homeController.showUserInfoDialog(context),
onPressed: _mainController.toMinePage,
),
),
),
@@ -235,9 +233,9 @@ Widget defaultUser({
tooltip: '默认用户头像',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.resolveWith((states) {
return theme.colorScheme.onInverseSurface;
}),
backgroundColor: WidgetStatePropertyAll(
theme.colorScheme.onInverseSurface,
),
),
onPressed: onPressed,
icon: Icon(

View File

@@ -121,54 +121,55 @@ class _LaterViewChildPageState extends State<LaterViewChildPage>
bottom: 5,
child: IgnorePointer(
child: LayoutBuilder(
builder: (context, constraints) => AnimatedOpacity(
opacity: videoItem.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
color: Colors.black.withValues(alpha: 0.6),
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: videoItem.checked == true ? 1 : 0,
duration: const Duration(
milliseconds: 250,
),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding: WidgetStateProperty.all(
EdgeInsets.zero,
),
backgroundColor:
WidgetStateProperty.resolveWith(
(states) {
return theme
.colorScheme
.surface
.withValues(alpha: 0.8);
},
),
builder: (context, constraints) =>
AnimatedOpacity(
opacity: videoItem.checked == true ? 1 : 0,
duration: const Duration(milliseconds: 200),
child: Container(
alignment: Alignment.center,
height: constraints.maxHeight,
width:
constraints.maxHeight *
StyleString.aspectRatio,
decoration: BoxDecoration(
borderRadius: StyleString.mdRadius,
color: Colors.black.withValues(
alpha: 0.6,
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color: theme.colorScheme.primary,
),
child: SizedBox(
width: 34,
height: 34,
child: AnimatedScale(
scale: videoItem.checked == true
? 1
: 0,
duration: const Duration(
milliseconds: 250,
),
curve: Curves.easeInOut,
child: IconButton(
tooltip: '取消选择',
style: ButtonStyle(
padding: WidgetStateProperty.all(
EdgeInsets.zero,
),
backgroundColor:
WidgetStatePropertyAll(
theme.colorScheme.surface
.withValues(alpha: 0.8),
),
),
onPressed: null,
icon: Icon(
Icons.done_all_outlined,
color: theme.colorScheme.primary,
),
),
),
),
),
),
),
),
),
),
),

View File

@@ -8,11 +8,15 @@ import 'package:PiliPlus/models/common/msg/msg_unread_type.dart';
import 'package:PiliPlus/models/common/nav_bar_config.dart';
import 'package:PiliPlus/models_new/msgfeed_unread/data.dart';
import 'package:PiliPlus/models_new/single_unread/data.dart';
import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/home/controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/storage_pref.dart';
import 'package:PiliPlus/utils/update.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -21,20 +25,27 @@ class MainController extends GetxController
AccountService accountService = Get.find<AccountService>();
List<NavigationBarType> navigationBars = <NavigationBarType>[];
RxInt dynCount = 0.obs;
StreamController<bool>? bottomBarStream;
late bool hideTabBar = Pref.hideTabBar;
late dynamic controller;
RxInt selectedIndex = 0.obs;
RxInt dynCount = 0.obs;
late DynamicBadgeMode dynamicBadgeMode;
late bool checkDynamic = Pref.checkDynamic;
late int dynamicPeriod = Pref.dynamicPeriod;
late int dynamicPeriod = Pref.dynamicPeriod * 60 * 1000;
late int _lastCheckDynamicAt = 0;
late int dynIndex = -1;
late final DynamicsController dynamicController = Get.put(
DynamicsController(),
);
late int homeIndex = -1;
late final HomeController? homeController = homeIndex == -1
? null
: Get.put(HomeController());
late DynamicBadgeMode msgBadgeMode = Pref.msgBadgeMode;
late Set<MsgUnReadType> msgUnReadTypes = Pref.msgUnReadTypeV2;
late final RxString msgUnReadCount = ''.obs;
@@ -48,6 +59,9 @@ class MainController extends GetxController
late bool directExitOnBack = Pref.directExitOnBack;
static const _period = 5 * 60 * 1000;
late int _lastSelectTime = 0;
@override
void onInit() {
super.onInit();
@@ -163,19 +177,19 @@ class MainController extends GetxController
}
}
Future<void> getUnreadDynamic() async {
void getUnreadDynamic() {
if (!accountService.isLogin.value || dynIndex == -1) {
return;
}
DynGrpc.dynRed().then((res) {
if (res != null) {
setCount(res);
setDynCount(res);
}
});
}
Future<void> setCount([int count = 0]) async {
if (dynIndex == -1 || dynCount.value == count) return;
void setDynCount([int count = 0]) {
if (dynIndex == -1) return;
dynCount.value = count;
}
@@ -187,7 +201,7 @@ class MainController extends GetxController
return;
}
int now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastCheckDynamicAt >= dynamicPeriod * 60 * 1000) {
if (now - _lastCheckDynamicAt >= dynamicPeriod) {
_lastCheckDynamicAt = now;
getUnreadDynamic();
}
@@ -198,7 +212,7 @@ class MainController extends GetxController
(GStorage.setting.get(SettingBoxKey.navBarSort) as List?)?.cast();
int defaultHomePage = Pref.defaultHomePage;
late final List<NavigationBarType> navigationBars;
if (navBarSort == null) {
if (navBarSort == null || navBarSort.isEmpty) {
navigationBars = NavigationBarType.values;
} else {
navigationBars = navBarSort
@@ -212,6 +226,86 @@ class MainController extends GetxController
);
}
void checkDefaultSearch([bool shouldCheck = false]) {
if (homeController?.enableSearchWord == true) {
if (shouldCheck &&
navigationBars[selectedIndex.value] != NavigationBarType.home) {
return;
}
int now = DateTime.now().millisecondsSinceEpoch;
if (now - homeController!.lateCheckSearchAt >= _period) {
homeController!
..lateCheckSearchAt = now
..querySearchDefault();
}
}
}
void checkUnread([bool shouldCheck = false]) {
if (accountService.isLogin.value &&
homeIndex != -1 &&
msgBadgeMode != DynamicBadgeMode.hidden) {
if (shouldCheck &&
navigationBars[selectedIndex.value] != NavigationBarType.home) {
return;
}
int now = DateTime.now().millisecondsSinceEpoch;
if (now - lastCheckUnreadAt >= _period) {
lastCheckUnreadAt = now;
queryUnreadMsg();
}
}
}
void toMinePage() {
final index = navigationBars.indexOf(NavigationBarType.mine);
if (index != -1) {
setIndex(index);
}
}
void setIndex(int value) {
feedBack();
final currentNav = navigationBars[value];
if (value != selectedIndex.value) {
selectedIndex.value = value;
if (mainTabBarView) {
controller.animateTo(value);
} else {
controller.jumpToPage(value);
}
if (currentNav == NavigationBarType.home) {
checkDefaultSearch();
checkUnread();
} else if (currentNav == NavigationBarType.dynamics) {
setDynCount();
}
} else {
int now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastSelectTime < 500) {
EasyThrottle.throttle(
'topOrRefresh',
const Duration(milliseconds: 500),
() {
if (currentNav == NavigationBarType.home) {
homeController!.onRefresh();
} else if (currentNav == NavigationBarType.dynamics) {
dynamicController.onRefresh();
}
},
);
} else {
if (currentNav == NavigationBarType.home) {
homeController!.toTopOrRefresh();
} else if (currentNav == NavigationBarType.dynamics) {
dynamicController.toTopOrRefresh();
}
}
_lastSelectTime = now;
}
}
@override
void onClose() {
bottomBarStream?.close();

View File

@@ -5,18 +5,13 @@ import 'package:PiliPlus/common/widgets/tabs.dart';
import 'package:PiliPlus/models/common/dynamic/dynamic_badge_mode.dart';
import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models/common/nav_bar_config.dart';
import 'package:PiliPlus/pages/dynamics/controller.dart';
import 'package:PiliPlus/pages/dynamics/view.dart';
import 'package:PiliPlus/pages/home/controller.dart';
import 'package:PiliPlus/pages/home/view.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/utils/app_scheme.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/feed_back.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
@@ -36,11 +31,6 @@ class MainApp extends StatefulWidget {
class _MainAppState extends State<MainApp>
with RouteAware, WidgetsBindingObserver {
final MainController _mainController = Get.put(MainController());
late final _homeController = Get.put(HomeController());
late final _dynamicController = Get.put(DynamicsController());
static const _period = 5 * 60 * 1000;
late int _lastSelectTime = 0;
@override
void initState() {
@@ -57,9 +47,10 @@ class _MainAppState extends State<MainApp>
@override
void didPopNext() {
WidgetsBinding.instance.addObserver(this);
_mainController.checkUnreadDynamic();
_checkDefaultSearch(true);
_checkUnread(context.orientation == Orientation.portrait);
_mainController
..checkUnreadDynamic()
..checkDefaultSearch(true)
..checkUnread(context.orientation == Orientation.portrait);
super.didPopNext();
}
@@ -72,85 +63,10 @@ class _MainAppState extends State<MainApp>
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_mainController.checkUnreadDynamic();
_checkDefaultSearch(true);
_checkUnread(context.orientation == Orientation.portrait);
}
}
void _checkDefaultSearch([bool shouldCheck = false]) {
if (_mainController.homeIndex != -1 && _homeController.enableSearchWord) {
if (shouldCheck &&
_mainController.navigationBars[_mainController.selectedIndex.value] !=
NavigationBarType.home) {
return;
}
int now = DateTime.now().millisecondsSinceEpoch;
if (now - _homeController.lateCheckSearchAt >= _period) {
_homeController
..lateCheckSearchAt = now
..querySearchDefault();
}
}
}
void _checkUnread([bool shouldCheck = false]) {
if (_mainController.accountService.isLogin.value &&
_mainController.homeIndex != -1 &&
_mainController.msgBadgeMode != DynamicBadgeMode.hidden) {
if (shouldCheck &&
_mainController.navigationBars[_mainController.selectedIndex.value] !=
NavigationBarType.home) {
return;
}
int now = DateTime.now().millisecondsSinceEpoch;
if (now - _mainController.lastCheckUnreadAt >= _period) {
_mainController
..lastCheckUnreadAt = now
..queryUnreadMsg();
}
}
}
void setIndex(int value) {
feedBack();
final currentPage = _mainController.navigationBars[value].page;
if (value != _mainController.selectedIndex.value) {
_mainController.selectedIndex.value = value;
if (_mainController.mainTabBarView) {
_mainController.controller.animateTo(value);
} else {
_mainController.controller.jumpToPage(value);
}
if (currentPage is HomePage) {
_checkDefaultSearch();
_checkUnread();
} else if (currentPage is DynamicsPage) {
_mainController.setCount();
}
} else {
int now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastSelectTime < 500) {
EasyThrottle.throttle(
'topOrRefresh',
const Duration(milliseconds: 500),
() {
if (currentPage is HomePage) {
_homeController.onRefresh();
} else if (currentPage is DynamicsPage) {
_dynamicController.onRefresh();
}
},
);
} else {
if (currentPage is HomePage) {
_homeController.toTopOrRefresh();
} else if (currentPage is DynamicsPage) {
_dynamicController.toTopOrRefresh();
}
}
_lastSelectTime = now;
_mainController
..checkUnreadDynamic()
..checkDefaultSearch(true)
..checkUnread(context.orientation == Orientation.portrait);
}
}
@@ -182,9 +98,10 @@ class _MainAppState extends State<MainApp>
onBack();
} else {
if (_mainController.selectedIndex.value != 0) {
setIndex(0);
_mainController.bottomBarStream?.add(true);
_homeController.searchBarStream?.add(true);
_mainController
..setIndex(0)
..bottomBarStream?.add(true)
..homeController?.searchBarStream?.add(true);
} else {
onBack();
}
@@ -229,7 +146,8 @@ class _MainAppState extends State<MainApp>
Radius.circular(16),
),
),
onDestinationSelected: setIndex,
onDestinationSelected:
_mainController.setIndex,
selectedIndex: _mainController
.selectedIndex
.value,
@@ -258,7 +176,8 @@ class _MainAppState extends State<MainApp>
groupAlignment: 0.5,
selectedIndex:
_mainController.selectedIndex.value,
onDestinationSelected: setIndex,
onDestinationSelected:
_mainController.setIndex,
labelType: NavigationRailLabelType.selected,
leading: userAndSearchVertical(theme),
destinations: _mainController.navigationBars
@@ -329,7 +248,8 @@ class _MainAppState extends State<MainApp>
? _mainController.navigationBars.length > 1
? Obx(
() => NavigationBar(
onDestinationSelected: setIndex,
onDestinationSelected:
_mainController.setIndex,
selectedIndex:
_mainController.selectedIndex.value,
destinations: _mainController
@@ -353,7 +273,7 @@ class _MainAppState extends State<MainApp>
() => BottomNavigationBar(
currentIndex:
_mainController.selectedIndex.value,
onTap: setIndex,
onTap: _mainController.setIndex,
iconSize: 16,
selectedFontSize: 12,
unselectedFontSize: 12,
@@ -424,8 +344,7 @@ class _MainAppState extends State<MainApp>
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () =>
_homeController.showUserInfoDialog(context),
onTap: _mainController.toMinePage,
splashColor: theme.colorScheme.primaryContainer
.withValues(alpha: 0.3),
customBorder: const CircleBorder(),
@@ -461,8 +380,7 @@ class _MainAppState extends State<MainApp>
)
: defaultUser(
theme: theme,
onPressed: () =>
_homeController.showUserInfoDialog(context),
onPressed: _mainController.toMinePage,
),
),
),

View File

@@ -1,65 +0,0 @@
import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/data.dart';
import 'package:PiliPlus/pages/common/common_data_controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MediaController
extends CommonDataController<FavFolderData, FavFolderData> {
final list = <({IconData icon, String title, VoidCallback onTap})>[
(
icon: Icons.history,
title: '观看记录',
onTap: () => Get.toNamed('/history'),
),
(
icon: Icons.subscriptions_outlined,
title: '我的订阅',
onTap: () => Get.toNamed('/subscription'),
),
(
icon: Icons.watch_later_outlined,
title: '稍后再看',
onTap: () => Get.toNamed('/later'),
),
(
icon: Icons.create_outlined,
title: '创作中心',
onTap: () => Get.toNamed(
'/webview',
parameters: {
'url': 'https://member.bilibili.com/platform/home',
},
),
),
];
RxInt count = (-1).obs;
AccountService accountService = Get.find<AccountService>();
@override
void onInit() {
super.onInit();
if (accountService.isLogin.value) {
queryData();
}
}
@override
bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) {
count.value = response.response.count ?? -1;
loadingState.value = response;
return true;
}
@override
Future<LoadingState<FavFolderData>> customGetData() {
return FavHttp.userfavFolder(
pn: 1,
ps: 5,
mid: accountService.mid,
);
}
}

View File

@@ -1,245 +0,0 @@
import 'dart:async';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/nav_bar_config.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
import 'package:PiliPlus/pages/common/common_page.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/pages/media/controller.dart';
import 'package:PiliPlus/pages/media/widgets/item.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class MediaPage extends CommonPage {
const MediaPage({super.key});
@override
State<MediaPage> createState() => _MediaPageState();
}
class _MediaPageState extends CommonPageState<MediaPage, MediaController>
with AutomaticKeepAliveClientMixin {
@override
MediaController controller = Get.put(MediaController());
late final MainController _mainController = Get.find<MainController>();
@override
bool get wantKeepAlive => true;
@override
void listener() {
if (_mainController.navigationBars[0] != NavigationBarType.media &&
_mainController.selectedIndex.value == 0) {
return;
}
super.listener();
}
@override
Widget build(BuildContext context) {
super.build(context);
final theme = Theme.of(context);
Color primary = theme.colorScheme.primary;
return Padding(
padding: const EdgeInsets.only(top: 30),
child: Material(
type: MaterialType.transparency,
child: ListView(
controller: controller.scrollController,
physics: const AlwaysScrollableScrollPhysics(),
children: [
ListTile(
leading: null,
title: Padding(
padding: const EdgeInsets.only(left: 20),
child: Text(
'媒体库',
style: TextStyle(
fontSize: theme.textTheme.titleLarge!.fontSize,
fontWeight: FontWeight.bold,
),
),
),
trailing: IconButton(
tooltip: '设置',
onPressed: () => Get.toNamed('/setting'),
icon: const Icon(
Icons.settings_outlined,
size: 20,
),
),
),
for (var item in controller.list)
ListTile(
onTap: item.onTap,
dense: true,
leading: Padding(
padding: const EdgeInsets.only(left: 15),
child: Icon(
item.icon,
color: primary,
),
),
contentPadding: const EdgeInsets.only(
left: 15,
top: 2,
bottom: 2,
),
minLeadingWidth: 0,
title: Text(
item.title,
style: const TextStyle(fontSize: 15),
),
),
Obx(
() => controller.loadingState.value is Loading
? const SizedBox.shrink()
: favFolder(theme),
),
],
),
),
);
}
Widget favFolder(ThemeData theme) {
return Column(
children: [
Divider(
height: 20,
color: theme.dividerColor.withValues(alpha: 0.1),
),
ListTile(
onTap: () async {
await Get.toNamed('/fav');
Future.delayed(const Duration(milliseconds: 150), () {
controller.onRefresh();
});
},
dense: true,
title: Padding(
padding: const EdgeInsets.only(left: 10),
child: Obx(
() {
final count = controller.count.value;
return Text.rich(
TextSpan(
children: [
TextSpan(
text: '我的收藏 ',
style: TextStyle(
fontSize: theme.textTheme.titleMedium!.fontSize,
fontWeight: FontWeight.bold,
),
),
if (count != -1)
TextSpan(
text: "$count ",
style: TextStyle(
fontSize: theme.textTheme.titleSmall!.fontSize,
color: theme.colorScheme.primary,
),
),
WidgetSpan(
child: Icon(
Icons.arrow_forward_ios,
size: 18,
color: theme.colorScheme.primary,
),
),
],
),
);
},
),
),
trailing: IconButton(
tooltip: '刷新',
onPressed: controller.onRefresh,
icon: const Icon(Icons.refresh, size: 20),
),
),
SizedBox(
width: double.infinity,
height: 200,
child: Obx(() => _buildBody(theme, controller.loadingState.value)),
),
const SizedBox(height: 100),
],
);
}
Widget _buildBody(ThemeData theme, LoadingState loadingState) {
return switch (loadingState) {
Loading() => const SizedBox.shrink(),
Success(:var response) => Builder(
builder: (context) {
List<FavFolderInfo>? favFolderList = response.list;
if (favFolderList.isNullOrEmpty) {
return const SizedBox.shrink();
}
bool flag = controller.count.value > favFolderList!.length;
return ListView.separated(
padding: const EdgeInsets.only(left: 20),
itemCount: response.list.length + (flag ? 1 : 0),
itemBuilder: (context, index) {
if (flag && index == favFolderList.length) {
return Padding(
padding: const EdgeInsets.only(right: 14, bottom: 35),
child: Center(
child: IconButton(
tooltip: '查看更多',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStateProperty.resolveWith((
states,
) {
return theme.colorScheme.primaryContainer.withValues(
alpha: 0.5,
);
}),
),
onPressed: () async {
await Get.toNamed('/fav');
Future.delayed(const Duration(milliseconds: 150), () {
controller.onRefresh();
});
},
icon: Icon(
Icons.arrow_forward_ios,
size: 18,
color: theme.colorScheme.primary,
),
),
),
);
} else {
return FavFolderItem(
heroTag: Utils.generateRandomString(8),
item: response.list[index],
callback: () => Future.delayed(
const Duration(milliseconds: 150),
controller.onRefresh,
),
);
}
},
scrollDirection: Axis.horizontal,
separatorBuilder: (context, index) => const SizedBox(width: 14),
);
},
),
Error(:var errMsg) => SizedBox(
height: 160,
child: Center(
child: Text(
errMsg ?? '',
textAlign: TextAlign.center,
),
),
),
};
}
}

View File

@@ -47,7 +47,10 @@ class _MemberDynamicsPageState extends State<MemberDynamicsPage>
return widget.mid == null
? Scaffold(
appBar: AppBar(title: const Text('我的动态')),
body: _buildBody,
body: SafeArea(
bottom: false,
child: _buildBody,
),
)
: _buildBody;
}

View File

@@ -531,9 +531,7 @@ class _EditProfilePageState extends State<EditProfilePage> {
.then((res) {
if (res.data['code'] == 0) {
SmartDialog.showToast('修改成功');
Future.delayed(
const Duration(milliseconds: 500),
).whenComplete(_getInfo);
Future.delayed(const Duration(milliseconds: 500), _getInfo);
} else {
SmartDialog.showToast(res.data['message']);
}

View File

@@ -1,8 +1,12 @@
import 'package:PiliPlus/http/fav.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/user.dart';
import 'package:PiliPlus/models/common/account_type.dart';
import 'package:PiliPlus/models/common/theme/theme_type.dart';
import 'package:PiliPlus/models/user/info.dart';
import 'package:PiliPlus/models/user/stat.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/data.dart';
import 'package:PiliPlus/pages/common/common_data_controller.dart';
import 'package:PiliPlus/services/account_service.dart';
import 'package:PiliPlus/utils/accounts.dart';
import 'package:PiliPlus/utils/accounts/account.dart';
@@ -14,14 +18,17 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class MineController extends GetxController {
class MineController
extends CommonDataController<FavFolderData, FavFolderData> {
AccountService accountService = Get.find<AccountService>();
int? favFoldercount;
// 用户信息 头像、昵称、lv
Rx<UserInfoData> userInfo = UserInfoData().obs;
// 用户状态 动态、关注、粉丝
Rx<UserStat> userStat = UserStat().obs;
AccountService accountService = Get.find<AccountService>();
Rx<ThemeType> themeType = ThemeType.system.obs;
static RxBool anonymity =
(Accounts.account.isNotEmpty &&
@@ -30,33 +37,73 @@ class MineController extends GetxController {
ThemeType get nextThemeType =>
ThemeType.values[(themeType.value.index + 1) % ThemeType.values.length];
late final list = <({IconData icon, String title, VoidCallback onTap})>[
(
icon: Icons.history,
title: '观看记录',
onTap: () {
if (isLogin) {
Get.toNamed('/history');
}
},
),
(
icon: Icons.subscriptions_outlined,
title: '我的订阅',
onTap: () {
if (isLogin) {
Get.toNamed('/subscription');
}
},
),
(
icon: Icons.watch_later_outlined,
title: '稍后再看',
onTap: () {
if (isLogin) {
Get.toNamed('/later');
}
},
),
(
icon: Icons.create_outlined,
title: '创作中心',
onTap: () {
if (isLogin) {
Get.toNamed(
'/webview',
parameters: {
'url': 'https://member.bilibili.com/platform/home',
},
);
}
},
),
];
@override
void onInit() {
super.onInit();
UserInfoData? userInfoCache = GStorage.userInfo.get('userInfoCache');
if (userInfoCache != null) {
userInfo.value = userInfoCache;
queryData();
queryUserInfo();
}
}
void onLogin([bool longPress = false]) {
if (!accountService.isLogin.value || longPress) {
Get.toNamed('/loginPage', preventDuplicates: false);
} else {
Get.toNamed(
'/member?mid=${userInfo.value.mid}',
preventDuplicates: false,
);
bool get isLogin {
if (!accountService.isLogin.value) {
// SmartDialog.showToast('账号未登录');
return false;
}
return true;
}
Future<void> queryUserInfo() async {
if (!accountService.isLogin.value) {
return;
}
var res = await UserHttp.userInfo();
if (res['status']) {
UserInfoData data = res['data'];
if (res.isSuccess) {
UserInfoData data = res.data;
if (data.isLogin == true) {
userInfo.value = data;
GStorage.userInfo.put('userInfoCache', data);
@@ -70,8 +117,9 @@ class MineController extends GetxController {
return;
}
} else {
SmartDialog.showToast(res['msg']);
if (res['msg'] == '账号未登录') {
final errMsg = res.toString();
SmartDialog.showToast(errMsg);
if (errMsg == '账号未登录') {
LoginUtils.onLogoutMain();
return;
}
@@ -86,6 +134,22 @@ class MineController extends GetxController {
}
}
@override
bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) {
favFoldercount = response.response.count;
loadingState.value = response;
return true;
}
@override
Future<LoadingState<FavFolderData>> customGetData() {
return FavHttp.userfavFolder(
pn: 1,
ps: 5,
mid: accountService.mid,
);
}
static void onChangeAnonymity() {
if (Accounts.account.isEmpty) {
SmartDialog.showToast('请先登录');
@@ -202,37 +266,31 @@ class MineController extends GetxController {
void onChangeTheme() {
final newVal = nextThemeType;
themeType.value = newVal;
try {
Get.find<MineController>().themeType.value = newVal;
} catch (_) {}
GStorage.setting.put(SettingBoxKey.themeMode, newVal.index);
Get.changeThemeMode(newVal.toThemeMode);
}
void pushFollow() {
if (!accountService.isLogin.value) {
SmartDialog.showToast('账号未登录');
return;
void push(String name) {
late final mid = userInfo.value.mid;
if (isLogin && mid != null) {
Get.toNamed('/$name?mid=$mid');
}
Get.toNamed('/follow?mid=${userInfo.value.mid}', preventDuplicates: false);
}
void pushFans() {
if (!accountService.isLogin.value) {
SmartDialog.showToast('账号未登录');
return;
void onLogin([bool longPress = false]) {
if (!accountService.isLogin.value || longPress) {
Get.toNamed('/loginPage');
} else {
Get.toNamed('/member?mid=${userInfo.value.mid}');
}
Get.toNamed('/fan?mid=${userInfo.value.mid}', preventDuplicates: false);
}
void pushDynamic() {
@override
Future<void> onRefresh() {
if (!accountService.isLogin.value) {
SmartDialog.showToast('账号未登录');
return;
return Future.value();
}
Get.toNamed(
'/memberDynamics?mid=${userInfo.value.mid}',
preventDuplicates: false,
);
queryUserInfo();
return super.onRefresh();
}
}

View File

@@ -1,121 +1,208 @@
import 'dart:async';
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models/common/image_type.dart';
import 'package:PiliPlus/models/common/nav_bar_config.dart';
import 'package:PiliPlus/models/user/info.dart';
import 'package:PiliPlus/models_new/fav/fav_folder/list.dart';
import 'package:PiliPlus/pages/common/common_page.dart';
import 'package:PiliPlus/pages/login/controller.dart';
import 'package:PiliPlus/pages/main/controller.dart';
import 'package:PiliPlus/pages/mine/controller.dart';
import 'package:PiliPlus/pages/mine/widgets/item.dart';
import 'package:PiliPlus/utils/extension.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
class MinePage extends StatefulWidget {
class MinePage extends CommonPage {
const MinePage({super.key});
@override
State<MinePage> createState() => _MinePageState();
State<MinePage> createState() => _MediaPageState();
}
class _MinePageState extends State<MinePage> {
final MineController _mineController = Get.put(MineController())
..queryUserInfo();
class _MediaPageState extends CommonPageState<MinePage, MineController>
with AutomaticKeepAliveClientMixin {
@override
MineController controller = Get.put(MineController());
late final MainController _mainController = Get.find<MainController>();
Widget _header(ThemeData theme) => Row(
children: [
const SizedBox(width: 12),
Image.asset(
'assets/images/logo/logo.png',
width: 35,
),
const SizedBox(width: 5),
Text(
'PiliPlus',
style: theme.textTheme.titleMedium,
),
const Spacer(),
Obx(
() {
final anonymity = MineController.anonymity.value;
return IconButton(
iconSize: 40.0,
padding: const EdgeInsets.all(8),
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
tooltip: "${anonymity ? '退出' : '进入'}无痕模式",
onPressed: MineController.onChangeAnonymity,
icon: anonymity
? const Icon(MdiIcons.incognito, size: 24)
: const Icon(MdiIcons.incognitoOff, size: 24),
);
},
),
Obx(
() {
return IconButton(
iconSize: 40.0,
padding: const EdgeInsets.all(8),
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
tooltip: '切换至${_mineController.nextThemeType.desc}主题',
onPressed: _mineController.onChangeTheme,
icon: _mineController.themeType.value.icon,
);
},
),
IconButton(
iconSize: 40.0,
padding: const EdgeInsets.all(8),
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
tooltip: '设置',
onPressed: () => {
Get.back(),
Get.toNamed('/setting', preventDuplicates: false),
},
icon: const Icon(MdiIcons.cogs, size: 24),
),
const SizedBox(width: 10),
],
);
@override
bool get wantKeepAlive => true;
@override
void listener() {
if (_mainController.navigationBars[0] != NavigationBarType.mine &&
_mainController.selectedIndex.value == 0) {
return;
}
super.listener();
}
@override
Widget build(BuildContext context) {
super.build(context);
final theme = Theme.of(context);
return IntrinsicWidth(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
_header(theme),
const SizedBox(height: 10),
userInfoBuild(theme),
],
),
return Column(
children: [
const SizedBox(height: 10),
_buildHeaderActions,
const SizedBox(height: 10),
Expanded(
child: refreshIndicator(
onRefresh: controller.onRefresh,
child: ListView(
controller: controller.scrollController,
physics: const AlwaysScrollableScrollPhysics(),
children: [
_buildUserInfo(theme),
_buildActions(theme.colorScheme.primary),
Obx(
() => controller.loadingState.value is Loading
? const SizedBox.shrink()
: _buildFav(theme),
),
],
),
),
),
],
);
}
Widget userInfoBuild(ThemeData theme) {
TextStyle style = TextStyle(
Widget _buildActions(Color primary) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: controller.list
.map(
(e) => InkWell(
onTap: e.onTap,
borderRadius: StyleString.mdRadius,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 80),
child: AspectRatio(
aspectRatio: 1,
child: Column(
spacing: 6,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
size: 22,
e.icon,
color: primary,
),
Text(
e.title,
style: const TextStyle(fontSize: 13),
),
],
),
),
),
),
)
.toList(),
);
}
Widget get _buildHeaderActions {
return Row(
spacing: 5,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Obx(
() {
final anonymity = MineController.anonymity.value;
return IconButton(
iconSize: 22,
padding: const EdgeInsets.all(8),
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
tooltip: "${anonymity ? '退出' : '进入'}无痕模式",
onPressed: MineController.onChangeAnonymity,
icon: anonymity
? const Icon(MdiIcons.incognito)
: const Icon(MdiIcons.incognitoOff),
);
},
),
IconButton(
iconSize: 22,
padding: const EdgeInsets.all(8),
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
tooltip: '设置账号模式',
onPressed: () => LoginPageController.switchAccountDialog(context),
icon: const Icon(Icons.switch_account_outlined),
),
Obx(
() {
return IconButton(
iconSize: 22,
padding: const EdgeInsets.all(8),
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
tooltip: '切换至${controller.nextThemeType.desc}主题',
onPressed: controller.onChangeTheme,
icon: controller.themeType.value.icon,
);
},
),
IconButton(
iconSize: 22,
padding: const EdgeInsets.all(8),
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
tooltip: '设置',
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
icon: const Icon(Icons.settings_outlined),
),
const SizedBox(width: 16),
],
);
}
Widget _buildUserInfo(ThemeData theme) {
final style = TextStyle(
fontSize: theme.textTheme.titleMedium!.fontSize,
color: theme.colorScheme.primary,
fontWeight: FontWeight.bold,
);
final lebelStyle = theme.textTheme.labelMedium!.copyWith(
color: theme.colorScheme.outline,
);
final coinLabelStyle = TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
color: theme.colorScheme.outline,
);
final coinValStyle = TextStyle(
fontSize: theme.textTheme.labelMedium!.fontSize,
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
);
return Obx(() {
final UserInfoData userInfo = _mineController.userInfo.value;
final UserInfoData userInfo = controller.userInfo.value;
final LevelInfo? levelInfo = userInfo.levelInfo;
final isVip = userInfo.vipStatus != null && userInfo.vipStatus! > 0;
final userStat = _mineController.userStat.value;
final userStat = controller.userStat.value;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: _mineController.onLogin,
onTap: controller.onLogin,
onLongPress: () {
Feedback.forLongPress(context);
_mineController.onLogin(true);
controller.onLogin(true);
},
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -160,11 +247,11 @@ class _MinePageState extends State<MinePage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
spacing: 4,
spacing: 6,
children: [
Flexible(
child: Text(
userInfo.uname ?? '点击头像登录',
userInfo.uname ?? '点击登录',
style: theme.textTheme.titleMedium!.copyWith(
height: 1,
color: isVip && userInfo.vipType == 2
@@ -184,69 +271,44 @@ class _MinePageState extends State<MinePage> {
],
),
const SizedBox(height: 8),
FittedBox(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '硬币 ',
style: TextStyle(
fontSize:
theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
),
),
TextSpan(
text: userInfo.money?.toString() ?? '-',
style: TextStyle(
fontSize:
theme.textTheme.labelSmall!.fontSize,
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
TextSpan(
text: " 经验 ",
style: TextStyle(
fontSize:
theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
),
),
TextSpan(
text: "${levelInfo?.currentExp ?? '-'}",
semanticsLabel:
"当前${levelInfo?.currentExp ?? '-'}",
style: TextStyle(
fontSize:
theme.textTheme.labelSmall!.fontSize,
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
TextSpan(
text: "/${levelInfo?.nextExp ?? '-'}",
semanticsLabel:
"升级需${levelInfo?.nextExp ?? '-'}",
style: TextStyle(
fontSize:
theme.textTheme.labelSmall!.fontSize,
color: theme.colorScheme.outline,
),
),
],
),
Text.rich(
TextSpan(
children: [
TextSpan(
text: '硬币 ',
style: coinLabelStyle,
),
TextSpan(
text: userInfo.money?.toString() ?? '-',
style: coinValStyle,
),
TextSpan(
text: " 经验 ",
style: coinLabelStyle,
),
TextSpan(
text: levelInfo?.currentExp?.toString() ?? '-',
style: coinValStyle,
),
TextSpan(
text: "/${levelInfo?.nextExp ?? '-'}",
style: coinLabelStyle,
),
],
),
),
const SizedBox(height: 4),
LinearProgressIndicator(
minHeight: 2,
value: levelInfo != null
? (levelInfo.currentExp! / levelInfo.nextExp!)
: 0,
backgroundColor: theme.colorScheme.inversePrimary,
valueColor: AlwaysStoppedAnimation<Color>(
theme.colorScheme.primary,
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 225),
child: LinearProgressIndicator(
minHeight: 2.25,
value: levelInfo != null
? (levelInfo.currentExp! / levelInfo.nextExp!)
: 0,
backgroundColor: theme.colorScheme.inversePrimary,
valueColor: AlwaysStoppedAnimation<Color>(
theme.colorScheme.primary,
),
),
),
],
@@ -257,37 +319,32 @@ class _MinePageState extends State<MinePage> {
),
),
const SizedBox(height: 10),
Center(
child: SizedBox(
width: 240,
child: Row(
children: [
_btn(
count: userStat.dynamicCount,
countStyle: style,
name: '动态',
nameStyle: theme.textTheme.labelMedium,
onTap: _mineController.pushDynamic,
),
_btn(
count: userStat.following,
countStyle: style,
name: '关注',
nameStyle: theme.textTheme.labelMedium,
onTap: _mineController.pushFollow,
),
_btn(
count: userStat.follower,
countStyle: style,
name: '粉丝',
nameStyle: theme.textTheme.labelMedium,
onTap: _mineController.pushFans,
),
],
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_btn(
count: userStat.dynamicCount,
countStyle: style,
name: '动态',
lebelStyle: lebelStyle,
onTap: () => controller.push('memberDynamics'),
),
),
_btn(
count: userStat.following,
countStyle: style,
name: '关注',
lebelStyle: lebelStyle,
onTap: () => controller.push('follow'),
),
_btn(
count: userStat.follower,
countStyle: style,
name: '粉丝',
lebelStyle: lebelStyle,
onTap: () => controller.push('fan'),
),
],
),
const SizedBox(height: 20),
],
);
});
@@ -297,31 +354,165 @@ class _MinePageState extends State<MinePage> {
required int? count,
required TextStyle countStyle,
required String name,
required TextStyle? nameStyle,
required TextStyle? lebelStyle,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
borderRadius: StyleString.mdRadius,
child: SizedBox(
width: 80,
height: 80,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
count?.toString() ?? '-',
style: countStyle,
),
const SizedBox(height: 8),
Text(
name,
style: nameStyle,
),
],
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 80),
child: AspectRatio(
aspectRatio: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
count?.toString() ?? '-',
style: countStyle,
),
const SizedBox(height: 4),
Text(
name,
style: lebelStyle,
),
],
),
),
),
);
}
Widget _buildFav(ThemeData theme) {
return Column(
children: [
Divider(
height: 20,
color: theme.dividerColor.withValues(alpha: 0.1),
),
ListTile(
onTap: () => Get.toNamed('/fav')?.whenComplete(
() => Future.delayed(
const Duration(milliseconds: 150),
controller.onRefresh,
),
),
dense: true,
title: Padding(
padding: const EdgeInsets.only(left: 10),
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '我的收藏 ',
style: TextStyle(
fontSize: theme.textTheme.titleMedium!.fontSize,
fontWeight: FontWeight.bold,
),
),
if (controller.favFoldercount != null)
TextSpan(
text: "${controller.favFoldercount} ",
style: TextStyle(
fontSize: theme.textTheme.titleSmall!.fontSize,
color: theme.colorScheme.primary,
),
),
WidgetSpan(
child: Icon(
Icons.arrow_forward_ios,
size: 18,
color: theme.colorScheme.primary,
),
),
],
),
),
),
trailing: IconButton(
tooltip: '刷新',
onPressed: controller.onRefresh,
icon: const Icon(Icons.refresh, size: 20),
),
),
SizedBox(
width: double.infinity,
height: 200,
child: _buildFavBody(theme, controller.loadingState.value),
),
const SizedBox(height: 100),
],
);
}
Widget _buildFavBody(ThemeData theme, LoadingState loadingState) {
return switch (loadingState) {
Loading() => const SizedBox.shrink(),
Success(:var response) => Builder(
builder: (context) {
List<FavFolderInfo>? favFolderList = response.list;
if (favFolderList.isNullOrEmpty) {
return const SizedBox.shrink();
}
bool flag = (controller.favFoldercount ?? 0) > favFolderList!.length;
return ListView.separated(
padding: const EdgeInsets.only(left: 20),
itemCount: response.list.length + (flag ? 1 : 0),
itemBuilder: (context, index) {
if (flag && index == favFolderList.length) {
return Padding(
padding: const EdgeInsets.only(right: 14, bottom: 35),
child: Center(
child: IconButton(
tooltip: '查看更多',
style: ButtonStyle(
padding: WidgetStateProperty.all(EdgeInsets.zero),
backgroundColor: WidgetStatePropertyAll(
theme.colorScheme.primaryContainer.withValues(
alpha: 0.5,
),
),
),
onPressed: () => Get.toNamed('/fav')?.whenComplete(
() => Future.delayed(
const Duration(milliseconds: 150),
controller.onRefresh,
),
),
icon: Icon(
Icons.arrow_forward_ios,
size: 18,
color: theme.colorScheme.primary,
),
),
),
);
} else {
return FavFolderItem(
heroTag: Utils.generateRandomString(8),
item: response.list[index],
callback: () => Future.delayed(
const Duration(milliseconds: 150),
controller.onRefresh,
),
);
}
},
scrollDirection: Axis.horizontal,
separatorBuilder: (context, index) => const SizedBox(width: 14),
);
},
),
Error(:var errMsg) => SizedBox(
height: 160,
child: Center(
child: Text(
errMsg ?? '',
textAlign: TextAlign.center,
),
),
),
};
}
}

View File

@@ -99,7 +99,8 @@ List<SettingsModel> get extraSettings => [
SettingBoxKey.dynamicPeriod,
dynamicPeriod,
);
Get.find<MainController>().dynamicPeriod = dynamicPeriod;
Get.find<MainController>().dynamicPeriod =
dynamicPeriod * 60 * 1000;
},
child: const Text('确定'),
),

View File

@@ -91,7 +91,8 @@ class _SetDisplayModeState extends State<SetDisplayMode> {
).whenComplete(
() => Future.delayed(
const Duration(milliseconds: 100),
).whenComplete(fetchAll),
fetchAll,
),
);
},
);

View File

@@ -592,9 +592,7 @@ class VideoIntroController extends CommonIntroController with ReloadMixin {
followStatus: followStatus,
callback: (attribute) {
followStatus['attribute'] = attribute;
Future.delayed(const Duration(milliseconds: 500), () {
queryFollowStatus();
});
Future.delayed(const Duration(milliseconds: 500), queryFollowStatus);
},
);
}

View File

@@ -428,9 +428,10 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
await videoDetailController.playerInit();
}
Future.delayed(const Duration(milliseconds: 600), () {
AutoOrientation.fullAutoMode();
});
Future.delayed(
const Duration(milliseconds: 600),
AutoOrientation.fullAutoMode,
);
plPlayerController?.addStatusLister(playerListener);
plPlayerController?.addPositionListener(positionListener);
}

View File

@@ -2163,14 +2163,9 @@ class HeaderControlState extends State<HeaderControl> {
children: [
TextButton(
style: ButtonStyle(
foregroundColor:
WidgetStateProperty.resolveWith((
states,
) {
return theme
.snackBarTheme
.actionTextColor;
}),
foregroundColor: WidgetStatePropertyAll(
theme.snackBarTheme.actionTextColor,
),
),
onPressed: () {
plPlayerController.setBackgroundPlay(
@@ -2183,14 +2178,9 @@ class HeaderControlState extends State<HeaderControl> {
const SizedBox(width: 10),
TextButton(
style: ButtonStyle(
foregroundColor:
WidgetStateProperty.resolveWith((
states,
) {
return theme
.snackBarTheme
.actionTextColor;
}),
foregroundColor: WidgetStatePropertyAll(
theme.snackBarTheme.actionTextColor,
),
),
onPressed: () {},
child: const Text('不启用'),
@@ -2203,7 +2193,7 @@ class HeaderControlState extends State<HeaderControl> {
showCloseIcon: true,
),
);
await Future.delayed(const Duration(seconds: 3), () {});
await Future.delayed(const Duration(seconds: 3));
}
if (!context.mounted) return;
PageUtils.enterPip(