Flutter widget test voor modaal scherm

Een van de manieren om schermen in een Flutter applicatie informatie te laten uitwisselen is via de Navigator: Als het volgende scherm modaal via een push() is aangemaakt, kan dit scherm een resultaat in een pop() terug geven. Maar hoe test je dit in een widget test?

Een manier om dit op te lossen, is een extensie bouwen op WidgetTester die de Navigator interactie naspeelt. Hiervoor is een extra widget nodig die het gevraagde widget in de navigatie stack pusht en het resultaat opvangt. Met een standaard pumpWidget() aanroep kan deze extra widget worden gestart, wat direct de build() functie aanroept.

Omdat Flutter niet toestaat om tijdens het uitvoeren van de build functie te navigeren, is het niet mogelijk om de aanroep van het widget rechtstreeks in de build te zetten. De oplossing hiervoor is de registratie van een callback die door het framework direct wordt aangeroepen nadata de build cyclus is afgelopen. (Overigens geeft Craig Labenz in deze video een helder overzicht over zowel de widget trees en hoe rendering intern werkt.)

Door het resultaat vervolgens via een callback terug te geven, kan de widget test het resultaat van het modale scherm controleren nadat de interactie met het scherm is uitgevoerd.

Zie de onderstaande code als voorbeeld:

extension WidgetTesterExtension on WidgetTester {  
  Future<void> pumpNavigation(
    widget,
    ValueChanged<Object?> onResult) async {
    await pumpWidget(
      _Pusher(
        widget: widget,
        onResult: onResult,
      ),
    );
    await pumpAndSettle();
  }
}

class _Pusher extends StatelessWidget {
  final Widget widget;
  final ValueChanged<Object?> onResult;

  const _Pusher({required this.widget, required this.onResult});

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      final result = await Navigator.push(
        context,
        MaterialPageRoute(
          builder: (_) => widget,
        ),
      );
      onResult(result);
    });
    return const SizedBox();
  }
}

Als uitbreiding zou het type van het resultaat nog via generics aangegeven kunnen worden, en een mechanisme toegevoegd kunnen worden dat zeker stelt dat er een resultaat terug is gekomen. (Het alternatief is natuurlijk om expliciet te controleren of het modale scherm aan het einde van de test niet meer gevonden wordt.)