Show Dialog in Flutter mit Callback Funktion

 

Vor kurzem hatte ich in diesem Blog einen Post über AlertDialog(...) geschrieben.

Photo by 수안 최 on Unsplash

In diesem Zusammenhang bin ich dort auf, ShowDialog(...) gestossen. Es ist also keine schlechte Idee diese Funktion näher zu betrachten. 

Im Zusammenhang mit AlertDialog(...) haben wir die Frage, was passiert wenn der Button gedrückt wurde, noch damit beantwortet, das der Anwender zurück auf den ursprünglichen Schirm kam. 
In einer realen Anwendung wird man aber in der Regel zwei Buttons haben, ok und cancel zum Beispiel. Wir müssen diese zwei Buttons auswerten können.

Grundlagen


Ein Dialog in Flutter schwebt über allen anderen Bildschirmelementen. Er kann Buttons, aber auch zum Beispiel ein Bild, enthalten. Als Grundeinstellung wird der Hintergrund abgedunkelt.
Um effektiv auf die Buttons reagieren zu können, benötigen wir ein Statefull Widget. Ich werde in diesem Post nicht weiter auf dieses Widget eingehen. In diesem Blog, aber auch im Internet, finden sich genug Informationen dazu.
Wenn Du Flutter und damit auch Dart benutzt wirst Du des Öfteren auf Callback Funktionen treffen. Eine Funktion kann als Parameter auch eine andere Funktion übernehmen. Ein Beispiel:

class Demo{
  final String text;
  final String text2;
  final void Function(bool v) callBack;
  
  Demo({required this.text,required this.text2,required this.callBack});
}

Der Code oben: Die Klasse Demo erwartet u. A. als Parameter eine Funktion callback. Mit bool v definieren wir, das diese Callback Funktion vom Typ bool ist, also wahr oder falsch.

Der Code


Starten wir mit dem Einstieg:


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String message = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(message, style: const TextStyle(color: Colors.black, fontSize: 30)),
            const SizedBox(height: 18),
            TextButton(
              style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.green)),
              onPressed: onTapShowDialog,
              child: const Text(
                'Show Dialog Widget',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ],
        ),
      ),
    );
  }

Im Code oben ist HomePage ein Stateful Widget. Zum setzen von state werden wir String message verwenden. In diesem Widget ist auch der Auslöser für Show Dialog zu finden. Ein Textbutton, der als onPressed: Argument onTapShowDialog übergeben bekommt.

onTapShowDialog


Nachdem der Auslöser betätigt wurde, kommt diese Funktion zum Einsatz:


void onTapShowDialog() {
    showAlertDialogs(
      context,
      title: 'Ein Flutter Demo',
      content: 'Für Testzwecke',
    ).then((value) {
      setState(() {
        if (value == true) {
          message = '[Yes] wurde benutzt!!!';
        } else if (value == false) {
          message = '[NO] wurde benutzt!!!';
        } else {
          message = '[Nichts] wurde benutzt!!!';
        }
      });
      debugPrint(value.toString());
    });
  }
}

Das erste was im Code oben auffällt: Gleich zu Beginn wird showAlertDialogs(...) mit den Werten für context, title: und content: versorgt.
Der "wichtige" Abschnitt beginnt mit .then(...) ! showAlertDialogs(...) retourniert einen Future !
Sobald das Resultat dieses Future's vorliegt (value), wird der Code im .then(...) Abschnitt ausgeführt. Dabei wird value ausgewertet und in der setState(...) Funktion wird abhängig davon die Zeichenkette message gesetzt.
Mit debugPrint(...) drucken wir zur Kontrolle den Wert in die Konsole.

showAlertDialogs


Wie oben schon geschrieben retourniert diese Funktion einen Future:


Future<bool?> showAlertDialogs(BuildContext context, {required String title, required String content}) async {

    bool? response;
    await showDialog(
    context: context,
    builder: (context) => MeinAlertDialog(
      title: title,
      inhalt: content,
      buttonText1: 'Yes',
      buttonText2: 'No',
      callback: (bool v) {
        response = v;
      },
    ),
  );
  return response;
}

