월루를 꿈꾸는 대학생

[flutter] Provider 응용 (영화 앱) 본문

Programing/플러터

[flutter] Provider 응용 (영화 앱)

하즈시 2022. 12. 12. 18:41
728x90

 

먼저 api가 필요함 

 

https://www.themoviedb.org/

 

The Movie Database (TMDB)

Welcome. Millions of movies, TV shows and people to discover. Explore now.

www.themoviedb.org

 

TMDB에서 회원가입을 한 후 

 

api 문서 홈페이지 접속 

 

https://developers.themoviedb.org/3/movies/get-popular-movies

 

API Docs

 

developers.themoviedb.org

 

유명한 영화 순위를 받아 올거니까 해당 api를 사용한다 

 

postman으로 리퀘스트 보내보고 확인 

 

 

 

 

 


네비게이션 바텀 바 만들 때를 위해서 provider 가 하나 더 필요해짐 

 

import 'package:flutter/material.dart';

class BottomNavigataionProvider extends ChangeNotifier {
  int _index = 0;

  int get currentPage => _index;

  //1페이지인지 2페이지인지 파라미터로 받은 후 _index 값 변경하고 하위 위젯들에게 통보
  updateCurrentPage(int index) {
    _index = index;
    notifyListeners();
  }
}

 

원래는 하나의 프로바이더만 썼었는데 2개가 필요하니 변경 

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: ChangeNotifierProvider(
      // child 하위 모든 위젯들은 create에서 만든 객체에 접근이 가능
      create: (BuildContext context) =>
          CountProvider() /*{
        return CountProvider();
      }*/
      ,
      child: Home(),
    ),
  );
}

 

변경 후 

 

home: MultiProvider(
  // [] 안에 있는 리스트들은 하위 위젯이 참고 가능 
  providers: [
    ChangeNotifierProvider(
        create: (BuildContext context) => CountProvider()),
    ChangeNotifierProvider(
        create: (BuildContext context) => BottomNavigataionProvider()),
  ],
  
  child: Home(),
),

 

 

하단 네비게이션 바 

Widget _bottomNavigationBarWidget() {
  return Consumer<BottomNavigationProvider>(
      builder: (context, provider, widget) {
    return BottomNavigationBar(
      items: [
        BottomNavigationBarItem(icon: Icon(Icons.home), label: 'home'),
        BottomNavigationBarItem(icon: Icon(Icons.movie), label: 'movie'),
      ], // 현재 선택은 0으로 초기화
      currentIndex: _bottomNavigationProvider!.currentPage,
      selectedItemColor: Colors.red,
      onTap: (index) {
        // provider navigation state변경 ;
        // 짜피 빌드 단에서 널체크 하니까 !
        _bottomNavigationProvider!.updateCurrentPage(index);
      },
    );
  });
}

컨슈머로 해당 위젯만 재빌드

 

상황에 따라 listen false를 쓸지 Consumer를 쓸지 선택해서 사용하면 됨 

 

하단 네비게이션바에서 prvider index를 변경하면 이 변경한 걸 바라보는 위젯이 화면을 재빌드 

 

Widget _navigationBody() {
  // 해당 Home클래스 내부에 _bottomNavigationProvider으로 멤버 변수가 있으니까 어떤 위젯에서도 가져와서 사용이 가능
  // 훨씬 편하네 ;;
  switch(_bottomNavigationProvider!.currentPage){
    case 0:
      return CountHomeWidget();
      break;
    case 1:
      return MovieListWidget();
      break;
  }
  return Container();
}

 

영화 사이트에서 데이터를 받고 이를 활용할 모델 만들기 

 

// api 요청에 의해 받은 값의 형태를 정의
// 이미지 poster_path, 제목 title, 설명이 필요 overview
class Movie {
  String? overview;
  String? posterPath;
  String? title;

  Movie({this.overview, this.posterPath, this.title});

  //json으로 들어온 거를 파싱할 필요가 있음
  //map 타입으로 들어옴
  //factory 패턴
  factory Movie.fromJson(Map<String, dynamic> json) {
    // movie 객체 생성
    return Movie(
      // json 형태에서 overview로 파싱하고 문자열로 넣음
      overview:json["overview"] as String,
      posterPath:json["poster_path"] as String,
      title:json["title"] as String,
    );
  }

  String get posterUrl => "https://image.tmdb.org/t/p/w500/${this.posterPath}";
}

 

api 리퀘스트 후 값을 받아서 리스트로 리턴할 레파지토리 

// api 서버 호출한 값을 받아오는 부분
import 'dart:convert';

import 'package:movie_app/const/api.dart';

import '../model/movie.dart';
import 'package:http/http.dart' as http;

class MovieRepository {
  Future<List<Movie>> loadMovies() async {
    var queryPram = {'api_key': '$apiKey'};

    /// var uri = Uri.https('example.org', '/path', {'q': 'dart'});
    /// print(uri); // https://example.org/path?q=dart

    var uri =
        Uri.https('api.themoviedb.org', '3/movie/popular', queryPram);
    var response = await http.get(uri);
    // 출력값이 있고
    if (response.body != null) {
      // 맵형태로 디코딩
      Map<String, dynamic> body = json.decode(response.body);
      // 결과값 있을 때
      if (body["results"] != null) {
        // map 으로 돌릴려면 개체로
        List<dynamic> list = body["results"];

        return list.map((e) => Movie.fromJson(e)).toList();
      }
    }
    // 값 못 받은 경우 빈걸로
    return [];
  }
}

 

이를 프로바이더에서 movieRepository를 사용해서 사용할 데이터를 가공한 후 프로바이더를 바라보는 위젯이 이를 사요할 수 있도록 

 

class MovieProvider extends ChangeNotifier {

  MovieRepository _movieRepository = MovieRepository();

  // response가 리스트 형태
  List<Movie> _movies = [];
  List<Movie> get movies => _movies;

  loadMovies() async{
    //repository 접근해서 데이터 불러오기
    List<Movie> listMovies = await _movieRepository.loadMovies();
    _movies=listMovies;
    notifyListeners();
  }


}

 

프로바이더의 데이터를 참조해서 화면을 빌드 

body: Consumer<MovieProvider>(
  builder: (context, provider, widget) {
    if (provider.movies != null && provider.movies.length > 0) {
      print(provider.movies.toString());
      // 프로바이더에 있는 리스트를 넘김
      return _makeListView(provider.movies);
    }

    //loadMovies 이거 호출을 안 한경우 null이니까 로딩바
    return Center(
      child: CircularProgressIndicator(),
    );
  },
),

 

 

 



주석 정리 및 전체 코드 

https://github.com/suhyun96/Flutter/tree/main/provider/movie_app

 

GitHub - suhyun96/Flutter: Flutter 공부 정리용

Flutter 공부 정리용 . Contribute to suhyun96/Flutter development by creating an account on GitHub.

github.com

 

출처 

https://www.youtube.com/watch?v=Mf9eDfi-VhU&t=320s 

 

728x90

'Programing > 플러터' 카테고리의 다른 글

[Firebase] Flutter Firebase 연동하기  (0) 2022.12.15
[firebase] 파이어베이스 개념 정리 / 세팅  (0) 2022.12.14
[flutter] Provider 개념정리 - Count세기  (0) 2022.12.12
[Flutter]Provider  (0) 2022.12.12
MVVM 패턴  (0) 2022.12.08