| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- // 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<VerbindingsBanner> createState() => _VerbindingsBannerState();
- }
- class _VerbindingsBannerState extends State<VerbindingsBanner> {
- // 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<void> _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<void> _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();
- }
- }
|