💫 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.',
|
||||
'messageHistoryWipe': 'Wipe local message history',
|
||||
'unknown': 'Unknown',
|
||||
'collapse': 'Collapse',
|
||||
'expand': 'Expand',
|
||||
};
|
||||
|
@ -353,4 +353,6 @@ const i18nSimplifiedChinese = {
|
||||
'messageOutOfSyncCaption': '由于 App 进入后台,消息列表可能与服务器存在时差,点击刷新。',
|
||||
'messageHistoryWipe': '清除消息记录',
|
||||
'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/navigation/app_navigation.dart';
|
||||
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 {
|
||||
final String? routeName;
|
||||
@ -24,8 +24,22 @@ class AppNavigationDrawer extends StatefulWidget {
|
||||
State<AppNavigationDrawer> createState() => _AppNavigationDrawerState();
|
||||
}
|
||||
|
||||
class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
bool _isCollapsed = true;
|
||||
class _AppNavigationDrawerState extends State<AppNavigationDrawer>
|
||||
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;
|
||||
|
||||
@ -42,13 +56,19 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
}
|
||||
}
|
||||
|
||||
Color get _unFocusColor =>
|
||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.75);
|
||||
|
||||
Widget _buildUserInfo() {
|
||||
return Obx(() {
|
||||
final AuthProvider auth = Get.find();
|
||||
if (auth.isAuthorized.isFalse || auth.userProfile.value == null) {
|
||||
if (_isCollapsed) {
|
||||
return InkWell(
|
||||
child: const Icon(Icons.account_circle).paddingAll(28),
|
||||
child: const Icon(Icons.account_circle).paddingSymmetric(
|
||||
horizontal: 28,
|
||||
vertical: 20,
|
||||
),
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed('account');
|
||||
_closeDrawer();
|
||||
@ -70,9 +90,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
|
||||
final leading = Obx(() {
|
||||
final statusBadgeColor = _accountStatus != null
|
||||
? StatusProvider.determineStatus(
|
||||
_accountStatus!,
|
||||
).$2
|
||||
? StatusProvider.determineStatus(_accountStatus!).$2
|
||||
: Colors.grey;
|
||||
|
||||
final RelationshipProvider relations = Get.find();
|
||||
@ -104,17 +122,23 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
|
||||
return InkWell(
|
||||
child: !_isCollapsed
|
||||
? ListTile(
|
||||
contentPadding: const EdgeInsets.only(left: 20, right: 20),
|
||||
title: Text(
|
||||
? Row(
|
||||
children: [
|
||||
leading,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
auth.userProfile.value!['nick'],
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
subtitle: Builder(
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
).paddingOnly(left: 16),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
if (_accountStatus == null) {
|
||||
return Text('loading'.tr);
|
||||
return Text('loading'.tr).paddingOnly(left: 16);
|
||||
}
|
||||
final info = StatusProvider.determineStatus(
|
||||
_accountStatus!,
|
||||
@ -123,12 +147,18 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
info.$3,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.fade,
|
||||
);
|
||||
style: TextStyle(
|
||||
color: _unFocusColor,
|
||||
),
|
||||
).paddingOnly(left: 16);
|
||||
},
|
||||
),
|
||||
leading: leading,
|
||||
)
|
||||
: leading.paddingAll(20),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
).paddingSymmetric(horizontal: 20, vertical: 16)
|
||||
: leading.paddingSymmetric(horizontal: 20, vertical: 16),
|
||||
onTap: () {
|
||||
AppRouter.instance.goNamed('account');
|
||||
_closeDrawer();
|
||||
@ -148,22 +178,59 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
});
|
||||
}
|
||||
|
||||
void _expandDrawer() {
|
||||
_drawerAnimationController.animateTo(1);
|
||||
}
|
||||
|
||||
void _collapseDrawer() {
|
||||
_drawerAnimationController.animateTo(0);
|
||||
}
|
||||
|
||||
void _closeDrawer() {
|
||||
_autoResize();
|
||||
rootScaffoldKey.currentState!.closeDrawer();
|
||||
}
|
||||
|
||||
void _autoResize() {
|
||||
if (SolianTheme.isExtraLargeScreen(context)) {
|
||||
_expandDrawer();
|
||||
} else if (SolianTheme.isLargeScreen(context)) {
|
||||
_collapseDrawer();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_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
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _drawerAnimation,
|
||||
builder: (context, child) {
|
||||
return Drawer(
|
||||
width: _isCollapsed ? 80 : null,
|
||||
width: _drawerAnimation.value,
|
||||
backgroundColor:
|
||||
SolianTheme.isLargeScreen(context) ? Colors.transparent : null,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
@ -175,7 +242,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
.map(
|
||||
(e) => _isCollapsed
|
||||
? InkWell(
|
||||
child: Icon(e.icon, size: 22).paddingSymmetric(
|
||||
child: Icon(e.icon, size: 20).paddingSymmetric(
|
||||
horizontal: 28,
|
||||
vertical: 16,
|
||||
),
|
||||
@ -201,7 +268,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
),
|
||||
const Divider(thickness: 0.3, height: 1),
|
||||
Expanded(
|
||||
child: AppNavigationRegions(
|
||||
child: AppNavigationRegion(
|
||||
isCollapsed: _isCollapsed,
|
||||
onSelected: (item) {
|
||||
_closeDrawer();
|
||||
@ -211,29 +278,55 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
|
||||
const Divider(thickness: 0.3, height: 1),
|
||||
Column(
|
||||
children: [
|
||||
_isCollapsed
|
||||
? InkWell(
|
||||
child: const Icon(Icons.settings, size: 22)
|
||||
.paddingSymmetric(
|
||||
if (_isCollapsed)
|
||||
InkWell(
|
||||
child: const Icon(
|
||||
Icons.settings,
|
||||
size: 20,
|
||||
).paddingSymmetric(
|
||||
horizontal: 28,
|
||||
vertical: 16,
|
||||
vertical: 10,
|
||||
),
|
||||
onTap: () {
|
||||
AppRouter.instance.pushNamed('settings');
|
||||
_closeDrawer();
|
||||
},
|
||||
)
|
||||
: ListTile(
|
||||
else
|
||||
ListTile(
|
||||
minTileHeight: 0,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
),
|
||||
leading: const Icon(Icons.settings, size: 20).paddingAll(2),
|
||||
title: Text('settings'.tr),
|
||||
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.settings, size: 20).paddingAll(2),
|
||||
title: Text('settings'.tr),
|
||||
const Icon(Icons.chevron_left, size: 20).paddingAll(2),
|
||||
title: Text('collapse'.tr),
|
||||
onTap: () {
|
||||
AppRouter.instance.pushNamed('settings');
|
||||
_closeDrawer();
|
||||
_collapseDrawer();
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -5,11 +5,11 @@ import 'package:solian/providers/content/channel.dart';
|
||||
import 'package:solian/router.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class AppNavigationRegions extends StatelessWidget {
|
||||
class AppNavigationRegion extends StatelessWidget {
|
||||
final bool isCollapsed;
|
||||
final Function(Channel item) onSelected;
|
||||
|
||||
const AppNavigationRegions({
|
||||
const AppNavigationRegion({
|
||||
super.key,
|
||||
required this.onSelected,
|
||||
this.isCollapsed = false,
|
||||
@ -32,7 +32,7 @@ class AppNavigationRegions extends StatelessWidget {
|
||||
|
||||
if (isCollapsed) {
|
||||
return InkWell(
|
||||
child: const Icon(Icons.tag_outlined).paddingSymmetric(
|
||||
child: const Icon(Icons.tag_outlined, size: 20).paddingSymmetric(
|
||||
horizontal: 20,
|
||||
vertical: 16,
|
||||
),
|
@ -2,7 +2,7 @@ name: solian
|
||||
description: "The Solar Network App"
|
||||
publish_to: "none"
|
||||
|
||||
version: 1.2.1+20
|
||||
version: 1.2.1+21
|
||||
|
||||
environment:
|
||||
sdk: ">=3.3.4 <4.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user