mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: 首页顶栏收入侧栏,线性宽度、渐变底色扩大,“我的”页面重构为弹窗,统一替换图标
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -6,6 +7,8 @@ import 'package:hive/hive.dart';
|
||||
import 'package:PiliPalaX/models/common/tab_type.dart';
|
||||
import 'package:PiliPalaX/utils/storage.dart';
|
||||
import '../../http/index.dart';
|
||||
import '../../utils/feed_back.dart';
|
||||
import '../mine/view.dart';
|
||||
|
||||
class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||
bool flag = false;
|
||||
@@ -27,6 +30,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||
late List<String> tabbarSort;
|
||||
RxString defaultSearch = ''.obs;
|
||||
late bool enableGradientBg;
|
||||
late bool useSideBar;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@@ -41,6 +45,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||
}
|
||||
enableGradientBg =
|
||||
setting.get(SettingBoxKey.enableGradientBg, defaultValue: true);
|
||||
useSideBar = setting.get(SettingBoxKey.useSideBar, defaultValue: false);
|
||||
// 进行tabs配置
|
||||
setTabConfig();
|
||||
}
|
||||
@@ -117,4 +122,14 @@ class HomeController extends GetxController with GetTickerProviderStateMixin {
|
||||
defaultSearch.value = res.data['data']['name'];
|
||||
}
|
||||
}
|
||||
|
||||
showUserInfoDialog(context) {
|
||||
feedBack();
|
||||
showDialog(
|
||||
context: context,
|
||||
useSafeArea: true,
|
||||
builder: (_) => const Dialog(
|
||||
child: MinePage(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPalaX/pages/mine/index.dart';
|
||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||
import '../../utils/storage.dart';
|
||||
import './controller.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
@@ -30,19 +30,6 @@ class _HomePageState extends State<HomePage>
|
||||
stream = _homeController.searchBarStream.stream;
|
||||
}
|
||||
|
||||
showUserBottomSheet() {
|
||||
feedBack();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => const SizedBox(
|
||||
height: 450,
|
||||
child: MinePage(),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
isScrollControlled: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
@@ -57,7 +44,7 @@ class _HomePageState extends State<HomePage>
|
||||
// }
|
||||
return Scaffold(
|
||||
extendBody: true,
|
||||
extendBodyBehindAppBar: true,
|
||||
// extendBodyBehindAppBar: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
toolbarHeight: 0,
|
||||
@@ -72,12 +59,12 @@ class _HomePageState extends State<HomePage>
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
if (!_homeController.useSideBar)
|
||||
CustomAppBar(
|
||||
stream: _homeController.hideSearchBar
|
||||
? stream
|
||||
: StreamController<bool>.broadcast().stream,
|
||||
ctr: _homeController,
|
||||
callback: showUserBottomSheet,
|
||||
),
|
||||
if (_homeController.tabs.length > 1) ...[
|
||||
if (_homeController.enableGradientBg) ...[
|
||||
@@ -128,15 +115,13 @@ class _HomePageState extends State<HomePage>
|
||||
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
final double height;
|
||||
final Stream<bool>? stream;
|
||||
final HomeController? ctr;
|
||||
final Function? callback;
|
||||
final HomeController ctr;
|
||||
|
||||
const CustomAppBar({
|
||||
super.key,
|
||||
this.height = kToolbarHeight,
|
||||
this.stream,
|
||||
this.ctr,
|
||||
this.callback,
|
||||
required this.ctr,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -148,24 +133,16 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
stream: stream,
|
||||
initialData: true,
|
||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||
final RxBool isUserLoggedIn = ctr!.userLogin;
|
||||
final double top = MediaQuery.of(context).padding.top;
|
||||
return AnimatedOpacity(
|
||||
opacity: snapshot.data ? 1 : 0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: AnimatedContainer(
|
||||
curve: Curves.easeInOutCubicEmphasized,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
height: snapshot.data ? top + 52 : top,
|
||||
padding: EdgeInsets.fromLTRB(14, top + 6, 14, 0),
|
||||
child: Obx(
|
||||
() => UserInfoWidget(
|
||||
top: top,
|
||||
height: snapshot.data ? 52 : 0,
|
||||
padding: const EdgeInsets.fromLTRB(14, 6, 14, 0),
|
||||
child: SearchBarAndUser(
|
||||
ctr: ctr,
|
||||
userLogin: isUserLoggedIn,
|
||||
userFace: ctr?.userFace.value,
|
||||
callback: () => callback!(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -174,21 +151,13 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class UserInfoWidget extends StatelessWidget {
|
||||
const UserInfoWidget({
|
||||
class SearchBarAndUser extends StatelessWidget {
|
||||
const SearchBarAndUser({
|
||||
Key? key,
|
||||
required this.top,
|
||||
required this.userLogin,
|
||||
required this.userFace,
|
||||
required this.callback,
|
||||
required this.ctr,
|
||||
}) : super(key: key);
|
||||
|
||||
final double top;
|
||||
final RxBool userLogin;
|
||||
final String? userFace;
|
||||
final VoidCallback? callback;
|
||||
final HomeController? ctr;
|
||||
final HomeController ctr;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -196,7 +165,7 @@ class UserInfoWidget extends StatelessWidget {
|
||||
children: [
|
||||
SearchBar(ctr: ctr),
|
||||
const SizedBox(width: 4),
|
||||
Obx(() => userLogin.value
|
||||
Obx(() => ctr.userLogin.value
|
||||
? ClipRect(
|
||||
child: IconButton(
|
||||
tooltip: '消息',
|
||||
@@ -211,20 +180,20 @@ class UserInfoWidget extends StatelessWidget {
|
||||
Semantics(
|
||||
label: "我的",
|
||||
child: Obx(
|
||||
() => userLogin.value
|
||||
() => ctr.userLogin.value
|
||||
? Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
width: 34,
|
||||
height: 34,
|
||||
src: userFace,
|
||||
src: ctr.userFace.value,
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => callback?.call(),
|
||||
onTap: () => ctr.showUserInfoDialog(context),
|
||||
splashColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primaryContainer
|
||||
@@ -237,13 +206,80 @@ class UserInfoWidget extends StatelessWidget {
|
||||
)
|
||||
],
|
||||
)
|
||||
: DefaultUser(callback: () => callback!()),
|
||||
: DefaultUser(
|
||||
callback: () => ctr.showUserInfoDialog(context)),
|
||||
)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UserAndSearchVertical extends StatelessWidget {
|
||||
const UserAndSearchVertical({
|
||||
Key? key,
|
||||
required this.ctr,
|
||||
}) : super(key: key);
|
||||
|
||||
final HomeController ctr;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Semantics(
|
||||
label: "我的",
|
||||
child: Obx(
|
||||
() => ctr.userLogin.value
|
||||
? Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
type: 'avatar',
|
||||
width: 34,
|
||||
height: 34,
|
||||
src: ctr.userFace.value,
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () => ctr.showUserInfoDialog(context),
|
||||
splashColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.primaryContainer
|
||||
.withOpacity(0.3),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(50),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
: DefaultUser(
|
||||
callback: () => ctr.showUserInfoDialog(context)),
|
||||
)),
|
||||
const SizedBox(height: 8),
|
||||
Obx(() => ctr.userLogin.value
|
||||
? IconButton(
|
||||
tooltip: '消息',
|
||||
onPressed: () => Get.toNamed('/whisper'),
|
||||
icon: const Icon(
|
||||
Icons.notifications_none,
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink()),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.search_outlined,
|
||||
semanticLabel: '搜索',
|
||||
),
|
||||
onPressed: () => Get.toNamed('/search'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultUser extends StatelessWidget {
|
||||
const DefaultUser({super.key, this.callback});
|
||||
final Function? callback;
|
||||
@@ -334,7 +370,6 @@ class CustomChip extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ColorScheme colorTheme = Theme.of(context).colorScheme;
|
||||
final Color secondaryContainer = colorTheme.secondaryContainer;
|
||||
final TextStyle chipTextStyle = selected
|
||||
? const TextStyle(fontWeight: FontWeight.bold, fontSize: 13)
|
||||
: const TextStyle(fontSize: 13);
|
||||
@@ -342,16 +377,19 @@ class CustomChip extends StatelessWidget {
|
||||
const VisualDensity visualDensity =
|
||||
VisualDensity(horizontal: -4.0, vertical: -2.0);
|
||||
return InputChip(
|
||||
side: BorderSide(
|
||||
color: selected
|
||||
? colorScheme.onSecondaryContainer.withOpacity(0.2)
|
||||
: Colors.transparent,
|
||||
),
|
||||
backgroundColor: secondaryContainer,
|
||||
selectedColor: secondaryContainer,
|
||||
color: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) => secondaryContainer.withAlpha(200)),
|
||||
padding: const EdgeInsets.fromLTRB(7, 1, 7, 1),
|
||||
side: selected
|
||||
? BorderSide(
|
||||
color: colorScheme.secondary.withOpacity(0.2),
|
||||
width: 2,
|
||||
)
|
||||
: BorderSide.none,
|
||||
// backgroundColor: colorTheme.primaryContainer.withOpacity(0.1),
|
||||
// selectedColor: colorTheme.secondaryContainer.withOpacity(0.8),
|
||||
color:
|
||||
MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {
|
||||
return colorTheme.secondaryContainer.withOpacity(0.6);
|
||||
}),
|
||||
padding: const EdgeInsets.fromLTRB(6, 1, 6, 1),
|
||||
label: Text(label, style: chipTextStyle),
|
||||
onPressed: () => onTap(),
|
||||
selected: selected,
|
||||
|
||||
@@ -130,16 +130,16 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withOpacity(0.9),
|
||||
.withOpacity(0.6),
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withOpacity(0.5),
|
||||
.primaryContainer
|
||||
.withOpacity(0.6),
|
||||
Theme.of(context).colorScheme.surface
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
stops: const [0, 0.0034, 0.34]),
|
||||
stops: const [0.1, 0.4 ,0.7]),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -150,17 +150,17 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||
children: [
|
||||
if (useSideBar) ...[
|
||||
SizedBox(
|
||||
width: 55 + MediaQuery.of(context).padding.left,
|
||||
width: context.width * 0.0387 +
|
||||
36.801 +
|
||||
MediaQuery.of(context).padding.left,
|
||||
child: NavigationRail(
|
||||
groupAlignment: 0.0,
|
||||
minWidth: 40.0,
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.surface
|
||||
.withOpacity(0.2),
|
||||
groupAlignment: 1,
|
||||
minWidth: context.width * 0.0286 + 28.56,
|
||||
backgroundColor: Colors.transparent,
|
||||
selectedIndex: _mainController.selectedIndex,
|
||||
onDestinationSelected: (value) => setIndex(value),
|
||||
labelType: NavigationRailLabelType.none,
|
||||
leading: UserAndSearchVertical(ctr: _homeController),
|
||||
destinations: _mainController.navigationBars
|
||||
.map((e) => NavigationRailDestination(
|
||||
icon: Badge(
|
||||
@@ -168,11 +168,8 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||
DynamicBadgeMode.number
|
||||
? Text(e['count'].toString())
|
||||
: null,
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
2 + MediaQuery.of(context).padding.left,
|
||||
0,
|
||||
2,
|
||||
0),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
isLabelVisible:
|
||||
_mainController.dynamicBadgeType !=
|
||||
DynamicBadgeMode.hidden &&
|
||||
@@ -186,12 +183,19 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||
),
|
||||
selectedIcon: e['selectIcon'],
|
||||
label: Text(e['label']),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 6),
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 0.01 * context.height),
|
||||
))
|
||||
.toList(),
|
||||
trailing: SizedBox(height: 0.1 * context.height),
|
||||
)),
|
||||
],
|
||||
VerticalDivider(
|
||||
width: 1,
|
||||
indent: MediaQuery.of(context).padding.top,
|
||||
endIndent: MediaQuery.of(context).padding.bottom,
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.06),
|
||||
),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
@@ -203,6 +207,7 @@ class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
|
||||
children: _mainController.pages,
|
||||
),
|
||||
),
|
||||
if (useSideBar) SizedBox(width: context.width * 0.004),
|
||||
],
|
||||
)
|
||||
]),
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:PiliPalaX/models/common/theme_type.dart';
|
||||
import 'package:PiliPalaX/models/user/info.dart';
|
||||
import 'package:PiliPalaX/models/user/stat.dart';
|
||||
import 'package:PiliPalaX/utils/storage.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
|
||||
class MineController extends GetxController {
|
||||
// 用户信息 头像、昵称、lv
|
||||
@@ -100,30 +101,29 @@ class MineController extends GetxController {
|
||||
alignment: Alignment.bottomCenter,
|
||||
builder: (context) {
|
||||
return ColoredBox(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 15, horizontal: 20),
|
||||
child:
|
||||
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
const Row(
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
Icons.check,
|
||||
color: Colors.green,
|
||||
MdiIcons.incognito,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
const SizedBox(width: 10),
|
||||
Text('已进入无痕模式',
|
||||
style: TextStyle(fontSize: 15, height: 1.5))
|
||||
style: Theme.of(context).textTheme.titleMedium)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
'搜索、观看视频/直播不携带Cookie与CSRF\n'
|
||||
Text(
|
||||
'搜索、观看视频/直播不携带身份信息(包含大会员)\n'
|
||||
'不产生查询或播放记录\n'
|
||||
'点赞等其它操作不受影响\n'
|
||||
'(前往隐私设置了解详情)',
|
||||
style: TextStyle(fontSize: 12.5, height: 1.5)),
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
@@ -172,17 +172,17 @@ class MineController extends GetxController {
|
||||
builder: (context) {
|
||||
return ColoredBox(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
child: const Padding(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 15, horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.check,
|
||||
color: Colors.green,
|
||||
MdiIcons.incognitoOff,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text('已退出无痕模式'),
|
||||
const SizedBox(width: 10),
|
||||
Text('已退出无痕模式',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
],
|
||||
)));
|
||||
});
|
||||
|
||||
@@ -7,62 +7,9 @@ import 'package:PiliPalaX/common/constants.dart';
|
||||
import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPalaX/models/common/theme_type.dart';
|
||||
import 'package:PiliPalaX/models/user/info.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
import 'controller.dart';
|
||||
|
||||
class LeftClipper extends CustomClipper<Path> {
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
Path path = Path();
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(0, size.height);
|
||||
path.lineTo(size.width / 2 - 2, size.height);
|
||||
path.lineTo(size.width / 2 - 2, 0);
|
||||
path.close();
|
||||
return path;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(CustomClipper<Path> oldClipper) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class RightClipper extends CustomClipper<Path> {
|
||||
@override
|
||||
Path getClip(Size size) {
|
||||
Path path = Path();
|
||||
path.moveTo(size.width, size.height);
|
||||
path.lineTo(size.width / 2 + 2, size.height);
|
||||
path.lineTo(size.width / 2 + 2, 0);
|
||||
path.lineTo(size.width, 0);
|
||||
path.close();
|
||||
return path;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(CustomClipper<Path> oldClipper) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class VerticalLinePainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = Colors.black.withOpacity(0.75)
|
||||
..strokeWidth = 1.2;
|
||||
|
||||
canvas.drawLine(
|
||||
Offset(size.width / 2, size.height-2),
|
||||
Offset(size.width / 2, 3),
|
||||
paint,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) => false;
|
||||
}
|
||||
|
||||
class MinePage extends StatefulWidget {
|
||||
const MinePage({super.key});
|
||||
|
||||
@@ -81,40 +28,45 @@ class _MinePageState extends State<MinePage> {
|
||||
|
||||
mineController.userLogin.listen((status) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_futureBuilderFuture = mineController.queryUserInfo();
|
||||
});
|
||||
_futureBuilderFuture.then((value) => setState(() {}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
scrolledUnderElevation: 0,
|
||||
elevation: 0,
|
||||
toolbarHeight: kTextTabBarHeight + 20,
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: false,
|
||||
title: ExcludeSemantics(
|
||||
child: Row(
|
||||
// 宽度以最长的行为准,便于两端对齐
|
||||
return IntrinsicWidth(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(width: 12),
|
||||
Image.asset(
|
||||
'assets/images/logo/logo_android_2.png',
|
||||
width: 40,
|
||||
width: 35,
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
'PiliPalaX',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
const SizedBox(width: 30),
|
||||
IconButton(
|
||||
iconSize: 40.0,
|
||||
padding: const EdgeInsets.all(8),
|
||||
// constraints: const BoxConstraints(),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap, // the '2023' part
|
||||
),
|
||||
tooltip: "${MineController.anonymity ? '退出' : '进入'}无痕模式",
|
||||
onPressed: () {
|
||||
MineController.onChangeAnonymity(context);
|
||||
@@ -122,12 +74,19 @@ class _MinePageState extends State<MinePage> {
|
||||
},
|
||||
icon: Icon(
|
||||
MineController.anonymity
|
||||
? CupertinoIcons.checkmark_shield
|
||||
: CupertinoIcons.shield_slash,
|
||||
size: 22,
|
||||
? MdiIcons.incognito
|
||||
: MdiIcons.incognitoOff,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 40.0,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap, // the '2023' part
|
||||
),
|
||||
//system -> dark -> light -> system
|
||||
tooltip:
|
||||
'切换至${mineController.themeType.value == ThemeType.system ? '深色' : (mineController.themeType.value == ThemeType.dark ? '浅色' : '跟随系统')}主题',
|
||||
@@ -135,104 +94,78 @@ class _MinePageState extends State<MinePage> {
|
||||
mineController.onChangeTheme();
|
||||
setState(() {});
|
||||
},
|
||||
icon: mineController.themeType.value == ThemeType.system
|
||||
? SizedBox(
|
||||
width: 22,
|
||||
height: 22,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
ClipPath(
|
||||
clipper: LeftClipper(),
|
||||
child: const Icon(
|
||||
CupertinoIcons.moon,
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
ClipPath(
|
||||
clipper: RightClipper(),
|
||||
child: const Icon(
|
||||
CupertinoIcons.sun_max,
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
CustomPaint(
|
||||
size: const Size(22, 22),
|
||||
painter: VerticalLinePainter(),
|
||||
),
|
||||
],
|
||||
))
|
||||
: Icon(
|
||||
mineController.themeType.value == ThemeType.dark
|
||||
? CupertinoIcons.moon
|
||||
: CupertinoIcons.sun_max,
|
||||
size: 22,
|
||||
icon: Icon(
|
||||
mineController.themeType.value == ThemeType.system
|
||||
? MdiIcons.themeLightDark
|
||||
: mineController.themeType.value == ThemeType.dark
|
||||
? MdiIcons.weatherSunny
|
||||
: MdiIcons.weatherNight,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
iconSize: 40.0,
|
||||
padding: const EdgeInsets.all(8),
|
||||
constraints: const BoxConstraints(),
|
||||
style: const ButtonStyle(
|
||||
tapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap, // the '2023' part
|
||||
),
|
||||
tooltip: '设置',
|
||||
onPressed: () => Get.toNamed('/setting', preventDuplicates: false),
|
||||
icon: const Icon(
|
||||
CupertinoIcons.gear,
|
||||
onPressed: () => {
|
||||
Get.back(),
|
||||
Get.toNamed('/setting', preventDuplicates: false),
|
||||
},
|
||||
icon: Icon(
|
||||
MdiIcons.cogs,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
body: LayoutBuilder(
|
||||
builder: (context, constraint) {
|
||||
return SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
child: SizedBox(
|
||||
height: constraint.maxHeight,
|
||||
child: Column(
|
||||
children: [
|
||||
]),
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
if (snapshot.data['status']) {
|
||||
return Obx(
|
||||
() => userInfoBuild(mineController, context));
|
||||
} else {
|
||||
if (snapshot.data == null || !snapshot.data['status']) {
|
||||
return userInfoBuild(mineController, context);
|
||||
}
|
||||
return Obx(() => userInfoBuild(mineController, context));
|
||||
} else {
|
||||
return userInfoBuild(mineController, context);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
Widget userInfoBuild(_mineController, context) {
|
||||
return Column(
|
||||
LevelInfo? levelInfo = _mineController.userInfo.value.levelInfo;
|
||||
TextStyle style = TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.titleMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.bold);
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 5),
|
||||
const SizedBox(width: 20),
|
||||
GestureDetector(
|
||||
onTap: () => _mineController.onLogin(),
|
||||
child: ClipOval(
|
||||
child: Container(
|
||||
width: 85,
|
||||
height: 85,
|
||||
width: 70,
|
||||
height: 70,
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
child: Center(
|
||||
child: _mineController.userInfo.value.face != null
|
||||
? NetworkImgLayer(
|
||||
src: _mineController.userInfo.value.face,
|
||||
semanticsLabel: '头像',
|
||||
width: 85,
|
||||
height: 85)
|
||||
width: 70,
|
||||
height: 70)
|
||||
: Image.asset(
|
||||
'assets/images/noface.jpeg',
|
||||
semanticLabel: "默认头像",
|
||||
@@ -241,9 +174,15 @@ class _MinePageState extends State<MinePage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 13),
|
||||
Row(
|
||||
const SizedBox(width: 16),
|
||||
IntrinsicWidth(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
_mineController.userInfo.value.uname ?? '点击头像登录',
|
||||
@@ -259,95 +198,75 @@ class _MinePageState extends State<MinePage> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text.rich(TextSpan(children: [
|
||||
TextSpan(
|
||||
text: '硬币: ',
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.outline)),
|
||||
TextSpan(
|
||||
text:
|
||||
(_mineController.userInfo.value.money ?? '-').toString(),
|
||||
style:
|
||||
TextStyle(color: Theme.of(context).colorScheme.primary)),
|
||||
]))
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
if (_mineController.userInfo.value.levelInfo != null) ...[
|
||||
LayoutBuilder(
|
||||
builder: (context, BoxConstraints box) {
|
||||
LevelInfo levelInfo = _mineController.userInfo.value.levelInfo;
|
||||
return SizedBox(
|
||||
width: box.maxWidth,
|
||||
height: 24,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
height: 24,
|
||||
constraints:
|
||||
const BoxConstraints(minWidth: 100), // 设置最小宽度为100
|
||||
width: box.maxWidth *
|
||||
(1 - (levelInfo.currentExp! / levelInfo.nextExp!)),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${levelInfo.currentExp!}/${levelInfo.nextExp!}',
|
||||
text: '硬币 ',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
fontSize: 12,
|
||||
),
|
||||
semanticsLabel:
|
||||
'当前经验${levelInfo.currentExp!},升级需要${levelInfo.nextExp!}',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 23,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
width: box.maxWidth *
|
||||
(_mineController
|
||||
.userInfo.value.levelInfo!.currentExp! /
|
||||
_mineController
|
||||
.userInfo.value.levelInfo!.nextExp!),
|
||||
height: 1,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline)),
|
||||
TextSpan(
|
||||
text: (_mineController.userInfo.value.money ?? '-')
|
||||
.toString(),
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary)),
|
||||
TextSpan(
|
||||
text: " 经验 ",
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline)),
|
||||
TextSpan(
|
||||
text: "${levelInfo?.currentExp ?? '-'}",
|
||||
semanticsLabel: "当前${levelInfo?.currentExp ?? '-'}",
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).colorScheme.primary)),
|
||||
TextSpan(
|
||||
text: "/${levelInfo?.nextExp ?? '-'}",
|
||||
semanticsLabel: "升级需${levelInfo?.nextExp ?? '-'}",
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize,
|
||||
color: Theme.of(context).colorScheme.outline)),
|
||||
])),
|
||||
// const SizedBox(height: 4),
|
||||
// Text.rich(TextSpan(children: [
|
||||
// ])),
|
||||
// Text.rich(
|
||||
// textAlign: TextAlign.right,
|
||||
// TextSpan(children: [
|
||||
//
|
||||
// ])),
|
||||
const SizedBox(height: 4),
|
||||
LinearProgressIndicator(
|
||||
minHeight: 2,
|
||||
value: levelInfo != null
|
||||
? (levelInfo.currentExp! / levelInfo.nextExp!)
|
||||
: 0,
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)),
|
||||
const SizedBox(width: 20),
|
||||
],
|
||||
const SizedBox(height: 26),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
TextStyle style = TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.titleMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.bold);
|
||||
return SizedBox(
|
||||
height: constraints.maxWidth * 0.33 * 0.6,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
width: 240,
|
||||
height: 100,
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1.66,
|
||||
children: <Widget>[
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => _mineController.pushDynamic(),
|
||||
borderRadius: StyleString.mdRadius,
|
||||
@@ -358,12 +277,10 @@ class _MinePageState extends State<MinePage> {
|
||||
duration: const Duration(milliseconds: 400),
|
||||
transitionBuilder:
|
||||
(Widget child, Animation<double> animation) {
|
||||
return ScaleTransition(
|
||||
scale: animation, child: child);
|
||||
return ScaleTransition(scale: animation, child: child);
|
||||
},
|
||||
child: Text(
|
||||
(_mineController.userStat.value.dynamicCount ??
|
||||
'-')
|
||||
(_mineController.userStat.value.dynamicCount ?? '-')
|
||||
.toString(),
|
||||
key: ValueKey<String>(_mineController
|
||||
.userStat.value.dynamicCount
|
||||
@@ -388,12 +305,10 @@ class _MinePageState extends State<MinePage> {
|
||||
duration: const Duration(milliseconds: 400),
|
||||
transitionBuilder:
|
||||
(Widget child, Animation<double> animation) {
|
||||
return ScaleTransition(
|
||||
scale: animation, child: child);
|
||||
return ScaleTransition(scale: animation, child: child);
|
||||
},
|
||||
child: Text(
|
||||
(_mineController.userStat.value.following ??
|
||||
'-')
|
||||
(_mineController.userStat.value.following ?? '-')
|
||||
.toString(),
|
||||
key: ValueKey<String>(_mineController
|
||||
.userStat.value.following
|
||||
@@ -412,14 +327,14 @@ class _MinePageState extends State<MinePage> {
|
||||
onTap: () => _mineController.pushFans(),
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 400),
|
||||
transitionBuilder:
|
||||
(Widget child, Animation<double> animation) {
|
||||
return ScaleTransition(
|
||||
scale: animation, child: child);
|
||||
return ScaleTransition(scale: animation, child: child);
|
||||
},
|
||||
child: Text(
|
||||
(_mineController.userStat.value.follower ?? '-')
|
||||
@@ -438,13 +353,8 @@ class _MinePageState extends State<MinePage> {
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,14 +69,14 @@ class _StyleSettingState extends State<StyleSetting> {
|
||||
children: [
|
||||
SetSwitchItem(
|
||||
title: '横屏适配',
|
||||
subTitle: '启用横屏布局与逻辑,适用于平板等设备',
|
||||
subTitle: '启用横屏布局与逻辑,适用于平板等设备;推荐全屏方向设为【不改变当前方向】',
|
||||
leading: const Icon(Icons.phonelink_outlined),
|
||||
setKey: SettingBoxKey.horizontalScreen,
|
||||
defaultVal: false,
|
||||
callFn: (value) {
|
||||
if (value) {
|
||||
autoScreen();
|
||||
SmartDialog.showToast('已开启横屏适配,推荐将全屏方式设为【不改变当前方向】');
|
||||
SmartDialog.showToast('已开启横屏适配');
|
||||
} else {
|
||||
AutoOrientation.portraitUpMode();
|
||||
SmartDialog.showToast('已关闭横屏适配');
|
||||
@@ -84,7 +84,7 @@ class _StyleSettingState extends State<StyleSetting> {
|
||||
}),
|
||||
const SetSwitchItem(
|
||||
title: '改用侧边栏',
|
||||
subTitle: '开启后底栏被替换,且底栏相关设置失效',
|
||||
subTitle: '开启后底栏与顶栏被替换,且相关设置失效',
|
||||
leading: Icon(Icons.chrome_reader_mode_outlined),
|
||||
setKey: SettingBoxKey.useSideBar,
|
||||
defaultVal: false,
|
||||
|
||||
12
pubspec.lock
12
pubspec.lock
@@ -575,10 +575,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_smart_dialog
|
||||
sha256: fe905154e101028e910e539ee5acb404f25cc36071ee824ebdcc7dd848c40700
|
||||
sha256: a74869ad9b20e5c413c45484267619fd31ee0b4d02b976a6af15f0150e1b911b
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||
source: hosted
|
||||
version: "4.9.7+2"
|
||||
version: "4.9.7+8"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -853,6 +853,14 @@ packages:
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
material_design_icons_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: material_design_icons_flutter
|
||||
sha256: "6f986b7a51f3ad4c00e33c5c84e8de1bdd140489bbcdc8b66fc1283dad4dea5a"
|
||||
url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/"
|
||||
source: hosted
|
||||
version: "7.0.7296"
|
||||
media_kit:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -78,8 +78,9 @@ dependencies:
|
||||
pull_to_refresh_notification: ^3.0.1
|
||||
# 图标
|
||||
font_awesome_flutter: ^10.4.0
|
||||
material_design_icons_flutter: ^7.0.7296
|
||||
# toast
|
||||
flutter_smart_dialog: ^4.9.4
|
||||
flutter_smart_dialog: ^4.9.7+8
|
||||
# 下滑关闭
|
||||
dismissible_page: ^1.0.2
|
||||
custom_sliding_segmented_control: ^1.7.5
|
||||
|
||||
Reference in New Issue
Block a user