why can't I call my Model in My View page?

Issue

I’m trying to learn how to use APIs, but I’ve been struggling to get my code to work,
here is what I have so far ;

Provider :

class MovieProvider extends GetConnect {
  @override
  var queryParameters = {
    'limit': '8',
    'genres': 'comedia',
    'sort': 'year',
    'type': 'movies'
  };
  static const String _baseUrl = 'movies-app1.p.rapidapi.com';
  void onInit() {
    httpClient.baseUrl = _baseUrl;
  }

  static const Map<String, String> _headers = {
    "x-rapidapi-key": "***************",
    "x-rapidapi-host": "movies-app1.p.rapidapi.com",
  };

  // Base API request to get response
  Future<dynamic> getStats() async {
    Uri uri = Uri.https(_baseUrl, '/api/movies', queryParameters);
    final response = await http.get(uri, headers: _headers);
    print('provider IS WORKING');
    if (response.statusCode == 200) {
      // If server returns an OK response, parse the JSON.
      print("success");
      print(response.body);
      return json.decode(response.body);
    } else {
      print("not success");
      // If that response was not OK, throw an error.
      throw Exception('Failed to load json data');
    }
  }
}

Model :

import 'dart:convert';

Movies moviesFromJson(String str) => Movies.fromJson(json.decode(str));

String moviesToJson(Movies data) => json.encode(data.toJson());

class Movies {
  Movies({
    this.status,
    this.success,
    this.messageStatus,
    this.results,
    this.totalResults,
    this.totalPages,
  });

  int? status;
  bool? success;
  String? messageStatus;
  List<Result>? results;
  int? totalResults;
  int? totalPages;

  factory Movies.fromJson(Map<String, dynamic> json) => Movies(
        status: json["status"],
        success: json["success"],
        messageStatus: json["messageStatus"],
        results:
            List<Result>.from(json["results"].map((x) => Result.fromJson(x))),
        totalResults: json["total_results"],
        totalPages: json["total_pages"],
      );

  Map<String, dynamic> toJson() => {
        "status": status,
        "success": success,
        "messageStatus": messageStatus,
        "results": List<dynamic>.from(results!.map((x) => x.toJson())),
        "total_results": totalResults,
        "total_pages": totalPages,
      };
}

class Result {
  Result({
    this.actors,
    this.directors,
    this.escritors,
    this.otherTitles,
    this.id,
    this.image,
    this.title,
    this.rating,
    this.year,
    this.titleOriginal,
    this.uuid,
    this.description,
    this.genres,
    this.countries,
    this.release,
    this.embedUrls,
    this.index,
    this.episodes,
    this.createdAt,
    this.updatedAt,
  });

  List<dynamic>? actors;
  List<dynamic>? directors;
  List<dynamic>? escritors;
  List<dynamic>? otherTitles;
  String? id;
  String? image;
  String? title;
  String? rating;
  String? year;
  String? titleOriginal;
  String? uuid;
  String? description;
  List<Country>? genres;
  List<Country>? countries;
  String? release;
  List<EmbedUrl>? embedUrls;
  int? index;
  List<dynamic>? episodes;
  DateTime? createdAt;
  DateTime? updatedAt;

  factory Result.fromJson(Map<String, dynamic> json) => Result(
        actors: List<dynamic>.from(json["actors"].map((x) => x)),
        directors: List<dynamic>.from(json["directors"].map((x) => x)),
        escritors: List<dynamic>.from(json["escritors"].map((x) => x)),
        otherTitles: List<dynamic>.from(json["otherTitles"].map((x) => x)),
        id: json["_id"],
        image: json["image"],
        title: json["title"],
        rating: json["rating"],
        year: json["year"],
        titleOriginal: json["titleOriginal"],
        uuid: json["uuid"],
        description: json["description"],
        genres:
            List<Country>.from(json["genres"].map((x) => Country.fromJson(x))),
        countries: List<Country>.from(
            json["countries"].map((x) => Country.fromJson(x))),
        release: json["release"],
        embedUrls: List<EmbedUrl>.from(
            json["embedUrls"].map((x) => EmbedUrl.fromJson(x))),
        index: json["index"],
        episodes: List<dynamic>.from(json["episodes"].map((x) => x)),
        createdAt: DateTime.parse(json["createdAt"]),
        updatedAt: DateTime.parse(json["updatedAt"]),
      );

