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