Home > Mobile >  Flutter late variable not being initialized fast enough?
Flutter late variable not being initialized fast enough?

Time:01-06

I have this widget that is getting data from a rest API, below, that works, but before anything is rendered to the screen, it throws up the following error for like a second before showing the data:

LateInitializationError: Field 'person' has not been initialized.

How can I fix this? Fairly new to flutter and any pointers will be appreciated.

Here is my code:

class PersonDetails extends StatefulWidget {
  final String id;
  PersonDetails({
    required this.id,
  });

  @override
  _PersonDetailsState createState() => _PersonDetailsState();
}

class _PersonDetailsState extends State<PersonDetails> {
  @override
  void initState() {
    getPersonDetails();
    super.initState();
  }

  late Person person;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(person.id.toString()),
      ),
      body: Container(child: Text('you are here')),
    );
  }

  Future<void> getPersonDetails() async {
    final params = {
      "api_key": "df1555a226ba2b6d6c15c425a626e6bf",
      "language": "en-US",
    };

    Uri url = Uri.https('api.themoviedb.org', '/3/person/${widget.id}', params);
    http.Response res = await http.get(url);

    person = Person.fromJson(cnv.jsonDecode(res.body));
    setState(() {});
  }

Here is the data model my API response is being mapped to:

class Person {
  bool? adult;
  List<String>? alsoKnownAs;
  String? biography;
  String? birthday;
  Null? deathday;
  int? gender;
  Null? homepage;
  int? id;
  String? imdbId;
  String? knownForDepartment;
  String? name;
  String? placeOfBirth;
  double? popularity;
  String? profilePath;

  Person(
      {this.adult,
      this.alsoKnownAs,
      this.biography,
      this.birthday,
      this.deathday,
      this.gender,
      this.homepage,
      this.id,
      this.imdbId,
      this.knownForDepartment,
      this.name,
      this.placeOfBirth,
      this.popularity,
      this.profilePath});

  Person.fromJson(Map<String, dynamic> json) {
    adult = json['adult'];
    alsoKnownAs = json['also_known_as'].cast<String>();
    biography = json['biography'];
    birthday = json['birthday'];
    deathday = json['deathday'];
    gender = json['gender'];
    homepage = json['homepage'];
    id = json['id'];
    imdbId = json['imdb_id'];
    knownForDepartment = json['known_for_department'];
    name = json['name'];
    placeOfBirth = json['place_of_birth'];
    popularity = json['popularity'];
    profilePath = json['profile_path'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['adult'] = this.adult;
    data['also_known_as'] = this.alsoKnownAs;
    data['biography'] = this.biography;
    data['birthday'] = this.birthday;
    data['deathday'] = this.deathday;
    data['gender'] = this.gender;
    data['homepage'] = this.homepage;
    data['id'] = this.id;
    data['imdb_id'] = this.imdbId;
    data['known_for_department'] = this.knownForDepartment;
    data['name'] = this.name;
    data['place_of_birth'] = this.placeOfBirth;
    data['popularity'] = this.popularity;
    data['profile_path'] = this.profilePath;
    return data;
  }

  getBio() {
    return this.biography;
  }
}

CodePudding user response:

you could use FutureBuilder as suggested by julemand101 or you could display something else while it's null make.

either way i you have to make Person nullable.

class PersonDetails extends StatefulWidget {
  final String id;
  const PersonDetails({
    Key? key,
    required this.id,
  }) : super(key: key);

  @override
  _PersonDetailsState createState() => _PersonDetailsState();
}

class _PersonDetailsState extends State<PersonDetails> {
  @override
  void initState() {
    getPersonDetails();
    super.initState();
  }

  Person? person;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(person?.id.toString() ?? "..."),
      ),
      body: Container(
        child: const Text('you are here'),
      ),
    );
  }

  Future<void> getPersonDetails() async {
    final params = {
      "api_key": "df1555a226ba2b6d6c15c425a626e6bf",
      "language": "en-US",
    };

    Uri url = Uri.https('api.themoviedb.org', '/3/person/${widget.id}', params);
    http.Response res = await http.get(url);

    setState(() {
      person = Person.fromJson(cnv.jsonDecode(res.body));
    });
  }
}

CodePudding user response:

class PersonDetails extends StatefulWidget {
      final String id;
     PersonDetails({
       required this.id,
      });

      @override
      _PersonDetailsState createState() => _PersonDetailsState();
      }

     class _PersonDetailsState extends State<PersonDetails> {
     @override
    void initState() {
        person=getPersonDetails();
     super.initState();
   }

      late Future<Person> person;

       @override
       Widget build(BuildContext context) {
             return Scaffold(
         appBar: AppBar(
             title: Text(person.id.toString()),
            ),
           body: Container(child: Text('you are here')),
         );
        }

     Future<Person> getPersonDetails() async {
     final params = {
     "api_key": "df1555a226ba2b6d6c15c425a626e6bf",
     "language": "en-US",
           };

        Uri url = Uri.https('api.themoviedb.org', 
       '/3/person/${widget.id}', 
      params);
      http.Response res = await http.get(url);

    if(res.statuscode==200){
       Map<String,dynamic>map=jsonDecode(res.body);
       return Person.fromjson(map);
      }else{
      return throw Exception("error occur");
      }}

in your code person variable is initialising late because when statefull widget initialize in the init state you are calling the api which take time to fetch data from the server and then that variable is initialized. that is the correct way of doing that or you can make Person nullable when data is fetched from the api you can setstate the variable

  •  Tags:  
  • Related