I am trying to fetch data from the API and I am able to get logs but setState is not working. Overall what I want to achieve is if there is response show the data on the screen, if there is any error in the API or on server or anything else I want to show it in the snackbar. My moto is to show errors as well.

Below is my model class

import 'http.dart';

class User {
int userId;
int id;
String title;
String body;

User({this.userId, this.id, this.title, this.body});

User.fromJson(Map<String, dynamic> json) {
  userId = json['userId'];
  id = json['id'];
  title = json['title'];
  body = json['body'];
}

Map<String, dynamic> toJson() {
  final Map<String, dynamic> data = new Map<String, dynamic>();
  data['userId'] = this.userId;
  data['id'] = this.id;
  data['title'] = this.title;
  data['body'] = this.body;
  return data;
}

}

class UserExt {

static getUserInfo(Function(User user) success, Function(String errorMesssage) error) async{

  final response = await HTTP.get(api: "https://jsonplaceholder.typicode.com/posts/1");
  if(response.isSuccess == true) {
    success(User.fromJson(response.response));
  } else {
    error(response.response);
  }

}

}

Below is my http.dart file

import 'dart:html';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'dart:convert' as convert;
import 'package:http/http.dart';

const _timeoutDuration = Duration(seconds: 5);

class HTTP {

  static Future<HttpResponse> get({@required String api}) async {

    try {
      Response response = await http.get(api).timeout(_timeoutDuration);
      return _modeledResponse(response);
    } catch (error) {
      return HttpResponse(isSuccess: false, response: error.toString());
    }

  }

  static Future<HttpResponse> _modeledResponse(Response response) async {

    try {
      if(response.statusCode == HttpStatus.ok) {
        var jsonResponse = convert.jsonDecode(response.body);
        return HttpResponse(isSuccess: true, response: jsonResponse);
      } else {
        return HttpResponse(isSuccess: false, response: response.statusCode.toString());
      }
    } catch (error) {
      return HttpResponse(isSuccess: false, response: error.toString());
    }

  }

}

class HttpResponse {
  final bool isSuccess;
  final dynamic response;
  HttpResponse({@required this.isSuccess, @required this.response});
}

Below is my screen from where I am calling the API.

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http_request/User.dart';
import 'http.dart';

class ApiCalling extends StatefulWidget {
  @override
  _ApiCallingState createState() => _ApiCallingState();
}

class _ApiCallingState extends State<ApiCalling> {

  bool showLoader = false;

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      body: Center(
        child: Stack(
          children: <Widget>[

            Center(
              child: RaisedButton(
                child: Text("Call API"),
                onPressed: () {

                  setState(() {
                    showLoader = true;
                  });
                  UserExt.getUserInfo((user){
                    print("UUUser id = ${user.userId}");
                    Scaffold.of(context).showSnackBar(SnackBar(content:     Text("${user.userId}"),));

                setState(() {
                  showLoader = false;
                });

              }, (error){
                Scaffold.of(context).showSnackBar(SnackBar(content: Text("${error}"),));
                setState(() {
                  showLoader = false;
                });
              });

            },
          ),
        ),
        Visibility(child: CircularProgressIndicator(backgroundColor: Colors.pink,), visible: showLoader,),

      ],
    ),
  ),
);
  }
}

In the current code indicator is not getting show/hide or snackbar is also not getting displayed.


Solution 1: himeshp

declare a global key final _scaffoldKey = GlobalKey();

and in UI _scaffoldKey.currentState.showSnackBar(snackbar);


Solution 2: Sagar Acharya

Just made some changes and addons just check the below code :

import 'package:flutter/material.dart';
import 'package:sample_project_for_api/model.dart';

void main() => runApp(ApiCalling());

class ApiCalling extends StatefulWidget {
  @override
  _ApiCallingState createState() => _ApiCallingState();
}