  Map<String, dynamic> toJson() => {
        "actors": List<dynamic>.from(actors!.map((x) => x)),
        "directors": List<dynamic>.from(directors!.map((x) => x)),
        "escritors": List<dynamic>.from(escritors!.map((x) => x)),
        "otherTitles": List<dynamic>.from(otherTitles!.map((x) => x)),
        "_id": id,
        "image": image,
        "title": title,
        "rating": rating,
        "year": year,
        "titleOriginal": titleOriginal,
        "uuid": uuid,
        "description": description,
        "genres": List<dynamic>.from(genres!.map((x) => x.toJson())),
        "countries": List<dynamic>.from(countries!.map((x) => x.toJson())),
        "release": release,
        "embedUrls": List<dynamic>.from(embedUrls!.map((x) => x.toJson())),
        "index": index,
        "episodes": List<dynamic>.from(episodes!.map((x) => x)),
        "createdAt": createdAt?.toIso8601String(),
        "updatedAt": updatedAt?.toIso8601String(),
      };
}

class Country {
  Country({
    this.name,
    this.uuid,
  });

  String? name;
  String? uuid;

  factory Country.fromJson(Map<String, dynamic> json) => Country(
        name: json["name"],
        uuid: json["uuid"],
      );

  Map<String, dynamic> toJson() => {
        "name": name,
        "uuid": uuid,
      };
}

class EmbedUrl {
  EmbedUrl({
    this.server,
    this.url,
    this.priority,
  });

  String? server;
  String? url;
  int? priority;

  factory EmbedUrl.fromJson(Map<String, dynamic> json) => EmbedUrl(
        server: json["server"],
        url: json["url"],
        priority: json["priority"],
      );

  Map<String, dynamic> toJson() => {
        "server": server,
        "url": url,
        "priority": priority,
      };
}

Controller :

class HomeController extends GetxController {
  var m = Movies();
  var r = new Result();
  final provider = Get.put(MovieProvider());
  final count = 0.obs;
  @override
  void onInit() {
    provider.getStats();
    super.onInit();
  }

  @override
  void onReady() {
    super.onReady();
  }

  @override
  void onClose() {
    super.onClose();
  }

  void increment() => count.value++;
}

And as for my View I’ve been so far just testing to get the title of one of the movies as a Text, using :

Text(controller.r.titleOriginal[0])

But I get an error related to Null-safety,

enter image description here

I tried adding a ‘!’ in !titleOriginal, but this is what I go :

Exception has occurred.
_CastError (Null check operator used on a null value)

adding ‘?’ doesn’t work either.

Here’s also my View :

class DigiappstoreView extends GetView<DigiappstoreController> {
  const DigiappstoreView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Center(child: Text(controller.r.titleOriginal![0])));
  }
}

Solution

You are returning dynamic type from your provider. You should return a specific type. As per the code, it should return the type of Movie.

class MovieProvider extends GetConnect {
  @override
  var queryParameters = {
    'limit': '8',
    'genres': 'comedia',
    'sort': 'year',
    'type': 'movies'
  };
  static const String _baseUrl = 'movies-app1.p.rapidapi.com';
  void onInit() {
    httpClient.baseUrl = _baseUrl;
  }

  static const Map<String, String> _headers = {
    "x-rapidapi-key": "***************",
    "x-rapidapi-host": "movies-app1.p.rapidapi.com",
  };

  // Base API request to get response
  Future<Movie> getStats() async {
    Uri uri = Uri.https(_baseUrl, '/api/movies', queryParameters);
    final response = await http.get(uri, headers: _headers);
    print('provider IS WORKING');
    if (response.statusCode == 200) {
      return moviesFromJson(response.body);
    } else {
      throw Exception('Failed to load json data');
    }
  }
}

Also, in controller, you should assign the result to the variable like

class HomeController extends GetxController {
  var m = Movies();
  var r = Result();
  final provider = Get.put(MovieProvider());

  @override
  void onInit() {
    getStates();
    super.onInit();
  }
  
   getStates() async {
      movies = await provider.getStats();
      r = movies.results;
      update();
   }
}

Now

in your view, wrap the child inside GetBuilder to update the UI once the data is updated.

class DigiappstoreView extends GetView<DigiappstoreController> {
  const DigiappstoreView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(body: GetBuilder<DigiappstoreController>(
      init: DigiappstoreController(),
      builder: (controller) {
        return Center(child: Text(controller.r == null ? '' : controller.r[0].titleOriginal!)); 
      },
    ));
  }
}

There may be some syntax error as i have not tried the code myself but these are the updates that you need to do.

I think you need to read the GetX Documentation in detail to know how it works

Answered By – Suz Zan

Answer Checked By – Senaida (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *