Vinicius Morais
Vinicius Morais

Reputation: 595

How i wait a future request?

I having this message error from my console from android studio:

════════ (3) Exception caught by widgets library ═══════════════════════════════════════════════════
The method '[]' was called on null.
Receiver: null
Tried calling: [](0)
The relevant error-causing widget was: 
  FutureBuilder<dynamic> file:///home/developer/projects/PaladinsStats/lib/screens/transferencia/formulario.dart:26:15
════════════════════════════════════════════════════════════════════════════════════════════════════

But when my request has a response, my page just load normal. Usually i put a checker to see if the objects is null to render but i don't know what is happening here. I think he is trying access a null variable, but the builder wait the future to render? If no, how can i made the builder wait the future?

My code:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../../models/champion.dart';

class FormularioTransferencia extends StatefulWidget {
  final Champion champion;

  const FormularioTransferencia({Key key, @required this.champion})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return FormularioTransferenciaState();
  }
}

class FormularioTransferenciaState extends State<FormularioTransferencia> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.champion.feName),
        ),
        body: FutureBuilder(
          future: fetchPost(widget.champion.feName),
          builder: (context, snapshot) {
            final champion = json.decode(snapshot.data.toString());
            return SingleChildScrollView(
              child: Column(
                children: <Widget>[
                  Image.network(
                      'https://web2.hirez.com/paladins/champion-headers/' +
                          champion[0]['slug'] +
                          '.png'),
                ],
              ),
            );
          },
        ));
  }

  fetchPost(championName) async {
    final response = await http.get(
        'https://cms.paladins.com/wp-json/wp/v2/champions?slug=' +
            championName.replaceAll(' ', '-').toLowerCase() +
            '&lang_id=1');

    if (response.statusCode == 200) {
      // If the call to the server was successful, parse the JSON
      return response.body;
    } else {
      // If that call was not successful, throw an error.
      throw Exception('Failed to load post');
    }
  }
}

Dart model:

class Champion {
  int id;
  String name;
  String feName;
  String title;
  String role;
  String feRole;
  String image;
  String latest;

  Champion(
      {this.id,
      this.name,
      this.feName,
      this.title,
      this.role,
      this.feRole,
      this.image,
      this.latest});

  Champion.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
    feName = json['feName'];
    title = json['title'];
    role = json['role'];
    feRole = json['feRole'];
    image = json['image'];
    latest = json['latest'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['name'] = this.name;
    data['feName'] = this.feName;
    data['title'] = this.title;
    data['role'] = this.role;
    data['feRole'] = this.feRole;
    data['image'] = this.image;
    data['latest'] = this.latest;
    return data;
  }
}

Upvotes: 1

Views: 1237

Answers (3)

Yuu Woods
Yuu Woods

Reputation: 1348

Add the 6 lines below.

body: FutureBuilder(
  future: fetchPost(widget.champion.feName),
  builder: (context, snapshot) {
    if (snapshot.hasData) {  // add this line
      final champion = json.decode(snapshot.data.toString());
      return SingleChildScrollView(
        child: Column(
          children: <Widget>[
            Image.network(
                'https://web2.hirez.com/paladins/champion-headers/' +
                    champion[0]['slug'] +
                    '.png'),
          ],
        ),
      );
    } else {  // add this line
      return Center(  // add this line
        child: CircularProgressIndicator(),  // add this line
      );  // add this line
    }  // add this line
  },
));

Upvotes: 1

Talha Javed Khan
Talha Javed Khan

Reputation: 420

you can use

if(!snapshot.hasData) {
  return CircularProgressIndicator();
}
else {
  return SingleChildScrollView(
              child: Column(
                children: <Widget>[
                  Image.network(
                      'https://web2.hirez.com/paladins/champion-headers/' +
                          champion[0]['slug'] +
                          '.png'),
                ],
              ),
            );
}

Upvotes: 1

Tom O
Tom O

Reputation: 2645

The first time build() is called snapshot.data is null. When the future method returns a value the widget gets rebuild.

Therefor you should do something like this:

return FutureBuilder(
  future:
      getSomeFuture(),
  builder: (context, snapshot) {
    if (snapshot.data == null) { //<-- Add this condition
      return Container();
    } else {
      return MyWidget(snapshot.data);
    }
  }
);

EDIT:

And if you want to make this look nice, use a loading animation as temporarily placeholder and make sure that it has the same height/width as the widget that gets build when the future is returned.

Upvotes: 3

Related Questions