0
votes

I am trying to use snackbar on my future builder to shows error.So after making my future method instance on initState method:

Future<DetailModel> futureDetail;
  ...
   @override
  void initState() {
    futureDetail =
        DetailProvider().detailProvider(widget.id);
    super.initState();
  }

In futureBuilder i use like below:

Center(
              child: SingleChildScrollView(
                child: FutureBuilder(
                  future: futureDetail,
                  builder:
                      (context, AsyncSnapshot<DetailProductModel> snapshot) {
                    if (!snapshot.hasError ||
                        snapshot.connectionState == ConnectionState.waiting) {
                      return Center(
                        child: CircularProgressIndicator(),
                      );
                    }
                    if (snapshot.hasData && snapshot.data.data != null) {
                      return Container();
                    }
                    if (snapshot.hasError &&
                        snapshot.error != null &&
                        snapshot.data == null) {
                      WidgetsBinding.instance.addPostFrameCallback((_) =>
                          showSnackbar(snapshot.error.toString(), context));
                      return Container();
                    } else {
                      return Center(
                        child: Text('failed to load'),
                      );
                    }
                  },
                ),
              ),
            ),

detailProvider method calls http service and in my example i throw SocketException to get error on builder and show snackbar.

I know future builder is for showing a widget per state but i want to show snakbar.

But builder is called twice and snackbar shows twice an error? I checked that this three conditions are the same snapshot.hasError and snapshot.error != null and snapshot.data == null in twice build . How could i prevent showing twice snakbar?

1
what you so on the logs if you just print(snapshot); as the first line in your builder?pskink
and your stateful widget is built two times too?pskink
This is full print paste.ubuntu.com/p/Jmry3HxWcs @pskinkCyrus the Great

1 Answers

1
votes

Update: There was a problem with the code that I shared. Please, check this out.

First of all, always create your SnackBar outside of the FutureBuilder because FutureBuilder is constantly called, so it causes an infinite loop. What you can do is to use catchError. You can still check whether or not there is an active SnackBar before create a new one by using the variable we already declared.

You will see that SnackBar will show up only once even though I called it thrice when you run the code below.

Apply the same way to update your own code:

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  bool snackBarActive = false;
  Future<String> futureDetail;

  @override
  void initState() {
    futureDetail = Future<String>.error('An error Occurred').catchError((errText) {
      showSnackBar(errText);
      showSnackBar(errText);
      showSnackBar(errText);
    });
    super.initState();
  }

  void showSnackBar(String errText) {
    if (!snackBarActive) {
      setState(() {
        snackBarActive = true;
      });

      WidgetsBinding.instance.addPostFrameCallback((_) => Scaffold.of(context)
              .showSnackBar(SnackBar(
                content: Text(errText),
              ))
              .closed
              .whenComplete(() {
            setState(() {
              snackBarActive = false;
            });
          }));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SingleChildScrollView(
          child: FutureBuilder(
            future: futureDetail,
            builder: (context, AsyncSnapshot<String> snapshot) {
              if (!snapshot.hasError &&
                  snapshot.hasData &&
                  snapshot.data != null) {
                return Container();
              } else {
                return Center(
                  child: Text('failed to load'),
                );
              }
            },
          ),
        ),
      ),
    );
  }
}