💄 Move the connection indicator

This commit is contained in:
LittleSheep 2025-01-31 21:50:18 +08:00
parent b7b921f1f4
commit b8dcdb2315
5 changed files with 84 additions and 67 deletions

View File

@ -4,6 +4,7 @@ import 'package:gap/gap.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/config.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/providers/websocket.dart'; import 'package:surface/providers/websocket.dart';
@ -13,6 +14,9 @@ class ConnectionIndicator extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ws = context.watch<WebSocketProvider>(); final ws = context.watch<WebSocketProvider>();
final cfg = context.watch<ConfigProvider>();
final marginLeft = cfg.drawerIsCollapsed ? 0.0 : cfg.drawerIsExpanded ? 304.0 : 80.0;
return ListenableBuilder( return ListenableBuilder(
listenable: ws, listenable: ws,
@ -22,45 +26,50 @@ class ConnectionIndicator extends StatelessWidget {
return IgnorePointer( return IgnorePointer(
ignoring: !show, ignoring: !show,
child: GestureDetector( child: Center(
child: Material( child: GestureDetector(
elevation: 2, child: Material(
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), elevation: 2,
color: Theme.of(context).colorScheme.secondaryContainer, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
child: ua.isAuthorized color: Theme.of(context).colorScheme.secondaryContainer,
? Row( child: ua.isAuthorized
mainAxisAlignment: MainAxisAlignment.center, ? Row(
crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min,
children: [ mainAxisAlignment: MainAxisAlignment.center,
if (ws.isBusy) crossAxisAlignment: CrossAxisAlignment.center,
Text('serverConnecting').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer) children: [
else if (!ws.isConnected) if (ws.isBusy)
Text('serverDisconnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer) Text('serverConnecting').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer)
else else if (!ws.isConnected)
Text('serverConnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer), Text('serverDisconnected')
const Gap(8), .tr()
if (ws.isBusy) .textColor(Theme.of(context).colorScheme.onSecondaryContainer)
const CircularProgressIndicator(strokeWidth: 2.5) else
.width(12) Text('serverConnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer),
.height(12) const Gap(8),
.padding(horizontal: 4, right: 4) if (ws.isBusy)
else if (!ws.isConnected) const CircularProgressIndicator(strokeWidth: 2.5)
const Icon(Symbols.power_off, size: 18) .width(12)
else .height(12)
const Icon(Symbols.power, size: 18), .padding(horizontal: 4, right: 4)
], else if (!ws.isConnected)
).padding(horizontal: 8, vertical: 4) const Icon(Symbols.power_off, size: 18)
: const SizedBox.shrink(), else
).opacity(show ? 1 : 0, animate: true).animate( const Icon(Symbols.power, size: 18),
const Duration(milliseconds: 300), ],
Curves.easeInOut, ).padding(horizontal: 8, vertical: 4)
), : const SizedBox.shrink(),
onTap: () { ).opacity(show ? 1 : 0, animate: true).animate(
if (!ws.isConnected && !ws.isBusy) { const Duration(milliseconds: 300),
ws.connect(); Curves.easeInOut,
} ),
}, onTap: () {
), if (!ws.isConnected && !ws.isBusy) {
ws.connect();
}
},
),
).padding(left: marginLeft),
); );
}, },
); );

View File

@ -28,7 +28,7 @@ class ContextMenuArea extends StatelessWidget {
// Leave padding for side navigation // Leave padding for side navigation
mousePosition = cfg.drawerIsExpanded mousePosition = cfg.drawerIsExpanded
? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2) ? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2)
: mousePosition.copyWith(dx: mousePosition.dx - 72 * 2); : mousePosition.copyWith(dx: mousePosition.dx - 80 * 2);
} }
}, },
child: GestureDetector( child: GestureDetector(

View File

@ -31,34 +31,37 @@ class _AppRailNavigationState extends State<AppRailNavigation> {
builder: (context, _) { builder: (context, _) {
final destinations = nav.destinations.where((ele) => ele.isPinned).toList(); final destinations = nav.destinations.where((ele) => ele.isPinned).toList();
return NavigationRail( return SizedBox(
selectedIndex: width: 80,
nav.currentIndex != null && nav.currentIndex! < nav.pinnedDestinationCount ? nav.currentIndex : null, child: NavigationRail(
destinations: [ selectedIndex:
...destinations.where((ele) => ele.isPinned).map((ele) { nav.currentIndex != null && nav.currentIndex! < nav.pinnedDestinationCount ? nav.currentIndex : null,
return NavigationRailDestination( destinations: [
icon: ele.icon, ...destinations.where((ele) => ele.isPinned).map((ele) {
label: Text(ele.label).tr(), return NavigationRailDestination(
); icon: ele.icon,
}), label: Text(ele.label).tr(),
], );
trailing: Expanded( }),
child: Align( ],
alignment: Alignment.bottomCenter, trailing: Expanded(
child: StyledWidget( child: Align(
IconButton( alignment: Alignment.bottomCenter,
icon: const Icon(Symbols.menu), child: StyledWidget(
onPressed: () { IconButton(
Scaffold.of(context).openDrawer(); icon: const Icon(Symbols.menu),
}, onPressed: () {
), Scaffold.of(context).openDrawer();
).padding(bottom: 16), },
),
).padding(bottom: 16),
),
), ),
onDestinationSelected: (idx) {
nav.setIndex(idx);
GoRouter.of(context).goNamed(destinations[idx].screen);
},
), ),
onDestinationSelected: (idx) {
nav.setIndex(idx);
GoRouter.of(context).goNamed(destinations[idx].screen);
},
); );
}, },
); );

View File

@ -140,6 +140,7 @@ class AppRootScaffold extends StatelessWidget {
); );
final safeTop = MediaQuery.of(context).padding.top; final safeTop = MediaQuery.of(context).padding.top;
final safeBottom = MediaQuery.of(context).padding.bottom;
return Scaffold( return Scaffold(
key: globalRootScaffoldKey, key: globalRootScaffoldKey,
@ -191,7 +192,10 @@ class AppRootScaffold extends StatelessWidget {
], ],
), ),
Positioned(top: safeTop > 0 ? safeTop : 16, right: 8, child: NotifyIndicator()), Positioned(top: safeTop > 0 ? safeTop : 16, right: 8, child: NotifyIndicator()),
Positioned(top: safeTop > 0 ? safeTop : 16, left: 8, child: ConnectionIndicator()), if (ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE))
Positioned(bottom: safeBottom > 0 ? safeBottom : 16, left: 0, right: 0, child: ConnectionIndicator())
else
Positioned(top: safeTop > 0 ? safeTop : 16, left: 0, right: 0, child: ConnectionIndicator()),
], ],
), ),
drawer: !isExpandedDrawer ? AppNavigationDrawer() : null, drawer: !isExpandedDrawer ? AppNavigationDrawer() : null,

View File

@ -84,6 +84,7 @@ class _NotifyIndicatorState extends State<NotifyIndicator> with SingleTickerProv
ignoring: !show, ignoring: !show,
child: GestureDetector( child: GestureDetector(
child: Animate( child: Animate(
autoPlay: false,
controller: _animationController, controller: _animationController,
effects: [ effects: [
SlideEffect( SlideEffect(