0

I'm having trouble displaying the result of api call in widget. I'm getting "Unexpected null value" error. From some reasons I don't want to use FutureBuilder in this case. With FutureBuilder everything is ok.

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({
    required this.userId,
    required this.id,
    required this.title,
  });

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
    );
  }
}

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Album ? myAlbum;
  Future<void>  fetchAlbum() async {
    final response = await http
        .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

    if (response.statusCode == 200) {
      // If the server did return a 200 OK response,
      // then parse the JSON.
      myAlbum= Album.fromJson(jsonDecode(response.body));
    } else {
      // If the server did not return a 200 OK response,
      // then throw an exception.
      throw Exception('Failed to load album');
    }
  }
  @override
  void initState() {
    fetchAlbum();
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fetch Data Example'),
        ),
        body: Center(
          child: Text(myAlbum!.title)
        ),
      ),
    );
  }
}

I assume that problem is because widget is builded before initState call.

woywie87
  • 7
  • 2

3 Answers3

0
            void initState() {
             super.initState();
              WidgetsBinding.instance
             .addPostFrameCallback((_) => fetchAlbum());
               }


   You can add this code in initState() So that api call will be executed 
   after WidgetBuilding.
Meghna
  • 3
  • 2
  • Thanks, but I've added Your code and still have an error: "Null check operator used on a null value" – woywie87 Aug 30 '23 at 18:13
0

It happened because when you are trying to display values in widget from API response your widget tree is in building state so you are getting this error.

You should have to use some state management Provider/Getx/Bloc . OR you can use setState() after API call .

                                       Future<void> fetchAlbum() async {
final response = await http
    .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

if (response.statusCode == 200) {

// If the server did return a 200 OK response,

// then parse the JSON.

  myAlbum = Album.fromJson(jsonDecode(response.body));

  setState(() {});
} else {

// If the server did not return a 200 OK response,

// then throw an exception.

  throw Exception('Failed to load album');
}

}

Meghna
  • 3
  • 2
0

This happens because your API Calls are Asynchronous. This means they take time in order to complete. Your request goes to the server, the server answers, it goes back and only then you will have the response value.

What makes it clear something is async is the keyword await before your http.get request. As the word suggests, you have to await for it to finish in order to continue the rest of the method.

This is called asynchronous because it happens on the background. Dart has a single thread execution model. If the execution were to stop and wait synchronously for the response, your app would freeze in the meanwhile.

The way your application is written does not tell how to behave while you don't have your response value. There are many ways to do this. One of them, is to display a default value while the response isn't ready:

child: Text(myAlbum?.title ?? 'Default title.')

This ?? is a null-checking operator. It return the right value if the left one is null.

In your example, you should also need a setState((){}); inside your fetchAlbum method, in order to let the UI know it needs to rebuild the screen.

(When you hot reload, it rebuilds the screen without changing the state. So if you hot reload after the API call is finished, you would not get the same error)

Naslausky
  • 3,443
  • 1
  • 14
  • 24