@@ -14,8 +14,11 @@ import 'models/app_state.dart';
1414import 'models/settings_service.dart' ;
1515import 'routes/app_routes.dart' ;
1616import 'services/auth_state.dart' ;
17+ import 'services/server_connection_status_service.dart' ;
1718import 'utils/talker.dart' ;
1819import 'widgets/app_alert_dialog.dart' ;
20+ import 'widgets/custom_title_bar.dart' ;
21+ import 'widgets/server_connection_banner.dart' ;
1922
2023void main () async {
2124 WidgetsFlutterBinding .ensureInitialized ();
@@ -397,6 +400,75 @@ class _TouchFishAppState extends State<TouchFishApp> {
397400 );
398401 }
399402
403+ Widget _buildServerConnectionOverlay (BuildContext context, Widget child) {
404+ final hasDesktopWindowFrame =
405+ ! kIsWeb && (Platform .isWindows || Platform .isMacOS || Platform .isLinux);
406+
407+ return ListenableBuilder (
408+ listenable: ServerConnectionStatusService .instance,
409+ builder: (context, _) {
410+ final service = ServerConnectionStatusService .instance;
411+
412+ return Stack (
413+ children: [
414+ child,
415+ SafeArea (
416+ child: Align (
417+ alignment: Alignment .topCenter,
418+ child: Padding (
419+ padding: EdgeInsets .fromLTRB (
420+ 16 ,
421+ (hasDesktopWindowFrame ? CustomTitleBar .height : 0 ) + 12 ,
422+ 16 ,
423+ 12 ,
424+ ),
425+ child: AnimatedSwitcher (
426+ duration: const Duration (milliseconds: 220 ),
427+ switchInCurve: Curves .easeOutCubic,
428+ switchOutCurve: Curves .easeInCubic,
429+ transitionBuilder: (child, animation) {
430+ final curved = CurvedAnimation (
431+ parent: animation,
432+ curve: Curves .easeOutCubic,
433+ reverseCurve: Curves .easeInCubic,
434+ );
435+ return FadeTransition (
436+ opacity: curved,
437+ child: SlideTransition (
438+ position: Tween <Offset >(
439+ begin: const Offset (0 , - 0.2 ),
440+ end: Offset .zero,
441+ ).animate (curved),
442+ child: child,
443+ ),
444+ );
445+ },
446+ child: service.isVisible
447+ ? ServerConnectionBanner (
448+ key: ValueKey (service.phase),
449+ phase: service.phase,
450+ onTap:
451+ service.phase ==
452+ ServerConnectionBannerPhase .disconnected
453+ ? () {
454+ unawaited (
455+ ServerConnectionStatusService .instance
456+ .retryConnection (),
457+ );
458+ }
459+ : null ,
460+ )
461+ : const SizedBox .shrink (key: ValueKey ('hidden' )),
462+ ),
463+ ),
464+ ),
465+ ),
466+ ],
467+ );
468+ },
469+ );
470+ }
471+
400472 @override
401473 Widget build (BuildContext context) {
402474 return ListenableBuilder (
@@ -465,7 +537,10 @@ class _TouchFishAppState extends State<TouchFishApp> {
465537 themeMode: _appState.themeMode,
466538 builder: (context, child) {
467539 _showStartupResetNoticeIfNeeded (context);
468- final content = child ?? const SizedBox .shrink ();
540+ final content = _buildServerConnectionOverlay (
541+ context,
542+ child ?? const SizedBox .shrink (),
543+ );
469544 if (hasBackgroundImage && ! kIsWeb) {
470545 return _buildSavedSessionRestoreOverlay (
471546 context,
0 commit comments