feat: 首页顶栏收入侧栏,线性宽度、渐变底色扩大,“我的”页面重构为弹窗,统一替换图标

This commit is contained in:
orz12
2024-07-11 17:44:03 +08:00
parent fce011fc72
commit e9e6601e39
8 changed files with 471 additions and 494 deletions

View File

@@ -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(),
));
}
}

View File

@@ -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,

View File

@@ -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),
],
)
]),

View File

@@ -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),
],
)));
});

View File

@@ -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> {
),
),
],
),
);
},
),
),
],
);
)),
]);
}
}

View File

@@ -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,

View File

@@ -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:

View File

@@ -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