verbindings_banner.dart 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Automatic FlutterFlow imports
  2. import '/backend/schema/structs/index.dart';
  3. import '/flutter_flow/flutter_flow_theme.dart';
  4. import '/flutter_flow/flutter_flow_util.dart';
  5. import 'index.dart'; // Imports other custom widgets
  6. import '/custom_code/actions/index.dart'; // Imports custom actions
  7. import '/flutter_flow/custom_functions.dart'; // Imports custom functions
  8. import 'package:flutter/material.dart';
  9. // Begin custom widget code
  10. // DO NOT REMOVE OR MODIFY THE CODE ABOVE!
  11. // Set your widget name, define your parameter, and then add the
  12. // boilerplate code using the `</>` button on the right!
  13. import 'package:go_router/go_router.dart';
  14. // ⬇⬇⬇ PLAK VANAF HIER ⬇⬇⬇
  15. /// Toont een melding zodra de server onbereikbaar is, of zodra de sessie is
  16. /// verlopen.
  17. ///
  18. /// Het widget zelf neemt **geen ruimte** in de layout in (`SizedBox.shrink`):
  19. /// de header van dit project zit in een `FlexibleSpaceBar.background` met een
  20. /// vaste hoogte, dus een balk die wél ruimte vraagt geeft daar gegarandeerd een
  21. /// RenderFlex-overflow. In plaats daarvan gaat de melding via de
  22. /// `ScaffoldMessenger` over de pagina heen. Daardoor kan het widget op één
  23. /// plek in de gedeelde header staan en werkt het op elke pagina.
  24. ///
  25. /// Waarom een custom widget en geen Text met een Visibility-conditie: de
  26. /// widgets in dit project lezen App State via de singleton `FFAppState()`,
  27. /// niet via `context.watch`. Een gewone conditie zou dus pas opnieuw evalueren
  28. /// als er toevallig iets anders een rebuild veroorzaakt. Dit widget luistert
  29. /// zelf naar FFAppState.
  30. class VerbindingsBanner extends StatefulWidget {
  31. const VerbindingsBanner({
  32. super.key,
  33. this.width,
  34. this.height,
  35. this.tekst,
  36. });
  37. final double? width;
  38. final double? height;
  39. final String? tekst;
  40. @override
  41. State<VerbindingsBanner> createState() => _VerbindingsBannerState();
  42. }
  43. class _VerbindingsBannerState extends State<VerbindingsBanner> {
  44. // Statisch, dus gedeeld over alle instanties: bij doorklikken tussen
  45. // pagina's wordt niet bij elke opbouw opnieuw een verzoek gedaan, en er
  46. // verschijnt nooit meer dan één balk tegelijk.
  47. static DateTime? _laatsteCheck;
  48. static const Duration _minimaalTussenChecks = Duration(seconds: 20);
  49. /// Welke melding er nu staat: null, 'verbinding' of 'sessie'.
  50. static String? _getoondeMelding;
  51. bool _bezig = false;
  52. @override
  53. void initState() {
  54. super.initState();
  55. FFAppState().addListener(_bijStateWijziging);
  56. WidgetsBinding.instance.addPostFrameCallback((_) async {
  57. await _checkIndienNodig();
  58. _werkBalkBij();
  59. });
  60. }
  61. @override
  62. void dispose() {
  63. FFAppState().removeListener(_bijStateWijziging);
  64. super.dispose();
  65. }
  66. void _bijStateWijziging() {
  67. if (mounted) _werkBalkBij();
  68. }
  69. void _werkBalkBij() {
  70. final messenger = ScaffoldMessenger.maybeOf(context);
  71. if (messenger == null) return;
  72. // Geen verbinding weegt zwaarder: zonder netwerk zegt een 403 niets zinnigs
  73. // over de sessie, en dan is "log opnieuw in" misleidend advies.
  74. final String? gewenst = FFAppState().geenVerbinding
  75. ? 'verbinding'
  76. : (FFAppState().sessieVerlopen ? 'sessie' : null);
  77. if (gewenst == _getoondeMelding) return;
  78. if (gewenst == null) {
  79. _getoondeMelding = null;
  80. messenger.hideCurrentSnackBar();
  81. return;
  82. }
  83. _getoondeMelding = gewenst;
  84. if (gewenst == 'sessie') {
  85. // Eenmalige notificatie: het vlaggetje gaat meteen uit, anders keert de
  86. // balk terug bij elke paginawissel. Dát je uitgelogd bent blijft daarna
  87. // gewoon zichtbaar, want userSessionid is leeg en alle
  88. // "ben je ingelogd?"-condities tonen hun uitgelogde variant.
  89. //
  90. // Bewust de directe setter en niet update(): die roept géén
  91. // notifyListeners() aan, en we zitten hier ín een listener.
  92. FFAppState().sessieVerlopen = false;
  93. }
  94. final SnackBar balk = gewenst == 'sessie'
  95. ? SnackBar(
  96. backgroundColor: FlutterFlowTheme.of(context).primary,
  97. duration: const Duration(seconds: 8),
  98. content: Row(
  99. mainAxisSize: MainAxisSize.max,
  100. children: const [
  101. Icon(Icons.lock_outline, color: Colors.white, size: 18.0),
  102. SizedBox(width: 8.0),
  103. Expanded(
  104. child: Text(
  105. 'Je sessie is verlopen. Log opnieuw in.',
  106. style: TextStyle(color: Colors.white, fontSize: 13.0),
  107. ),
  108. ),
  109. ],
  110. ),
  111. action: SnackBarAction(
  112. label: 'Inloggen',
  113. textColor: Colors.white,
  114. onPressed: () {
  115. // De balk kan de pagina overleven waarop hij verscheen.
  116. if (mounted) context.pushNamed('login');
  117. },
  118. ),
  119. )
  120. : SnackBar(
  121. backgroundColor: FlutterFlowTheme.of(context).primary,
  122. // Praktisch oneindig: de balk hoort te blijven staan zolang er
  123. // geen verbinding is, niet na een paar seconden te verdwijnen.
  124. duration: const Duration(days: 1),
  125. content: Row(
  126. mainAxisSize: MainAxisSize.max,
  127. children: [
  128. const Icon(Icons.cloud_off, color: Colors.white, size: 18.0),
  129. const SizedBox(width: 8.0),
  130. Expanded(
  131. child: Text(
  132. widget.tekst ?? 'Geen verbinding met uitgaanskrant.com',
  133. style: const TextStyle(color: Colors.white, fontSize: 13.0),
  134. ),
  135. ),
  136. ],
  137. ),
  138. action: SnackBarAction(
  139. // Bewust een vast label: een SnackBar bouwt niet opnieuw op, dus
  140. // een status-afhankelijke tekst blijft hangen op de stand van het
  141. // moment waarop hij getoond werd.
  142. label: 'Opnieuw',
  143. textColor: Colors.white,
  144. onPressed: _check,
  145. ),
  146. );
  147. messenger.showSnackBar(balk).closed.then((_) {
  148. // Ook als de gebruiker hem zelf wegveegt, mag er later weer één komen.
  149. // De guard voorkomt dat het afsluiten van een oude balk de vlag van een
  150. // inmiddels getoonde nieuwe balk wist.
  151. if (_getoondeMelding == gewenst) _getoondeMelding = null;
  152. });
  153. }
  154. Future<void> _checkIndienNodig() async {
  155. final vorige = _laatsteCheck;
  156. if (vorige != null &&
  157. DateTime.now().difference(vorige) < _minimaalTussenChecks) {
  158. return;
  159. }
  160. await _check(herlaadBijHerstel: false);
  161. }
  162. /// [herlaadBijHerstel] staat alleen aan bij een tik van de gebruiker.
  163. /// Automatisch herladen zou een lus opleveren: de pagina bouwt het widget
  164. /// opnieuw op, dat checkt weer, enzovoort.
  165. Future<void> _check({bool herlaadBijHerstel = true}) async {
  166. if (_bezig) return;
  167. _bezig = true;
  168. _laatsteCheck = DateTime.now();
  169. bool bereikbaar = false;
  170. try {
  171. bereikbaar = await checkVerbinding();
  172. } finally {
  173. _bezig = false;
  174. }
  175. if (bereikbaar && herlaadBijHerstel && mounted) {
  176. // De FutureBuilders op deze pagina zijn al klaar met een mislukte
  177. // respons; alleen de balk weghalen laat een leeg scherm achter.
  178. // Daarom de huidige route opnieuw opbouwen, zodat de data alsnog laadt.
  179. final huidigePad = GoRouterState.of(context).uri.toString();
  180. context.pushReplacement(huidigePad);
  181. }
  182. }
  183. @override
  184. Widget build(BuildContext context) {
  185. // Neemt bewust geen ruimte in: de melding gaat via de ScaffoldMessenger.
  186. return const SizedBox.shrink();
  187. }
  188. }