How to solve: Update an AlertDialog with success or fail message after network communication

Question

Asked by cap on December 20, 2021 (source).

  1. Summarize the Problem:

My application sends information to a server and the server responds with success or failure. I am having trouble updating an AlertDialog with the result of network communication. I am sending multiple items to the server when the user saves their settings and I need to track if all the settings were successfully sent. So when all the settings were successfully sent, I can update the AlertDialog with success. The issue I am seeing with current implementation is it takes me two times to activate the TextButton before I see the correct message. AlertDialog should show the correct message after the first TextButton press labeled as "save". One of the cases I need to solve is if the server is down and the app's connection request times out. Then I need to use something like a CircularProgressIndicator so the user can wait while network communication is being done.

The variable successPrompt is what contains the message with the result of the network transaction. This needs to be updated to the correct message by the time the AlertDialog pops up.

2: What I've tried:

I've tried using FutureBuilder to create the AlertDialog but I got the same result. I need a way to bring up the AlertDialog when I know the result of the network transaction. What happens is the AlertDialog will be brought up but the application is still trying to connect to the server in the background. I want to bring up the widget once this step is done and the socket is closed.

3: Here's the relevant code. Please don't mind the debug prints and commented out code.


import 'package:flutter/material.dart';
import 'dart:io';
import 'globals.dart';
import 'dart:convert' show utf8;

import 'package:local_auth/local_auth.dart';

class SystemsSettingsPage extends StatefulWidget {

  final int index;
  SystemsSettingsPage({ required this.index});

  @override
  _SystemsSettingsPage createState() => _SystemsSettingsPage();

}

class _SystemsSettingsPage extends State<SystemsSettingsPage> {

  bool tileValTemp = false;
  bool tileValDetect = false;
  bool tileValCamOff = false;
  bool tileValSystem = false;
  bool connected = false;

  int successCount = 0;
  String successPrompt = "";


  @override
  Widget build(BuildContext context) {

    return Scaffold(
        backgroundColor: Colors.black,
        appBar: AppBar(
          backgroundColor: Colors.blueAccent,
          title: Text("Base Station Settings"),
        ),
        body: Column(
          children: <Widget> [
            SwitchListTile(value: tileValDetect,
                onChanged: (bool val){ setState(() {
                  tileValDetect = val;
                });},
                title: Text('Detection notifications', style: TextStyle(color: Colors.white))
            ),
            SwitchListTile(value: tileValTemp,
                onChanged: (bool val){ setState(() {
                  tileValTemp = val;
                });},
                title: Text('Temperature threshold out of range', style: TextStyle(color: Colors.white))
            ),
            TextButton(
              child: const Text("save", style: TextStyle(fontSize: 20.0)),
              style: ButtonStyle(foregroundColor: MaterialStateProperty.all<Color>(Colors.white),
                padding: MaterialStateProperty.all<EdgeInsets>(EdgeInsets.all(10.0)),
                backgroundColor: MaterialStateProperty.all<Color>(Colors.blueAccent)),
                onPressed: () {

                  //successPrompt = "Loading.. Wait 5 seconds to update.";
                  successCount = 0;

                  Socket.connect(baseStationAddresses[0], baseStationPort,timeout: Duration(seconds: 5)).then(
                    (socket) {
                      print('Connected to: '
                          '${socket.remoteAddress.address}:${socket
                          .remotePort}');

                      String command = "SETSYSTEM," + baseStationNames[0] + ",detectMotion," + "$tileValDetect";
                      socket.write(command);

                      socket.listen((data) {

                        String socketData = utf8.decode(data);

                        if(socketData == "REQUEST_CONFIRMED") {
                          successCount += 1;
                        }
                      },
                        onDone: () {
                          socket.destroy();
                        },

                      );
                    },
                  ).catchError((onError) {
                    print("here 1");
                    successPrompt = "There was a problem. Please retry.";

                  });

                  Socket.connect(baseStationAddresses[0], baseStationPort,timeout: Duration(seconds: 5)).then(
                          (socket) {
                        print('Connected to: '
                            '${socket.remoteAddress.address}:${socket
                            .remotePort}');

                        String command = "SETSYSTEM," + baseStationNames[0] + ",tempThreshold," + "$tileValTemp";
                        socket.write(command);

                        socket.listen((data) {
                          String socketData = utf8.decode(data);

                          if(socketData == "REQUEST_CONFIRMED") {
                            successCount += 1;
                          }
                        },
                          onDone: () {
                            print("SuccessCount $successCount");

                            if(successCount == 2)
                            {
                              print("here 2");
                              successPrompt = "Setting successfully saved.";
                            }
                            else
                            {
                              print("here 3");
                              successPrompt = "Couldn't save, please retry.";
                            }

                            socket.destroy();
                          },
                        );
                      }
                  ).catchError((onError) {
                    print("here 4");
                    successPrompt = "There was a problem. Please retry.";

                  });

                  showDialog(context: context, builder: (context) =>
                      AlertDialog(
                        title: Text("Save results"),
                        content: Text(successPrompt),
                        actions: <Widget>[
                          TextButton(onPressed: () => Navigator.pop(context),
                            child: const Text("OK"),
                          )
                        ]
                    )
                  );

                  /*
                  FutureBuilder<String>(
                    future: getSaveStatus(),
                    builder: (context, snapshot) {
                      String nonNullableString = snapshot.data ?? 'Error';
                      if(snapshot.hasData) {
                        return AlertDialog(
                          title: Text("Save results"),
                          content: Text(nonNullableString),
                          actions: <Widget>[
                            TextButton(onPressed: () => Navigator.pop(context),
                              child: const Text("OK"),
                            )
                          ]
                        );
                      }
                      return Center(child: CircularProgressIndicator());
                    },
                  );*/
                }
              ),
            Center(
              child:ClipRRect(
                borderRadius: BorderRadius.circular(4),
                child: Stack(
                  children: <Widget>[
                    Positioned.fill(
                      child: Container(
                        decoration: const BoxDecoration(
                          color: Colors.red,
                        ),
                      ),
                    ),
                    TextButton(
                      style: TextButton.styleFrom(
                        padding: const EdgeInsets.all(16.0),
                        primary: Colors.white,
                        textStyle: const TextStyle(fontSize: 20),
                      ),
                      onPressed: () {},
                      child: const Text('Remove System'),
                    ),
                  ],
                ),
              ),
            )
          ],
        )
    );
  }

  Future<String> getSaveStatus() async {

    return await new Future(() => successPrompt);
  }

}

Any suggestion would be helpful.

Answer

Question answered by harsha.kuruwita (source).

Wrap the content of the dialog inside of a StatefulBuilder until that your AlertDialog behave as stateless widget Refer:

await showDialog<void>(
  context: context,
  builder: (BuildContext context) {
    int selectedRadio = 0;
    return AlertDialog(
      content: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return Text(successPrompt);
        },
      ),
    );
  },
);
DART FLUTTER
SHARE: