verbindings_banner.dart.txt 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. // ⬇⬇⬇ PLAK VANAF HIER ⬇⬇⬇
  12. /// Toont een melding zodra de server onbereikbaar is.
  13. ///
  14. /// Het widget zelf neemt **geen ruimte** in de layout in (`SizedBox.shrink`):
  15. /// de header van dit project zit in een `FlexibleSpaceBar.background` met een
  16. /// vaste hoogte, dus een balk die wél ruimte vraagt geeft daar gegarandeerd een
  17. /// RenderFlex-overflow. In plaats daarvan gaat de melding via de
  18. /// `ScaffoldMessenger` over de pagina heen. Daardoor kan het widget op één
  19. /// plek in de gedeelde header staan en werkt het op elke pagina.
  20. ///
  21. /// Waarom een custom widget en geen Text met een Visibility-conditie: de
  22. /// widgets in dit project lezen App State via de singleton `FFAppState()`,
  23. /// niet via `context.watch`. Een gewone conditie zou dus pas opnieuw evalueren
  24. /// als er toevallig iets anders een rebuild veroorzaakt. Dit widget luistert
  25. /// zelf naar FFAppState.
  26. class VerbindingsBanner extends StatefulWidget {
  27. const VerbindingsBanner({
  28. super.key,
  29. this.width,
  30. this.height,
  31. this.tekst,
  32. });
  33. final double? width;
  34. final double? height;
  35. final String? tekst;
  36. @override
  37. State<VerbindingsBanner> createState() => _VerbindingsBannerState();
  38. }
  39. class _VerbindingsBannerState extends State<VerbindingsBanner> {
  40. // Statisch, dus gedeeld over alle instanties: bij doorklikken tussen
  41. // pagina's wordt niet bij elke opbouw opnieuw een verzoek gedaan, en er
  42. // verschijnt nooit meer dan één balk tegelijk.
  43. static DateTime? _laatsteCheck;
  44. static const Duration _minimaalTussenChecks = Duration(seconds: 20);
  45. static bool _balkStaatAl = false;
  46. bool _bezig = false;
  47. @override
  48. void initState() {
  49. super.initState();
  50. FFAppState().addListener(_bijStateWijziging);
  51. WidgetsBinding.instance.addPostFrameCallback((_) async {
  52. await _checkIndienNodig();
  53. _werkBalkBij();
  54. });
  55. }
  56. @override
  57. void dispose() {
  58. FFAppState().removeListener(_bijStateWijziging);
  59. super.dispose();
  60. }
  61. void _bijStateWijziging() {
  62. if (mounted) _werkBalkBij();
  63. }
  64. void _werkBalkBij() {
  65. final messenger = ScaffoldMessenger.maybeOf(context);
  66. if (messenger == null) return;
  67. if (!FFAppState().geenVerbinding) {
  68. if (_balkStaatAl) {
  69. _balkStaatAl = false;
  70. messenger.hideCurrentSnackBar();
  71. }
  72. return;
  73. }
  74. if (_balkStaatAl) return;
  75. _balkStaatAl = true;
  76. messenger
  77. .showSnackBar(
  78. SnackBar(
  79. backgroundColor: FlutterFlowTheme.of(context).primary,
  80. // Praktisch oneindig: de balk hoort te blijven staan zolang er
  81. // geen verbinding is, niet na een paar seconden te verdwijnen.
  82. duration: const Duration(days: 1),
  83. content: Row(
  84. mainAxisSize: MainAxisSize.max,
  85. children: [
  86. const Icon(Icons.cloud_off, color: Colors.white, size: 18.0),
  87. const SizedBox(width: 8.0),
  88. Expanded(
  89. child: Text(
  90. widget.tekst ?? 'Geen verbinding met uitgaanskrant.com',
  91. style: const TextStyle(color: Colors.white, fontSize: 13.0),
  92. ),
  93. ),
  94. ],
  95. ),
  96. action: SnackBarAction(
  97. // Bewust een vast label: een SnackBar bouwt niet opnieuw op, dus
  98. // een status-afhankelijke tekst blijft hangen op de stand van het
  99. // moment waarop hij getoond werd.
  100. label: 'Opnieuw',
  101. textColor: Colors.white,
  102. onPressed: _check,
  103. ),
  104. ),
  105. )
  106. .closed
  107. .then((_) {
  108. // Ook als de gebruiker hem zelf wegveegt, mag er later weer één komen.
  109. _balkStaatAl = false;
  110. });
  111. }
  112. Future<void> _checkIndienNodig() async {
  113. final vorige = _laatsteCheck;
  114. if (vorige != null &&
  115. DateTime.now().difference(vorige) < _minimaalTussenChecks) {
  116. return;
  117. }
  118. await _check(herlaadBijHerstel: false);
  119. }
  120. /// [herlaadBijHerstel] staat alleen aan bij een tik van de gebruiker.
  121. /// Automatisch herladen zou een lus opleveren: de pagina bouwt het widget
  122. /// opnieuw op, dat checkt weer, enzovoort.
  123. Future<void> _check({bool herlaadBijHerstel = true}) async {
  124. if (_bezig) return;
  125. _bezig = true;
  126. _laatsteCheck = DateTime.now();
  127. bool bereikbaar = false;
  128. try {
  129. bereikbaar = await checkVerbinding();
  130. } finally {
  131. _bezig = false;
  132. }
  133. if (bereikbaar && herlaadBijHerstel && mounted) {
  134. // De FutureBuilders op deze pagina zijn al klaar met een mislukte
  135. // respons; alleen de balk weghalen laat een leeg scherm achter.
  136. // Daarom de huidige route opnieuw opbouwen, zodat de data alsnog laadt.
  137. final huidigePad = GoRouterState.of(context).uri.toString();
  138. context.pushReplacement(huidigePad);
  139. }
  140. }
  141. @override
  142. Widget build(BuildContext context) {
  143. // Neemt bewust geen ruimte in: de melding gaat via de ScaffoldMessenger.
  144. return const SizedBox.shrink();
  145. }
  146. }