diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 958e2e9c..6f6c2330 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -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 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(), + )); + } } diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 69073be7..60ab113a 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -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 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 // } return Scaffold( extendBody: true, - extendBodyBehindAppBar: true, + // extendBodyBehindAppBar: true, backgroundColor: Colors.transparent, appBar: AppBar( toolbarHeight: 0, @@ -72,13 +59,13 @@ class _HomePageState extends State ), body: Column( children: [ - CustomAppBar( - stream: _homeController.hideSearchBar - ? stream - : StreamController.broadcast().stream, - ctr: _homeController, - callback: showUserBottomSheet, - ), + if (!_homeController.useSideBar) + CustomAppBar( + stream: _homeController.hideSearchBar + ? stream + : StreamController.broadcast().stream, + ctr: _homeController, + ), if (_homeController.tabs.length > 1) ...[ if (_homeController.enableGradientBg) ...[ const CustomTabs(), @@ -128,15 +115,13 @@ class _HomePageState extends State class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { final double height; final Stream? 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, - ctr: ctr, - userLogin: isUserLoggedIn, - userFace: ctr?.userFace.value, - callback: () => callback!(), - ), + height: snapshot.data ? 52 : 0, + padding: const EdgeInsets.fromLTRB(14, 6, 14, 0), + child: SearchBarAndUser( + ctr: ctr, ), ), ); @@ -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( - (Set 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((Set states) { + return colorTheme.secondaryContainer.withOpacity(0.6); + }), + padding: const EdgeInsets.fromLTRB(6, 1, 6, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), selected: selected, diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index ad7582f3..9cdafcbf 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -130,16 +130,16 @@ class _MainAppState extends State 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 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 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 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 with SingleTickerProviderStateMixin { children: _mainController.pages, ), ), + if (useSideBar) SizedBox(width: context.width * 0.004), ], ) ]), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index a34c1490..60485a2e 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -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: [ - const Row( + Row( children: [ 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), ], ))); }); diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index b2195e39..0a2c7f24 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -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 { - @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 oldClipper) { - return false; - } -} - -class RightClipper extends CustomClipper { - @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 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,370 +28,333 @@ class _MinePageState extends State { mineController.userLogin.listen((status) { if (mounted) { - setState(() { - _futureBuilderFuture = mineController.queryUserInfo(); - }); + _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: [ - IconButton( - tooltip: "${MineController.anonymity ? '退出' : '进入'}无痕模式", - onPressed: () { - MineController.onChangeAnonymity(context); - setState(() {}); - }, - icon: Icon( - MineController.anonymity - ? CupertinoIcons.checkmark_shield - : CupertinoIcons.shield_slash, - size: 22, - ), - ), - IconButton( - //system -> dark -> light -> system - tooltip: - '切换至${mineController.themeType.value == ThemeType.system ? '深色' : (mineController.themeType.value == ThemeType.dark ? '浅色' : '跟随系统')}主题', - onPressed: () { - 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, - ), - ), - IconButton( - tooltip: '设置', - onPressed: () => Get.toNamed('/setting', preventDuplicates: false), - icon: const Icon( - CupertinoIcons.gear, - ), - ), - 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 { - return userInfoBuild(mineController, context); - } - } else { - return userInfoBuild(mineController, context); - } - }, - ), - ], + 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); + setState(() {}); + }, + icon: Icon( + MineController.anonymity + ? 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 ? '浅色' : '跟随系统')}主题', + onPressed: () { + mineController.onChangeTheme(); + setState(() {}); + }, + 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.back(), + Get.toNamed('/setting', preventDuplicates: false), + }, + icon: Icon( + MdiIcons.cogs, + size: 24, + ), + ), + const SizedBox(width: 10), + ]), + const SizedBox(height: 10), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + 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( - children: [ - const SizedBox(height: 5), - GestureDetector( - onTap: () => _mineController.onLogin(), - child: ClipOval( - child: Container( - width: 85, - height: 85, - 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) - : Image.asset( - 'assets/images/noface.jpeg', - semanticLabel: "默认头像", - ), + 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(width: 20), + GestureDetector( + onTap: () => _mineController.onLogin(), + child: ClipOval( + child: Container( + 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: 70, + height: 70) + : Image.asset( + 'assets/images/noface.jpeg', + semanticLabel: "默认头像", + ), + ), ), ), ), - ), - const SizedBox(height: 13), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - _mineController.userInfo.value.uname ?? '点击头像登录', - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox(width: 4), - Image.asset( - 'assets/images/lv/lv${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}.png', - height: 10, - semanticLabel: - '等级:${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}', - ), - ], - ), - 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!}', - 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, - ), - ), - ), - ], - ), - ); - }, - ), + 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 ?? '点击头像登录', + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(width: 4), + Image.asset( + 'assets/images/lv/lv${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}.png', + height: 10, + semanticLabel: + '等级:${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}', + ), + ], + ), + const SizedBox(height: 8), + Text.rich(TextSpan(children: [ + TextSpan( + text: '硬币 ', + style: TextStyle( + 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( + 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, - child: GridView.count( - primary: false, - padding: const EdgeInsets.all(0), - crossAxisCount: 3, - childAspectRatio: 1.66, - children: [ - InkWell( - onTap: () => _mineController.pushDynamic(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - (_mineController.userStat.value.dynamicCount ?? - '-') - .toString(), - key: ValueKey(_mineController - .userStat.value.dynamicCount - .toString()), - style: style), - ), - const SizedBox(height: 8), - Text( - '动态', - style: Theme.of(context).textTheme.labelMedium, - ), - ], - ), + ), + const SizedBox(height: 10), + Container( + width: 240, + height: 100, + child: GridView.count( + padding: EdgeInsets.zero, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: 3, + children: [ + InkWell( + onTap: () => _mineController.pushDynamic(), + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + (_mineController.userStat.value.dynamicCount ?? '-') + .toString(), + key: ValueKey(_mineController + .userStat.value.dynamicCount + .toString()), + style: style), ), - InkWell( - onTap: () => _mineController.pushFollow(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - (_mineController.userStat.value.following ?? - '-') - .toString(), - key: ValueKey(_mineController - .userStat.value.following - .toString()), - style: style), - ), - const SizedBox(height: 8), - Text( - '关注', - style: Theme.of(context).textTheme.labelMedium, - ), - ], - ), - ), - InkWell( - onTap: () => _mineController.pushFans(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - (_mineController.userStat.value.follower ?? '-') - .toString(), - key: ValueKey(_mineController - .userStat.value.follower - .toString()), - style: style), - ), - const SizedBox(height: 8), - Text( - '粉丝', - style: Theme.of(context).textTheme.labelMedium, - ), - ], - ), + const SizedBox(height: 8), + Text( + '动态', + style: Theme.of(context).textTheme.labelMedium, ), ], ), - ); - }, - ), - ), - ], - ); + ), + InkWell( + onTap: () => _mineController.pushFollow(), + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + (_mineController.userStat.value.following ?? '-') + .toString(), + key: ValueKey(_mineController + .userStat.value.following + .toString()), + style: style), + ), + const SizedBox(height: 8), + Text( + '关注', + style: Theme.of(context).textTheme.labelMedium, + ), + ], + ), + ), + InkWell( + 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 animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: Text( + (_mineController.userStat.value.follower ?? '-') + .toString(), + key: ValueKey(_mineController + .userStat.value.follower + .toString()), + style: style), + ), + const SizedBox(height: 8), + Text( + '粉丝', + style: Theme.of(context).textTheme.labelMedium, + ), + ], + ), + ), + ], + )), + ]); } } diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 35f94bbd..a716ae9f 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -69,14 +69,14 @@ class _StyleSettingState extends State { 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 { }), const SetSwitchItem( title: '改用侧边栏', - subTitle: '开启后底栏被替换,且底栏相关设置失效', + subTitle: '开启后底栏与顶栏被替换,且相关设置失效', leading: Icon(Icons.chrome_reader_mode_outlined), setKey: SettingBoxKey.useSideBar, defaultVal: false, diff --git a/pubspec.lock b/pubspec.lock index 6a8a33c3..2b224dcd 100644 --- a/pubspec.lock +++ b/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: diff --git a/pubspec.yaml b/pubspec.yaml index 66767a8b..1542130f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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