Files
votianlt/app/lib/widgets/offline_banner.dart

169 lines
4.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:votianlt_app/services/developer.dart' as developer;
import 'package:votianlt_app/services/websocket_service.dart';
import 'package:votianlt_app/services/dart_mq.dart';
class OfflineBanner extends StatefulWidget {
const OfflineBanner({super.key});
@override
State<OfflineBanner> createState() => _OfflineBannerState();
}
class _OfflineBannerState extends State<OfflineBanner> {
final StompService _stompService = StompService();
DartMQSubscription? _connSub;
Timer? _countdownTimer;
int _secondsToRetry = 15;
bool _hadConnection = false; // Track if we ever had a successful connection
@override
void initState() {
super.initState();
// Check if we're already connected (e.g., coming back to this screen)
_hadConnection = _stompService.isConnected && _stompService.isAuthenticated;
// Initialize countdown based on current connection state
_onConnectionChange(_stompService.isConnected && _stompService.isAuthenticated);
_connSub = DartMQ().subscribe<bool>(MQTopics.connectionStatus, _onConnectionChange);
}
void _onConnectionChange(bool isConnected) {
if (!mounted) return;
if (isConnected) {
_hadConnection = true; // Mark that we had a successful connection
_stopCountdown();
setState(() {});
} else {
_startCountdown();
}
}
void _startCountdown() {
_stopCountdown();
setState(() {
_secondsToRetry = 15;
});
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (_) async {
if (!mounted) return;
if (_stompService.isConnected) {
_stopCountdown();
return;
}
// Decrement until 0, then attempt reconnect
if (_secondsToRetry > 1) {
setState(() {
_secondsToRetry = _secondsToRetry - 1;
});
return;
}
// Show 0 for one tick and try to reconnect now
setState(() {
_secondsToRetry = 0;
});
try {
// Only auto-reconnect if we already know the target; discovery remains user-initiated
await _stompService.connect();
} catch (e, stackTrace) {
developer.log('Auto-reconnect attempt failed: $e', name: 'OfflineBanner');
developer.log('Stack trace: $stackTrace', name: 'OfflineBanner');
}
if (!mounted) return;
if (!_stompService.isConnected) {
// Still offline -> reset countdown for next attempt
setState(() {
_secondsToRetry = 15;
});
}
});
}
void _stopCountdown() {
_countdownTimer?.cancel();
_countdownTimer = null;
}
@override
void dispose() {
_stopCountdown();
_connSub?.cancel();
_connSub = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
final isOnline = _stompService.isConnected && _stompService.isAuthenticated;
if (isOnline) return const SizedBox.shrink();
// Different messages for initial connection vs connection lost
final String title;
final String subtitle;
final IconData icon;
final Color? bgColor;
final Color? iconColor;
final Color? titleColor;
final Color? subtitleColor;
if (_hadConnection) {
// Connection was lost
title = 'Offline Verbindung verloren';
subtitle = 'Verbindung wird wiederhergestellt.';
icon = Icons.wifi_off;
bgColor = Colors.red[50];
iconColor = Colors.red[700];
titleColor = Colors.red[900];
subtitleColor = Colors.red[800];
} else {
// Initial connection attempt
title = 'Verbinde mit Server...';
subtitle = 'Bitte warten.';
icon = Icons.sync;
bgColor = Colors.orange[50];
iconColor = Colors.orange[700];
titleColor = Colors.orange[900];
subtitleColor = Colors.orange[800];
}
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
color: bgColor,
child: Row(
children: [
Icon(icon, color: iconColor),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: TextStyle(
color: titleColor,
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: TextStyle(
color: subtitleColor,
fontSize: 12,
),
),
],
),
),
],
),
);
}
}