Flutter Bloc

김영진·2021년 4월 9일
1
post-thumbnail

최근 스타트업에 지원하였는데, Flutter BLOC 패턴을 사용한다 하여 공부를 진행하였다.

종류

  • Provider
  • Bloc
  • getX

Bloc

lib
|   main.dart
|
\---src
    |   app.dart
    |
    +---blocs
    |       movies_bloc.dart
    |
    +---models
    |       item_model.dart
    |
    +---resources
    |       movie_api_provider.dart
    |       repository.dart
    |
    \---ui
            moive_list.dart

의존성

  • http
  • RxDart
    - rxdart는 Stream데이터를 관리의 편리를 위해 만들어 졌다.

ui

  • ui적으로 보여지는 부분만 코딩
  • bloc을 통해 데이터를 가져옴
class MovieList extends StatelessWidget {
  
  Widget build(BuildContext context) {
    bloc.fetchAllMovies();   // 영화 데이터를 가져오는 메서드 실행
    return Scaffold(
      appBar: AppBar(
        title: Text('Popular Movies'),
      ),
      body: StreamBuilder(
        stream: bloc.allMovies,
        builder: (context, AsyncSnapshot<ItemModel> snapshot) {
          if (snapshot.hasData) {
            return buildList(snapshot);
          } else if (snapshot.hasError) {
            return Text(snapshot.error.toString());
          }
          return Center(child: CircularProgressIndicator());
        },
      ),
    );
  }

bloc

  • ui에 데이터를 전달하는 역할
rxdart에 대한 사용법은 추후에 다른 글에서 정리하고 Bloc에 대한 내용만 기재
import '../resources/repository.dart';
import 'package:rxdart/rxdart.dart';
import '../models/item_model.dart';

class MoviesBloc {
  final _repository = Repository();
  
  final _moviesFetcher = PublishSubject<ItemModel>();
  
  Stream<ItemModel> get allMovies => _moviesFetcher.stream;
  
  fetchAllMovies() async {
    ItemModel itemModel = await _repository.fetchAllMovies();
    _moviesFetcher.sink.add(itemModel);
  }
  dispose() {
    _moviesFetcher.close();
  }
}
final bloc = MoviesBloc();

resources

repository
MovieApiProvider
  • API,DB를 활용하여 데이터를 직접적으로 가져오고, Bloc에 넘겨줌
repository
import 'dart:async';
import 'movie_api_provider.dart';
import '../models/item_model.dart';

class Repository {
  final moviesApiProvider = MovieApiProvider();

  Future<ItemModel> fetchAllMovies() => moviesApiProvider.fetchMovieList();
}
movie_api_provider
import 'dart:async';
import 'package:http/http.dart' show Client;
import 'dart:convert';
import '../models/item_model.dart';

class MovieApiProvider {
  Client client = Client();
  final _apiKey = apikey;

  Future<ItemModel> fetchMovieList() async {
    print("entered");
    var url = Uri.https(
      'api.themoviedb.org',
      '3/movie/popular',
      {
        'q': '{http}',
        'api_key': _apiKey,
      },
    );
    final response = await client.get(url);
    print(response.body.toString());
    if (response.statusCode == 200) {
      // If the call to the server was successful, parse the JSON
      return ItemModel.fromJson(json.decode(response.body));
    } else {
      // If that call was not successful, throw an error.
      throw Exception('Failed to load post');
    }
  }
}

Models

  • 데이터, getter, setter를 정의함
class ItemModel {
  int _page;
  int _total_results;
  int _total_pages;
  List<_Result> _results = [];

  ItemModel.fromJson(Map<String, dynamic> parsedJson) {
    print(parsedJson['results'].length);
    _page = parsedJson['page'];
    _total_results = parsedJson['total_results'];
    _total_pages = parsedJson['total_pages'];
    List<_Result> temp = [];
    for (int i = 0; i < parsedJson['results'].length; i++) {
      _Result result = _Result(parsedJson['results'][i]);
      temp.add(result);
    }
    _results = temp;
  }

  List<_Result> get results => _results;

  int get total_pages => _total_pages;

  int get total_results => _total_results;

  int get page => _page;
}

class _Result {
  int _vote_count;
  int _id;
  bool _video;
  var _vote_average;
  String _title;
  double _popularity;
  String _poster_path;
  String _original_language;
  String _original_title;
  List<int> _genre_ids = [];
  String _backdrop_path;
  bool _adult;
  String _overview;
  String _release_date;

  _Result(result) {
    _vote_count = result['vote_count'];
    _id = result['id'];
    _video = result['video'];
    _vote_average = result['vote_average'];
    _title = result['title'];
    _popularity = result['popularity'];
    _poster_path = result['poster_path'];
    _original_language = result['original_language'];
    _original_title = result['original_title'];
    for (int i = 0; i < result['genre_ids'].length; i++) {
      _genre_ids.add(result['genre_ids'][i]);
    }
    _backdrop_path = result['backdrop_path'];
    _adult = result['adult'];
    _overview = result['overview'];
    _release_date = result['release_date'];
  }

  String get release_date => _release_date;

  String get overview => _overview;

  bool get adult => _adult;

  String get backdrop_path => _backdrop_path;

  List<int> get genre_ids => _genre_ids;

  String get original_title => _original_title;

  String get original_language => _original_language;

  String get poster_path => _poster_path;

  double get popularity => _popularity;

  String get title => _title;

  double get vote_average => _vote_average;

  bool get video => _video;

  int get id => _id;

  int get vote_count => _vote_count;
}

회고

  • 데이터를 처리하는 blocs와 resources를 분리하는 이유가 궁금했음
  • 로컬DB나 네이티브 API을 이용해서 제공받을때도 있기때문에
  • Bloc에게 Model을 제공하는 부분을 분리하는게 좋다.

    Provider vs Bloc

    프로바이더가 직관적이고 사용이 더 용이한것 같음
    Bloc은 세심한 컨트롤이 필요한 대규모 프로젝트에 더 적합한듯 하다.

profile
2021.05.03) Flutter, BlockChain, Sports, StartUp

0개의 댓글