2

I am building a Flutter app where I fetch data from an API inside the initState() method. However, the API data does not display on the first run — it only appears after I perform a hot reload.

class MyPage extends StatefulWidget {
  @override
  _MyPageState createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  String _apiData = "";

  @override
  void initState() {
    super.initState();
    fetchData();
  }

  Future<void> fetchData() async {
    final response = await http.get(Uri.parse('https://api.example.com/data'));
    if (response.statusCode == 200) {
      setState(() {
        _apiData = response.body; 
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text(_apiData.isEmpty ? "Loading..." : _apiData),
      ),
    );
  }
}

Problem:

  • On first app load, the screen only shows "Loading...".
  • After a hot reload (in VSCode or Android Studio), the API data shows correctly.
  • It seems like setState is not updating the UI properly on first load.

What I tried:

  • Checked API response manually — it's working.
  • Verified that setState() is called after API fetch.
  • Added print statements — they show data is retrieved.
  • Tried moving the API call to build() method (still no success).

How can I ensure that API data is loaded and displayed properly on the initial load, without needing a hot reload?

2
  • 1
    You're missing a lot of information... how getUserName provides the name to the widget? I see a variable mData but you don't show how you fill it. Commented Aug 9, 2020 at 17:17
  • there is mData class ModelUserData{ String fullname; ModelUserData({ this.fullname }); ModelUserData.fromJson(Map<String,dynamic> parseJson){ fullname = parseJson['full_name']; } } var mData = ModelUserData();
    – Sam Allen
    Commented Aug 10, 2020 at 7:14

2 Answers 2

1

Firstly, there's a typo and no @override tag to your initState. It should be like that :

@override
void initState() {
    super.initState();

Then, you don't show where mData comes from. The business logic code is generally essential to give. So that would be GetRequest().

Anyway, the simplest way would be to make getUserName like this :

Future<String> getUserName(String token) async {
     // do your thing
     return userName;
}

With that, you can await getUserName and rebuild when it comes back. So like that in the initState (like token, make a userName State variable) :

() async {
    userName = await GetRequest().getUserName(token);
    setState((){}); // this triggers the widget to rebuild
}();

Now, the widget rebuilds when the request resolves, and you can directly use the userName variable in your UI code.

However, as your app grows, this will quickly become tedious. To make it cleaner, learn state management.

1

I changed your method getUserName to return data instead of assigning mData inside it

class GetRequest{

    String messageAlert;

    Future<ModelUserData> getUserName(String _token) async {
        var response = await http.get("${ApiService.url}/dashboard", 
            headers: <String, String>{
            "accept": "application/json",
            "authorization": "Bearer " + _token,
            //"token": _token,
        });
        var responseBody = json.decode(response.body);
        return ModelUserData.fromJson(responseBody);
    }
}

Then on initState you should do

@override
void initState() {
    GetRequest().getUserName(token).then((model) => {
        setState(() => mData = model);
    })

    super.initState();        
} 

The important thing here is to cal setState when model arrives

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.