💫 Animated collapsible sidebar
This commit is contained in:
parent
f834351ce2
commit
bc99865ba8
@ -383,4 +383,6 @@ const i18nEnglish = {
|
|||||||
'Since the App has entered the background, there may be a time difference between the message list and the server. Click to Refresh.',
|
'Since the App has entered the background, there may be a time difference between the message list and the server. Click to Refresh.',
|
||||||
'messageHistoryWipe': 'Wipe local message history',
|
'messageHistoryWipe': 'Wipe local message history',
|
||||||
'unknown': 'Unknown',
|
'unknown': 'Unknown',
|
||||||
|
'collapse': 'Collapse',
|
||||||
|
'expand': 'Expand',
|
||||||
};
|
};
|
||||||
|
@ -353,4 +353,6 @@ const i18nSimplifiedChinese = {
|
|||||||
'messageOutOfSyncCaption': '由于 App 进入后台,消息列表可能与服务器存在时差,点击刷新。',
|
'messageOutOfSyncCaption': '由于 App 进入后台,消息列表可能与服务器存在时差,点击刷新。',
|
||||||
'messageHistoryWipe': '清除消息记录',
|
'messageHistoryWipe': '清除消息记录',
|
||||||
'unknown': '未知',
|
'unknown': '未知',
|
||||||
|
'collapse': '折叠',
|
||||||
|
'expand': '展开',
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@ import 'package:solian/widgets/account/account_avatar.dart';
|
|||||||
import 'package:solian/widgets/account/account_status_action.dart';
|
import 'package:solian/widgets/account/account_status_action.dart';
|
||||||
import 'package:solian/widgets/navigation/app_navigation.dart';
|
import 'package:solian/widgets/navigation/app_navigation.dart';
|
||||||
import 'package:badges/badges.dart' as badges;
|
import 'package:badges/badges.dart' as badges;
|
||||||
import 'package:solian/widgets/navigation/app_navigation_regions.dart';
|
import 'package:solian/widgets/navigation/app_navigation_region.dart';
|
||||||
|
|
||||||
class AppNavigationDrawer extends StatefulWidget {
|
class AppNavigationDrawer extends StatefulWidget {
|
||||||
final String? routeName;
|
final String? routeName;
|
||||||
@ -24,8 +24,22 @@ class AppNavigationDrawer extends StatefulWidget {
|
|||||||
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
|
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
class _AppNavigationDrawerState extends State<AppNavigationDrawer>
|
||||||
bool _isCollapsed = true;
|
with TickerProviderStateMixin {
|
||||||
|
bool _isCollapsed = false;
|
||||||
|
|
||||||
|
late final AnimationController _drawerAnimationController =
|
||||||
|
AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
late final Animation<double> _drawerAnimation = Tween<double>(
|
||||||
|
begin: 80.0,
|
||||||
|
end: 304.0,
|
||||||
|
).animate(CurvedAnimation(
|
||||||
|
parent: _drawerAnimationController,
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
));
|
||||||
|
|
||||||
AccountStatus? _accountStatus;
|
AccountStatus? _accountStatus;
|
||||||
|
|
||||||
@ -42,13 +56,19 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Color get _unFocusColor =>
|
||||||
|
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
||||||
|
|
||||||
Widget _buildUserInfo() {
|
Widget _buildUserInfo() {
|
||||||
return Obx(() {
|
return Obx(() {
|
||||||
final AuthProvider auth = Get.find();
|
final AuthProvider auth = Get.find();
|
||||||
if (auth.isAuthorized.isFalse || auth.userProfile.value == null) {
|
if (auth.isAuthorized.isFalse || auth.userProfile.value == null) {
|
||||||
if (_isCollapsed) {
|
if (_isCollapsed) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
child: const Icon(Icons.account_circle).paddingAll(28),
|
child: const Icon(Icons.account_circle).paddingSymmetric(
|
||||||
|
horizontal: 28,
|
||||||
|
vertical: 20,
|
||||||
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
AppRouter.instance.goNamed('account');
|
AppRouter.instance.goNamed('account');
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
@ -70,9 +90,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
|
|
||||||
final leading = Obx(() {
|
final leading = Obx(() {
|
||||||
final statusBadgeColor = _accountStatus != null
|
final statusBadgeColor = _accountStatus != null
|
||||||
? StatusProvider.determineStatus(
|
? StatusProvider.determineStatus(_accountStatus!).$2
|
||||||
_accountStatus!,
|
|
||||||
).$2
|
|
||||||
: Colors.grey;
|
: Colors.grey;
|
||||||
|
|
||||||
final RelationshipProvider relations = Get.find();
|
final RelationshipProvider relations = Get.find();
|
||||||
@ -104,31 +122,43 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
child: !_isCollapsed
|
child: !_isCollapsed
|
||||||
? ListTile(
|
? Row(
|
||||||
contentPadding: const EdgeInsets.only(left: 20, right: 20),
|
children: [
|
||||||
title: Text(
|
leading,
|
||||||
auth.userProfile.value!['nick'],
|
Expanded(
|
||||||
maxLines: 1,
|
child: Column(
|
||||||
overflow: TextOverflow.fade,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
),
|
children: [
|
||||||
subtitle: Builder(
|
Text(
|
||||||
builder: (context) {
|
auth.userProfile.value!['nick'],
|
||||||
if (_accountStatus == null) {
|
maxLines: 1,
|
||||||
return Text('loading'.tr);
|
overflow: TextOverflow.fade,
|
||||||
}
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
final info = StatusProvider.determineStatus(
|
).paddingOnly(left: 16),
|
||||||
_accountStatus!,
|
Builder(
|
||||||
);
|
builder: (context) {
|
||||||
return Text(
|
if (_accountStatus == null) {
|
||||||
info.$3,
|
return Text('loading'.tr).paddingOnly(left: 16);
|
||||||
maxLines: 1,
|
}
|
||||||
overflow: TextOverflow.fade,
|
final info = StatusProvider.determineStatus(
|
||||||
);
|
_accountStatus!,
|
||||||
},
|
);
|
||||||
),
|
return Text(
|
||||||
leading: leading,
|
info.$3,
|
||||||
)
|
maxLines: 1,
|
||||||
: leading.paddingAll(20),
|
overflow: TextOverflow.fade,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _unFocusColor,
|
||||||
|
),
|
||||||
|
).paddingOnly(left: 16);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 20, vertical: 16)
|
||||||
|
: leading.paddingSymmetric(horizontal: 20, vertical: 16),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
AppRouter.instance.goNamed('account');
|
AppRouter.instance.goNamed('account');
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
@ -148,22 +178,59 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _expandDrawer() {
|
||||||
|
_drawerAnimationController.animateTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _collapseDrawer() {
|
||||||
|
_drawerAnimationController.animateTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
void _closeDrawer() {
|
void _closeDrawer() {
|
||||||
|
_autoResize();
|
||||||
rootScaffoldKey.currentState!.closeDrawer();
|
rootScaffoldKey.currentState!.closeDrawer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _autoResize() {
|
||||||
|
if (SolianTheme.isExtraLargeScreen(context)) {
|
||||||
|
_expandDrawer();
|
||||||
|
} else if (SolianTheme.isLargeScreen(context)) {
|
||||||
|
_collapseDrawer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_getStatus();
|
_getStatus();
|
||||||
|
Future.delayed(Duration.zero, () => _autoResize());
|
||||||
|
_drawerAnimationController.addListener(() {
|
||||||
|
if (_drawerAnimation.value > 180 && _isCollapsed) {
|
||||||
|
setState(() => _isCollapsed = false);
|
||||||
|
} else if (_drawerAnimation.value < 180 && !_isCollapsed) {
|
||||||
|
setState(() => _isCollapsed = true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_drawerAnimationController.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Drawer(
|
return AnimatedBuilder(
|
||||||
width: _isCollapsed ? 80 : null,
|
animation: _drawerAnimation,
|
||||||
backgroundColor:
|
builder: (context, child) {
|
||||||
SolianTheme.isLargeScreen(context) ? Colors.transparent : null,
|
return Drawer(
|
||||||
|
width: _drawerAnimation.value,
|
||||||
|
backgroundColor:
|
||||||
|
SolianTheme.isLargeScreen(context) ? Colors.transparent : null,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
bottom: false,
|
bottom: false,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -175,7 +242,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
.map(
|
.map(
|
||||||
(e) => _isCollapsed
|
(e) => _isCollapsed
|
||||||
? InkWell(
|
? InkWell(
|
||||||
child: Icon(e.icon, size: 22).paddingSymmetric(
|
child: Icon(e.icon, size: 20).paddingSymmetric(
|
||||||
horizontal: 28,
|
horizontal: 28,
|
||||||
vertical: 16,
|
vertical: 16,
|
||||||
),
|
),
|
||||||
@ -201,7 +268,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
),
|
),
|
||||||
const Divider(thickness: 0.3, height: 1),
|
const Divider(thickness: 0.3, height: 1),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: AppNavigationRegions(
|
child: AppNavigationRegion(
|
||||||
isCollapsed: _isCollapsed,
|
isCollapsed: _isCollapsed,
|
||||||
onSelected: (item) {
|
onSelected: (item) {
|
||||||
_closeDrawer();
|
_closeDrawer();
|
||||||
@ -211,31 +278,57 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
|||||||
const Divider(thickness: 0.3, height: 1),
|
const Divider(thickness: 0.3, height: 1),
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
_isCollapsed
|
if (_isCollapsed)
|
||||||
? InkWell(
|
InkWell(
|
||||||
child: const Icon(Icons.settings, size: 22)
|
child: const Icon(
|
||||||
.paddingSymmetric(
|
Icons.settings,
|
||||||
horizontal: 28,
|
size: 20,
|
||||||
vertical: 16,
|
).paddingSymmetric(
|
||||||
),
|
horizontal: 28,
|
||||||
onTap: () {
|
vertical: 10,
|
||||||
AppRouter.instance.pushNamed('settings');
|
),
|
||||||
_closeDrawer();
|
onTap: () {
|
||||||
},
|
AppRouter.instance.pushNamed('settings');
|
||||||
)
|
_closeDrawer();
|
||||||
: ListTile(
|
},
|
||||||
minTileHeight: 0,
|
)
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
else
|
||||||
horizontal: 20,
|
ListTile(
|
||||||
),
|
minTileHeight: 0,
|
||||||
leading:
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
const Icon(Icons.settings, size: 20).paddingAll(2),
|
horizontal: 20,
|
||||||
title: Text('settings'.tr),
|
),
|
||||||
onTap: () {
|
leading: const Icon(Icons.settings, size: 20).paddingAll(2),
|
||||||
AppRouter.instance.pushNamed('settings');
|
title: Text('settings'.tr),
|
||||||
_closeDrawer();
|
onTap: () {
|
||||||
},
|
AppRouter.instance.pushNamed('settings');
|
||||||
),
|
_closeDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (_isCollapsed)
|
||||||
|
InkWell(
|
||||||
|
child: const Icon(Icons.chevron_right, size: 20)
|
||||||
|
.paddingSymmetric(
|
||||||
|
horizontal: 28,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_expandDrawer();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 0,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
),
|
||||||
|
leading:
|
||||||
|
const Icon(Icons.chevron_left, size: 20).paddingAll(2),
|
||||||
|
title: Text('collapse'.tr),
|
||||||
|
onTap: () {
|
||||||
|
_collapseDrawer();
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
).paddingOnly(
|
).paddingOnly(
|
||||||
top: 8,
|
top: 8,
|
||||||
|
@ -5,11 +5,11 @@ import 'package:solian/providers/content/channel.dart';
|
|||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
class AppNavigationRegions extends StatelessWidget {
|
class AppNavigationRegion extends StatelessWidget {
|
||||||
final bool isCollapsed;
|
final bool isCollapsed;
|
||||||
final Function(Channel item) onSelected;
|
final Function(Channel item) onSelected;
|
||||||
|
|
||||||
const AppNavigationRegions({
|
const AppNavigationRegion({
|
||||||
super.key,
|
super.key,
|
||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
this.isCollapsed = false,
|
this.isCollapsed = false,
|
||||||
@ -32,7 +32,7 @@ class AppNavigationRegions extends StatelessWidget {
|
|||||||
|
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
child: const Icon(Icons.tag_outlined).paddingSymmetric(
|
child: const Icon(Icons.tag_outlined, size: 20).paddingSymmetric(
|
||||||
horizontal: 20,
|
horizontal: 20,
|
||||||
vertical: 16,
|
vertical: 16,
|
||||||
),
|
),
|
@ -2,7 +2,7 @@ name: solian
|
|||||||
description: "The Solar Network App"
|
description: "The Solar Network App"
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 1.2.1+20
|
version: 1.2.1+21
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.4 <4.0.0"
|
sdk: ">=3.3.4 <4.0.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user