Man beachte das der Funktionskörper mit async, also als asynchron markiert ist. Damit die Ausführung des Codes unterbrochen wird, bis das Resultat von showDialog(...) vorliegt, ist diese mit await gekennzeichnet.
Als Parameter für builder: übergeben wir MeinAlertDialog(...). Hier müssen wir auch die CallBack Funktion mit übergeben. Sie wird einen bool Wert liefern, der response zugewiesen wird. Am Schluss wird response an die aufrufende Ebene retourniert. Im übrigen wird die Variable response, dadurch das sie nicht initialisiert wird, zu Beginn NULL zugewiesen.

Der Hauptteil der Arbeit wir aber im folgenden Code ausgeführt:

MeinAlertDialog



class MeinAlertDialog extends StatelessWidget {
  final String title;
  final String inhalt;
  final String buttonText1;
  final String? buttonText2;
  final void Function(bool v) callback;

  const MeinAlertDialog(
      {Key? key,
        required this.title,
        required this.inhalt,
        required this.buttonText1,
        this.buttonText2,
        required this.callback})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(title),
      content: Text(inhalt),
      actions: <Widget>[
        TextButton(
          style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.green)),
          child: Text(buttonText1, style: const TextStyle(color: Colors.white)),
          onPressed: () {
            ///Wenn der Benutzer YES drückt
            callback(true);
            Navigator.pop(context);
          },
        ),
          TextButton(
            child: Text("$buttonText2", style: const TextStyle(color: Colors.black)),
            onPressed: () {
              ///Wenn der Benutzer NO drückt
              callback(false);
              Navigator.pop(context);
            },
          ),
      ],
    );
  }
}

Auch hier muss man wissen: Wird der YES Button gedrückt, wird true mit callback(...) retourniert, bei NO false. Klickt der Benutzer ausserhalb des AlertDialog Fensters wird showDialog(...) NULL retournieren. Hier noch einmal der komplette Code. Bis bald !
Dieser Artikel basiert im übrigen auf einem Post von Paul Edeme’kong.

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String message = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(message, style: const TextStyle(color: Colors.black, fontSize: 30)),
            const SizedBox(height: 18),
            TextButton(
              style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.green)),
              onPressed: onTapShowDialog,
              child: const Text(
                'Show Dialog Widget',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ],
        ),
      ),
    );
  }

  void onTapShowDialog() {
    showAlertDialogs(
      context,
      title: 'Ein Flutter Demo',
      content: 'Für Testzwecke',
    ).then((value) {
      setState(() {
        if (value == true) {
          message = '[Yes] wurde benutzt!!!';
        } else if (value == false) {
          message = '[NO] wurde benutzt!!!';
        } else {
          message = '[Nichts] wurde benutzt!!!';
        }
      });
      debugPrint(value.toString());
    });
  }
}

class MeinAlertDialog extends StatelessWidget {
  final String title;
  final String inhalt;
  final String buttonText1;
  final String? buttonText2;
  final void Function(bool v) callback;

  const MeinAlertDialog(
      {Key? key,
        required this.title,
        required this.inhalt,
        required this.buttonText1,
        this.buttonText2,
        required this.callback})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(title),
      content: Text(inhalt),
      actions: <Widget>[
        TextButton(
          style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.green)),
          child: Text(buttonText1, style: const TextStyle(color: Colors.white)),
          onPressed: () {
            ///Wenn der Benutzer YES drückt
            callback(true);
            Navigator.pop(context);
          },
        ),
          TextButton(
            child: Text("$buttonText2", style: const TextStyle(color: Colors.black)),
            onPressed: () {
              ///Wenn der Benutzer NO drückt
              callback(false);
              Navigator.pop(context);
            },
          ),
      ],
    );
  }
}

Future<bool?> showAlertDialogs(BuildContext context, {required String title, required String content}) async {

    bool? response;
    await showDialog(
    context: context,
    builder: (context) => MeinAlertDialog(
      title: title,
      inhalt: content,
      buttonText1: 'Yes',
      buttonText2: 'No',
      callback: (bool v) {
        response = v;
      },
    ),
  );
  return response;
}

Kommentare

Beliebte Posts aus diesem Blog

Material Design in Flutter Teil 2

Listen in Dart (2021): Part 1 List.filled List.empty und List.add

Dart Basic: Listen Part 1