verbindings_banner.dart 5.5 KB

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