class _ApiCallingState extends State<ApiCalling> {
  bool showLoader = false;
  final _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        key: _scaffoldKey,
        body: Center(
          child: Stack(
            children: <Widget>[
              Builder(
                builder: (context) {
                  return Column(
                    children: <Widget>[
                      RaisedButton(
                        child: Text(
                            "this is first api call under the builder widget"),
                        onPressed: () {
                          UserExt.getUserInfo((user) {
                            print("UUUser id = ${user.userId}");
                            Scaffold.of(context).showSnackBar(SnackBar(
                              backgroundColor: Colors.redAccent,
                              content:
                                  Text("This is you user id ${user.userId}"),
                            ));
                          }, (error) {
                            Scaffold.of(context).showSnackBar(SnackBar(
                              duration: Duration(seconds: 2),
                              backgroundColor: Colors.redAccent,
                              content: Text("${error.toString()}"),
                            ));
                          });
                        },
                      ),
                      RaisedButton(
                        child: Text(
                            "this is second api call under the builder widget"),
                        onPressed: () {
                          UserExt.getUserInfo((user) {
                            print("UUUser id = ${user.userId}");
                            Scaffold.of(context).showSnackBar(SnackBar(
                              backgroundColor: Colors.redAccent,
                              content:
                                  Text("This is you user id ${user.userId}"),
                            ));
                          }, (error) {
                            Scaffold.of(context).showSnackBar(SnackBar(
                              duration: Duration(seconds: 2),
                              backgroundColor: Colors.redAccent,
                              content: Text("${error.toString()}"),
                            ));
                          });
                        },
                      )
                    ],
                  );
                },
              ),
              Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    RaisedButton(
                      child: Text("call snacker using the global key"),
                      onPressed: () {
                        setState(() {
                          showLoader = true;
                        });
                        UserExt.getUserInfo((user) {
                          print("UUUser id = ${user.userId}");
                          _scaffoldKey.currentState.showSnackBar(new SnackBar(
                              duration: Duration(seconds: 2),
                              content: new Text(
                                  "This is you user id :${user.userId}")));

                          setState(() {
                            showLoader = false;
                          });
                        }, (error) {
                          _scaffoldKey.currentState.showSnackBar(new SnackBar(
                              duration: Duration(seconds: 2),
                              content: new Text("${error.toString()}")));

                          setState(() {
                            showLoader = false;
                          });
                        });
                      },
                    ),
                    Secondbutton(),
                  ],
                ),
              ),
              Visibility(
                child: CircularProgressIndicator(
                  backgroundColor: Colors.pink,
                ),
                visible: showLoader,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class Secondbutton extends StatefulWidget {
  @override
  _SecondbuttonState createState() => _SecondbuttonState();
}

class _SecondbuttonState extends State<Secondbutton> {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("calling snacker without using the global key"),
      onPressed: () {
        UserExt.getUserInfo((user) {
          print("UUUser id = ${user.userId}");
          Scaffold.of(context).showSnackBar(SnackBar(
            backgroundColor: Colors.redAccent,
            content: Text("This is you user id ${user.userId}"),
          ));
        }, (error) {
          Scaffold.of(context).showSnackBar(SnackBar(
            duration: Duration(seconds: 2),
            backgroundColor: Colors.redAccent,
            content: Text("${error.toString()}"),
          ));
        });
      },
    );
  }
}

Your problem was because it was not getting the proper context.


Solution 3: B.shruti

From the official Documentation of flutter https://api.flutter.dev/flutter/material/Scaffold/of.html

When the Scaffold is actually created in the same build function, the context argument to the build function can't be used to find the Scaffold (since it's "above" the widget being returned in the widget tree). In such cases, the following technique with a Builder can be used to provide a new scope with a BuildContext that is "under" the Scaffold:

So basically the problem is with your context of Scaffold,so instead of using context of Direct parent that instantiate the Scaffold, use the context of the child.

Below code will work.

class _ApiCallingState extends State<ApiCalling> {
  bool showLoader = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Builder(
        builder: (context)=>
            Center(
        child: Stack(
          children: <Widget>[
            Center(
              child: RaisedButton(
                child: Text("Call API"),
                onPressed: () {
                  setState(() {
                    showLoader = true;
                  });
                  UserExt.getUserInfo((user) {
                    print("UUUser id = ${user.userId}");
                    print("context==$context");
                    Scaffold.of(context).showSnackBar(SnackBar(
                      content: Text(" User Id${user.userId}"),
                    ));
                     setState(() {
                      showLoader = false;

                    });

                  }, (error) {
                    setState(() {
                      showLoader = false;
                    });
                    Scaffold.of(context).showSnackBar(SnackBar(
                      content: Text("${error}"),
                    ));

                  });
                },
              ),
            ),
            Visibility(
              child:
              Center(
                child:CircularProgressIndicator(
                  backgroundColor: Colors.pink,
                ),

              ),
              visible: showLoader,
            )
          ],
        ),
      ),
      )

    );
  }
}