데이터 소스(2) - 완전한 영화 응용 프로그램

안녕하세요, 오셔서 반갑습니다.
우리는 가장 좋은 인코딩 실천과 도구로 영화 응용 프로그램을 구축하고 있다.이전 영상에서 저는 우리의 폴더 구조가 어떻게 될지 설명했고 깨끗한 체계 구조 개념을 사용할 것입니다.
동영상 자습서의 부속 기사입니다.

TMDb API 키 만들기
영화를 보려면 TMDb API를 사용하므로 먼저 TMDb API 키를 만듭니다.https://www.themoviedb.org/login를 열고 사이트에 등록하거나 로그인합니다.
로그인하면 액세스https://www.themoviedb.org/settings/account를 통해 설정에 들어갈 수 있습니다.왼쪽 메뉴에서 API 섹션을 열고 API 키(v3 auth) 아래의 값을 복사합니다.또한 API 호출을 수행하려면 기본 URL이 필요하므로 예제 API 요청 URL을 읽어 보십시오.
프로젝트를 열고 api constants 파일을 생성합니다.데이터/core 폴더에 채널을 삽입하고 다음 코드를 배치합니다.
class ApiConstants { 
  //1
  ApiConstants._();

  //2
  static const String BASE_URL = "https://api.themoviedb.org/3/";
  //3
  static const String API_KEY = "f33521953035af3fc3162fe1ac22e60c";
  //4
  static const String BASE_IMAGE_URL = "https://image.tmdb.org/t/p/w500";
}
  • 이 클래스는 외부에서 실례화되어서는 안 되기 때문에 구조 함수를private라고 명시해야 한다.
  • 이 URL은 항상 특정 URL 앞에 있습니다.
  • 이 파일에 API 키를 넣습니다.나는 이 시리즈가 끝난 후에 그것을 거절할 수도 있기 때문에, 나는 네가 자신을 창조할 것을 건의한다.
  • TMDb API에서 얻은 이미지 경로의 형식은 zuW6fOiusv4X9nnW3paHGfXcSll입니다.jpg.그림을 불러오기 위해서 미리 설정해야 합니다. BASE_IMAGE_URL

  • TMDb API 응답
    https://developers.themoviedb.org/3/로 이동하고 왼쪽 메뉴에서 트렌드를 선택하세요.오른쪽에서 디테일을 볼 수 있습니다.너는 이것에 대해 더 많은 탐색을 할 수 있지만, 두 가지 매우 중요한 것은 media_typetime_window 이다.이것은 영화 프로그램이기 때문에 미디어 형식은 영화가 될 것입니다. 시간 창을 데이로 선택할 수 있습니다.평가판 탭을 열고 영화 제목media_type, 날짜 제목time_window을 선택한 다음 API 필드에 API 키를 넣습니다.이제 요청 보내기 버튼을 누르면 요청과 응답을 볼 수 있습니다.전체 URL을 읽고 기본 URL을 확인할 수 있습니다.
    이 응답을 복사하고dart모델을 만듭니다.https://javiercbk.github.io/json_to_dart/를 열고 응답을 텍스트 상자에 붙여넣습니다.클래스의 이름을 MoviesResultModel로 지정합니다.
    dart 파일\u 결과\u 모델을 생성합니다.data/models 폴더에dart를 삽입하고 jsonToDart에서 생성한 코드를 붙여넣습니다.만든 필드를 보십시오.생성된 클래스는 영화 목록이 있습니다.페이지 나누기를 보여 주지 않기 때문에 페이지, totalPages, totalResults와 같은 페이지와 관련된 필드를 삭제할 수 있습니다.코드를 더욱 간소화하기 위해 이 파일에서 Results 클래스를 삭제하고 movie model이라는 단독 파일을 만들 수 있습니다.데이터/모델을 빠르게 입력합니다.다음은 두 가지 과정입니다.
    class MoviesResultModel {
      final List<MovieModel> movies;
    
      MoviesResultModel({this.page, this.movies});
    
      factory MoviesResultModel.fromJson(Map<String, dynamic> json) {
        List tempMovies = new List<MovieModel>();
        if (json['results'] != null) {
          json['results'].forEach((v) {
            tempMovies.add(MovieModel.fromJson(v));
          });
        }
    
        return MoviesResultModel(movies: tempMovies);
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        if (this.movies != null) {
          data['results'] = this.movies.map((v) => v.toJson()).toList();
        }
        return data;
      }
    }
    
    class MovieModel extends MovieEntity {
      int id;
      bool video;
      int voteCount;
      double voteAverage;
      String title;
      String releaseDate;
      String originalLanguage;
      String originalTitle;
      List<int> genreIds;
      String backdropPath;
      bool adult;
      String overview;
      String posterPath;
      double popularity;
      String mediaType;
    
      const MovieModel({
        this.popularity,
        this.voteCount,
        this.video,
        this.posterPath,
        this.id,
        this.adult,
        this.backdropPath,
        this.originalLanguage,
        this.originalTitle,
        this.genreIds,
        this.title,
        this.voteAverage,
        this.overview,
        this.releaseDate,
        this.mediaType,
      });
    
      Results.fromJson(Map<String, dynamic> json) {
        id = json['id'];
        video = json['video'];
        voteCount = json['vote_count'];
        voteAverage = json['vote_average'];
        title = json['title'];
        releaseDate = json['release_date'];
        originalLanguage = json['original_language'];
        originalTitle = json['original_title'];
        genreIds = json['genre_ids'].cast<int>();
        backdropPath = json['backdrop_path'];
        adult = json['adult'];
        overview = json['overview'];
        posterPath = json['poster_path'];
        popularity = json['popularity'];
        mediaType = json['media_type'];
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['popularity'] = this.popularity;
        data['vote_count'] = this.voteCount;
        data['video'] = this.video;
        data['poster_path'] = this.posterPath;
        data['id'] = this.id;
        data['adult'] = this.adult;
        data['backdrop_path'] = this.backdropPath;
        data['original_language'] = this.originalLanguage;
        data['original_title'] = this.originalTitle;
        data['genre_ids'] = this.genreIds;
        data['title'] = this.title;
        data['vote_average'] = this.voteAverage;
        data['overview'] = this.overview;
        data['release_date'] = this.releaseDate;
        data['media_type'] = this.mediaType;
        return data;
      }
    }
    
    의존 항목 2개 추가 - http 및 equatable
    equatable: ^1.2.0
    http: ^0.12.1
    
    dart에서 같은 플러그인을 사용할 때 대상을 비교하는 것이 더 쉽다.HTTP 플러그인은 네트워크 호출에 사용됩니다.
    화면에 모든 필드를 동시에 사용할 수 없기 때문에 프로그램에서 필요한 필드를 고려해 보세요.필요한 필드는 주로 id,posterPath,backdropPath,title,voteAverage,releaseDate,overview입니다.일부 필드는 여전히 불필요하지만, 우리가 가장 좋아하는 영화를 제작할 때, 당신은 이 시리즈의 뒷부분에서 그것들을 사용해야 합니다.
    이제 domain/entities 폴더에 MovieEntity 클래스를 만들고 이 필드를 최종 필드로 선언합니다.Equatable로 이 종류를 확장합니다.id와 제목 필드를 사용하여 다시 쓰기 props 방법입니다.인쇄 대상에서 id와 제목을 볼 수 있도록 다시 쓰기 stringify 방법도 있습니다.
    MovieEntity 클래스는 다음과 같습니다.
    import 'package:equatable/equatable.dart';
    import 'package:flutter/foundation.dart';
    
    class MovieEntity extends Equatable {
      final String posterPath;
      final int id;
      final String backdropPath;
      final String title;
      final num voteAverage;
      final String releaseDate;
      final String overview;
    
      const MovieEntity({
        @required this.posterPath,
        @required this.id,
        @required this.backdropPath,
        @required this.title,
        @required this.voteAverage,
        @required this.releaseDate,
        this.overview,
      }) : assert(id != null, 'Movie id must not be null');
    
      @override
      List<Object> get props => [id, title];
    
      @override
      bool get stringify => true;
    }
    
    MovieModel와 MovieEntity를 정확하게 연결하고 깨끗한 구조를 실행하려면 MovieModel 확장MovieEntity을 사용하십시오.이것은 영역과 데이터 층을 분리하는 데 도움이 될 것이다.
    현재MovieModel가 확장MovieEntity되고 있으므로 변경해야 합니다.
  • 모든 필드를 MovieModel로 설정합니다.
  • final 방법을 공장 방법으로 업데이트하고 fromJson 대상과 새로운 값을 부여하지 않고 직접 되돌려줍니다.
  • 사용MovieModel 구조 함수는 super에 값을 부여한다.이렇게 하면 MovieEntity를 언제 MovieModel 실례로 변환하든지 간에 정확한 비공식 값을 가진 필수 필드를 얻을 수 있다.
  • 다음은 업데이트된 버전MovieEntity의 외관입니다.
    import '../../domain/entities/movie_entity.dart';
    
    class MovieModel extends MovieEntity {
      final double popularity;
      final int voteCount;
      final bool video;
      final String posterPath;
      final int id;
      final bool adult;
      final String backdropPath;
      final String originalLanguage;
      final String originalTitle;
      final List<int> genreIds;
      final String title;
      final num voteAverage;
      final String overview;
      final String releaseDate;
      final String mediaType;
    
      const MovieModel({
        this.popularity,
        this.voteCount,
        this.video,
        this.posterPath,
        this.id,
        this.adult,
        this.backdropPath,
        this.originalLanguage,
        this.originalTitle,
        this.genreIds,
        this.title,
        this.voteAverage,
        this.overview,
        this.releaseDate,
        this.mediaType,
      }) : super(
              id: id,
              title: title,
              backdropPath: backdropPath,
              posterPath: posterPath,
              releaseDate: releaseDate,
              voteAverage: voteAverage,
              overview: overview,
            );
    
      factory MovieModel.fromJson(Map<String, dynamic> json) {
        return MovieModel(
          popularity: json['popularity'],
          voteCount: json['vote_count'],
          video: json['video'],
          posterPath: json['poster_path'],
          id: json['id'],
          adult: json['adult'],
          backdropPath: json['backdrop_path'],
          originalLanguage: json['original_language'],
          originalTitle: json['original_title'],
          genreIds: json['genre_ids'].cast<int>(),
          title: json['title'],
          voteAverage: json['vote_average'],
          overview: json['overview'],
          releaseDate: json['release_date'],
          mediaType: json['media_type'],
        );
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['popularity'] = this.popularity ;
        data['vote_count'] = this.voteCount;
        data['video'] = this.video;
        data['poster_path'] = this.posterPath;
        data['id'] = this.id;
        data['adult'] = this.adult;
        data['backdrop_path'] = this.backdropPath;
        data['original_language'] = this.originalLanguage;
        data['original_title'] = this.originalTitle;
        data['genre_ids'] = this.genreIds;
        data['title'] = this.title;
        data['vote_average'] = this.voteAverage;
        data['overview'] = this.overview;
        data['release_date'] = this.releaseDate;
        data['media_type'] = this.mediaType;
        return data;
      }
    }
    

    When you're declaring any field as double in models, be completely sure that it'll always be returned as double. If it is returned as an int, convert library fails to parse it. Either you can declare this as num or use toDouble function with safe operators.

    json['popularity']?.toDouble() ?? 0.0
    

    This will parse int to double if int is returned from API. As well as, if it is returned as null, 0.0 will be the default value of popularity.

    Additionally, you can assign more default values to all the model fields.



    데이터 소스 만들기
    이제 모형과 실체를 채우기 위해 인터넷 전화에 집중합시다.

    Create a file movie_remote_data_source.dart in data/data_sources folder.

    Create an abstract class MovieRemoteDataSource with one function as of now. This method will call TMDb API for trending movies by day.


    abstract class MovieRemoteDataSource {
      Future<List<MovieModel>> getTrending();
    }
    
    같은 파일에 추상 클래스 추가
    class MovieRemoteDataSourceImpl extends MovieRemoteDataSource {
      @override
      Future<List<MovieModel>> getTrending() async {
        //TODO: Fetch Trending Movies
      }
    }
    
    
    맨 위에 마지막 필드 MovieModel 를 설명하고 http 패키지의 실례 _client 를 사용하여 MovieRemoteDataSource 의 구조 함수를 만듭니다.
    class MovieRemoteDataSourceImpl extends MovieRemoteDataSource {
      final Client _client;
    
      MovieRemoteDataSourceImpl(this._client);
    }
    

    다채로운 영화를 보다.
    현재, 우리는 Client 를 사용하여 유행 영화를 위해 get API를 호출할 것이다.
    @override
    Future<List<MovieModel>> getTrending() async {
      final response = await _client.get(
        //1
        '${ApiConstants.BASE_URL}trending/movie/day?api_key=${ApiConstants.API_KEY}',
        //2
        headers: {
          'Content-Type': 'application/json',
        },
      );
    
      //3
      if (response.statusCode == 200) {
        //4
        final responseBody =  json.decode(response.body);
        //5
        final movies = MoviesResultModel.fromJson(responseBody).movies;
        print(movies);
        //6
        return movies;
      } else {
        //7
        throw Exception(response.reasonPhrase);
      }
    }
    
  • URL 가져오기 경로 만들기 - https://api.themoviedb.org/3/trending/movie/day?api_key=f33521953035af3fc3162fe1ac22e60c.
  • TMDb API가 json 형식을 생성하므로 제목을 json으로 유지합니다.
  • 서비스가 성공적으로 응답했는지 확인합니다.
  • JSON 응답을 분석하고 바디를 가져옵니다.바디에는 모델을 작성할 정확한 JSON을 만들기 위해 json2Dart 도구에 복사하여 붙여넣을 수 있습니다.
  • 현재 _client 공장 방법으로 모델에 대한 JSON 응답을 분석할 것이다.영화 목록이 하나밖에 없기 때문에 해석한 후에 fromJson에서만 영화를 얻을 수 있습니다
  • 마지막으로 영화를 돌려줍니다.
  • 서비스 응답 시 오류 결과가 발생하면 데이터층 자체에서 이상을 던집니다.

  • GetTrending 함수 호출
    UI를 만들지 않기 전에, Google 네트워크 호출이 정상적으로 작동하고,main에서 직접 호출해서 원하는 결과를 제공할 수 있습니다.던지다.
    주관도를 열다.MovieResultModel를 호출하기 전에 다음 코드 세그먼트runApp(MyApp()) 함수를 호출할 수 있습니다.
    //1
    MovieRemoteDataSource dataSource = MovieRemoteDataSourceImpl(Client());
    //2
    dataSource.getTrending();
    
  • 실례화된 데이터 원본을 http 패키지에서 전달getTrending().
  • 호출 함수.프로그램을 실행하면 콘솔에서 각 영화Clientid가 포함된 영화 목록을 볼 수 있습니다. title에서 id와 제목을 전달했기 때문입니다.

  • 인기 영화 를 얻다
    API 호출을 빠르게 추가합니다.팝 영화를 얻기 위해 다른 API를 호출하는 getPopular 함수를 만듭니다.다음과 같이 추상 클래스에 props를 추가합니다.
    abstract class MovieRemoteDataSource {
      Future<List<MovieModel>> getTrending();
      Future<List<MovieModel>> getPopular();
    }
    
    집행getPopular() 방법은 다음과 같다.다행히도 URL 경로만 바뀌는 것이 유행하고 있다.
    @override
    Future<List<MovieModel>> getPopular() async {
      final response = await _client.get(
        '${ApiConstants.BASE_URL}movie/popular?api_key=${ApiConstants.API_KEY}',
        headers: {
          'Content-Type': 'application/json',
        },
      );
    
      if (response.statusCode == 200) {
        final responseBody =  json.decode(response.body);
        final movies = MoviesResultModel.fromJson(responseBody).movies;
        print(movies);
        return movies;
      } else {
        throw Exception(response.reasonPhrase);
      }
    }
    
    이 방법은main에서 호출getPopular()하는 방식으로 실행할 수 있습니다.던지다.

    핵심 API 클라이언트
    비록 지금까지 우리는 두 가지 방법만 추가했지만, 당신은 이미 중복된 코드를 보았을 것입니다.그래서 공공 코드를 단독 파일로 옮겨 봅시다.
    api 클라이언트를 만듭니다.데이터/코어를 삽입합니다.
    ApiClient 클래스에서 클라이언트를 고유한 최종 필드로 추가하고 이를 사용하여 구조 함수를 작성합니다.
    class ApiClient {
      final Client _client;
    
      ApiClient(this._client);
    }
    
    모든 종류의 모델을 되돌릴 수 있기 때문에dynamic로 되돌아오는 get 방법을 만듭니다.
    dynamic get(String path) async {
      final response = await _client.get(
        '${ApiConstants.BASE_URL}$path?api_key=${ApiConstants.API_KEY}',
        headers: {
          'Content-Type': 'application/json',
        },
      );
    
      if (response.statusCode == 200) {
        return json.decode(response.body);
      } else {
        throw Exception(response.reasonPhrase);
      }
    }
    
    우리는 이미 대부분의 코드를 이 방법으로 옮겼는데, 현재 우리는 매우 간단한 코드로 데이터 원본을 업데이트하여 실현할 수 있다.이제 클라이언트가 아닌 ApiClient가 사용됩니다.
    class MovieRemoteDataSourceImpl extends MovieRemoteDataSource {
      //1
      final ApiClient _client;
    
      MovieRemoteDataSourceImpl(this._client);
    
      @override
      Future<List<MovieModel>> getTrending() async {
        //2
        final response = await _client.get('trending/movie/day');
        return MoviesResultModel.fromJson(response).movies;
      }
    
      @override
      Future<List<MovieModel>> getPopular() async {
        //3
        final response = await _client.get('movie/popular');
        return MoviesResultModel.fromJson(response).movies;
      }
    
    }
    
  • 이제 클라이언트 대신 ApiClient를 사용합니다.
  • 트렌드 영화를 얻기 위해 필요한 경로를 사용하여 ApiClient에서 호출getTrending() 방법.
  • 유행 영화를 얻는 데 필요한 경로를 사용하여 ApiClient에서 호출get 방법을 사용합니다.
  • 이 모든 것은 인터넷 전화를 하기 위해서다.
    이 글은 최초로 발표되었다Medium
    만약 네가 이 문장 중의 어떤 것을 좋아한다면 나를 따라오는 것을 잊지 마라🙏🏻. 너는 나에게 연락해서 따라와도 된다. GitHub

    좋은 웹페이지 즐겨찾기