feat(block2): global 'you're offline' banner (network state)
Feedback: offline, the social layer stops working with no clear reason. A slim amber strip now shows above every screen when there's no network (connectivity_ plus), so it's obvious WHY sharing is paused — the inventory keeps working. Wrapped once via the MaterialApp builder; connectivity check is guarded so it no-ops where the plugin is absent (tests/unsupported platforms). i18n en/es/pt. Analyzer clean.
This commit is contained in:
parent
5741d6bde8
commit
1ac954a09e
11 changed files with 135 additions and 5 deletions
87
apps/app_seeds/lib/ui/offline_banner.dart
Normal file
87
apps/app_seeds/lib/ui/offline_banner.dart
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../i18n/strings.g.dart';
|
||||
|
||||
/// Wraps the app and shows a slim strip at the very top when there's no network,
|
||||
/// so it's clear WHY the social layer (market, chat, profile) is paused — the
|
||||
/// inventory keeps working offline. Wrap the whole app once (MaterialApp builder).
|
||||
class OfflineBanner extends StatefulWidget {
|
||||
const OfflineBanner({required this.child, super.key});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
State<OfflineBanner> createState() => _OfflineBannerState();
|
||||
}
|
||||
|
||||
class _OfflineBannerState extends State<OfflineBanner> {
|
||||
bool _offline = false;
|
||||
StreamSubscription<List<ConnectivityResult>>? _sub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_init();
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
try {
|
||||
final current = await Connectivity().checkConnectivity();
|
||||
if (mounted) setState(() => _offline = _isOffline(current));
|
||||
_sub = Connectivity().onConnectivityChanged.listen((results) {
|
||||
if (mounted) setState(() => _offline = _isOffline(results));
|
||||
});
|
||||
} catch (_) {
|
||||
// Platform without connectivity support → assume online, hide the banner.
|
||||
}
|
||||
}
|
||||
|
||||
bool _isOffline(List<ConnectivityResult> results) =>
|
||||
results.isEmpty ||
|
||||
results.every((r) => r == ConnectivityResult.none);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_sub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
if (_offline)
|
||||
Material(
|
||||
color: const Color(0xFFE8A200), // amber — not an error, just paused
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.cloud_off, size: 16, color: Colors.white),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
context.t.common.offline,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12.5,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: widget.child),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue