// Automatic FlutterFlow imports import '/backend/schema/structs/index.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import 'index.dart'; // Imports other custom widgets import '/custom_code/actions/index.dart'; // Imports custom actions import '/flutter_flow/custom_functions.dart'; // Imports custom functions import 'package:flutter/material.dart'; // Begin custom widget code // DO NOT REMOVE OR MODIFY THE CODE ABOVE! // Set your widget name, define your parameter, and then add the // boilerplate code using the `` button on the right! import 'package:go_router/go_router.dart'; // ⬇⬇⬇ PLAK VANAF HIER ⬇⬇⬇ /// Toont een melding zodra de server onbereikbaar is, of zodra de sessie is /// verlopen. /// /// Het widget zelf neemt **geen ruimte** in de layout in (`SizedBox.shrink`): /// de header van dit project zit in een `FlexibleSpaceBar.background` met een /// vaste hoogte, dus een balk die wél ruimte vraagt geeft daar gegarandeerd een /// RenderFlex-overflow. In plaats daarvan gaat de melding via de /// `ScaffoldMessenger` over de pagina heen. Daardoor kan het widget op één /// plek in de gedeelde header staan en werkt het op elke pagina. /// /// Waarom een custom widget en geen Text met een Visibility-conditie: de /// widgets in dit project lezen App State via de singleton `FFAppState()`, /// niet via `context.watch`. Een gewone conditie zou dus pas opnieuw evalueren /// als er toevallig iets anders een rebuild veroorzaakt. Dit widget luistert /// zelf naar FFAppState. class VerbindingsBanner extends StatefulWidget { const VerbindingsBanner({ super.key, this.width, this.height, this.tekst, }); final double? width; final double? height; final String? tekst; @override State createState() => _VerbindingsBannerState(); } class _VerbindingsBannerState extends State { // Statisch, dus gedeeld over alle instanties: bij doorklikken tussen // pagina's wordt niet bij elke opbouw opnieuw een verzoek gedaan, en er // verschijnt nooit meer dan één balk tegelijk. static DateTime? _laatsteCheck; static const Duration _minimaalTussenChecks = Duration(seconds: 20); /// Welke melding er nu staat: null, 'verbinding' of 'sessie'. static String? _getoondeMelding; bool _bezig = false; @override void initState() { super.initState(); FFAppState().addListener(_bijStateWijziging); WidgetsBinding.instance.addPostFrameCallback((_) async { await _checkIndienNodig(); _werkBalkBij(); }); } @override void dispose() { FFAppState().removeListener(_bijStateWijziging); super.dispose(); } void _bijStateWijziging() { if (mounted) _werkBalkBij(); } void _werkBalkBij() { final messenger = ScaffoldMessenger.maybeOf(context); if (messenger == null) return; // Geen verbinding weegt zwaarder: zonder netwerk zegt een 403 niets zinnigs // over de sessie, en dan is "log opnieuw in" misleidend advies. final String? gewenst = FFAppState().geenVerbinding ? 'verbinding' : (FFAppState().sessieVerlopen ? 'sessie' : null); if (gewenst == _getoondeMelding) return; if (gewenst == null) { _getoondeMelding = null; messenger.hideCurrentSnackBar(); return; } _getoondeMelding = gewenst; if (gewenst == 'sessie') { // Eenmalige notificatie: het vlaggetje gaat meteen uit, anders keert de // balk terug bij elke paginawissel. Dát je uitgelogd bent blijft daarna // gewoon zichtbaar, want userSessionid is leeg en alle // "ben je ingelogd?"-condities tonen hun uitgelogde variant. // // Bewust de directe setter en niet update(): die roept géén // notifyListeners() aan, en we zitten hier ín een listener. FFAppState().sessieVerlopen = false; } final SnackBar balk = gewenst == 'sessie' ? SnackBar( backgroundColor: FlutterFlowTheme.of(context).primary, duration: const Duration(seconds: 8), content: Row( mainAxisSize: MainAxisSize.max, children: const [ Icon(Icons.lock_outline, color: Colors.white, size: 18.0), SizedBox(width: 8.0), Expanded( child: Text( 'Je sessie is verlopen. Log opnieuw in.', style: TextStyle(color: Colors.white, fontSize: 13.0), ), ), ], ), action: SnackBarAction( label: 'Inloggen', textColor: Colors.white, onPressed: () { // De balk kan de pagina overleven waarop hij verscheen. if (mounted) context.pushNamed('login'); }, ), ) : SnackBar( backgroundColor: FlutterFlowTheme.of(context).primary, // Praktisch oneindig: de balk hoort te blijven staan zolang er // geen verbinding is, niet na een paar seconden te verdwijnen. duration: const Duration(days: 1), content: Row( mainAxisSize: MainAxisSize.max, children: [ const Icon(Icons.cloud_off, color: Colors.white, size: 18.0), const SizedBox(width: 8.0), Expanded( child: Text( widget.tekst ?? 'Geen verbinding met uitgaanskrant.com', style: const TextStyle(color: Colors.white, fontSize: 13.0), ), ), ], ), action: SnackBarAction( // Bewust een vast label: een SnackBar bouwt niet opnieuw op, dus // een status-afhankelijke tekst blijft hangen op de stand van het // moment waarop hij getoond werd. label: 'Opnieuw', textColor: Colors.white, onPressed: _check, ), ); messenger.showSnackBar(balk).closed.then((_) { // Ook als de gebruiker hem zelf wegveegt, mag er later weer één komen. // De guard voorkomt dat het afsluiten van een oude balk de vlag van een // inmiddels getoonde nieuwe balk wist. if (_getoondeMelding == gewenst) _getoondeMelding = null; }); } Future _checkIndienNodig() async { final vorige = _laatsteCheck; if (vorige != null && DateTime.now().difference(vorige) < _minimaalTussenChecks) { return; } await _check(herlaadBijHerstel: false); } /// [herlaadBijHerstel] staat alleen aan bij een tik van de gebruiker. /// Automatisch herladen zou een lus opleveren: de pagina bouwt het widget /// opnieuw op, dat checkt weer, enzovoort. Future _check({bool herlaadBijHerstel = true}) async { if (_bezig) return; _bezig = true; _laatsteCheck = DateTime.now(); bool bereikbaar = false; try { bereikbaar = await checkVerbinding(); } finally { _bezig = false; } if (bereikbaar && herlaadBijHerstel && mounted) { // De FutureBuilders op deze pagina zijn al klaar met een mislukte // respons; alleen de balk weghalen laat een leeg scherm achter. // Daarom de huidige route opnieuw opbouwen, zodat de data alsnog laadt. final huidigePad = GoRouterState.of(context).uri.toString(); context.pushReplacement(huidigePad); } } @override Widget build(BuildContext context) { // Neemt bewust geen ruimte in: de melding gaat via de ScaffoldMessenger. return const SizedBox.shrink(); } }