Compare commits
3 Commits
b19fdeda6d
...
4eb97b0f84
Author | SHA1 | Date | |
---|---|---|---|
4eb97b0f84 | |||
7176854e2c | |||
082474d6d8 |
44
lib/data/dtos/films_dto.dart
Normal file
44
lib/data/dtos/films_dto.dart
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'films_dto.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable(createToJson: false)
|
||||||
|
class FilmsDto {
|
||||||
|
final int total;
|
||||||
|
final List<FilmDataDto> items;
|
||||||
|
|
||||||
|
|
||||||
|
const FilmsDto({
|
||||||
|
required this.total,
|
||||||
|
required this.items,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory FilmsDto.fromJson(Map<String, dynamic> json) => _$FilmsDtoFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(createToJson: false)
|
||||||
|
class FilmDataDto {
|
||||||
|
final String nameRu;
|
||||||
|
final int year;
|
||||||
|
final List<GenreDto> genres;
|
||||||
|
final String posterUrl;
|
||||||
|
|
||||||
|
const FilmDataDto({
|
||||||
|
required this.nameRu,
|
||||||
|
required this.year,
|
||||||
|
required this.genres,
|
||||||
|
required this.posterUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory FilmDataDto.fromJson(Map<String, dynamic> json) => _$FilmDataDtoFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(createToJson: false)
|
||||||
|
class GenreDto {
|
||||||
|
final String genre;
|
||||||
|
|
||||||
|
const GenreDto({required this.genre});
|
||||||
|
|
||||||
|
factory GenreDto.fromJson(Map<String, dynamic> json) => _$GenreDtoFromJson(json);
|
||||||
|
}
|
||||||
|
|
27
lib/data/dtos/films_dto.g.dart
Normal file
27
lib/data/dtos/films_dto.g.dart
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'films_dto.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
FilmsDto _$FilmsDtoFromJson(Map<String, dynamic> json) => FilmsDto(
|
||||||
|
total: (json['total'] as num).toInt(),
|
||||||
|
items: (json['items'] as List<dynamic>)
|
||||||
|
.map((e) => FilmDataDto.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
FilmDataDto _$FilmDataDtoFromJson(Map<String, dynamic> json) => FilmDataDto(
|
||||||
|
nameRu: json['nameRu'] as String,
|
||||||
|
year: (json['year'] as num).toInt(),
|
||||||
|
genres: (json['genres'] as List<dynamic>)
|
||||||
|
.map((e) => GenreDto.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
posterUrl: json['posterUrl'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
GenreDto _$GenreDtoFromJson(Map<String, dynamic> json) => GenreDto(
|
||||||
|
genre: json['genre'] as String,
|
||||||
|
);
|
27
lib/data/mappers/films_mapper.dart
Normal file
27
lib/data/mappers/films_mapper.dart
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:project1/data/dtos/films_dto.dart';
|
||||||
|
import 'package:project1/domain/models/card.dart';
|
||||||
|
import 'package:project1/domain/models/home.dart';
|
||||||
|
|
||||||
|
const _imagePlaceholder =
|
||||||
|
'https://upload.wikimedia.org/wikipedia/en/archive/b/b1/20210811082420%21Portrait_placeholder.png';
|
||||||
|
|
||||||
|
extension FilmsDtoToModel on FilmsDto {
|
||||||
|
HomeData toDomain() => HomeData(
|
||||||
|
data: items.map((e) => e.toDomain()).toList(),
|
||||||
|
nextPage: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FilmDataDtoToModel on FilmDataDto {
|
||||||
|
CardData toDomain() => CardData(
|
||||||
|
nameRu,
|
||||||
|
imageUrl: posterUrl ?? _imagePlaceholder,
|
||||||
|
descriptionText: _makeDescriptionText(year, genres),
|
||||||
|
);
|
||||||
|
|
||||||
|
String _makeDescriptionText(int? year, List<GenreDto> genres) {
|
||||||
|
final yearText = year != null ? 'Год выхода: $year\n' : '';
|
||||||
|
final genresText = genres.isNotEmpty ? 'Жанр: ${genres.map((g) => g.genre).join(', ')}' : '';
|
||||||
|
return [yearText, genresText].where((s) => s.isNotEmpty).join();
|
||||||
|
}
|
||||||
|
}
|
90
lib/data/repositories/films_repository.dart
Normal file
90
lib/data/repositories/films_repository.dart
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||||
|
import 'package:project1/data/dtos/films_dto.dart';
|
||||||
|
import 'package:project1/data/mappers/films_mapper.dart';
|
||||||
|
import 'package:project1/domain/models/home.dart';
|
||||||
|
|
||||||
|
import 'api_interface.dart';
|
||||||
|
|
||||||
|
class FilmsRepository extends ApiInterface {
|
||||||
|
static final Dio _dio = Dio()
|
||||||
|
..interceptors.add(PrettyDioLogger(
|
||||||
|
requestHeader: true,
|
||||||
|
requestBody: true,
|
||||||
|
));
|
||||||
|
|
||||||
|
static const String _baseUrl = 'https://kinopoiskapiunofficial.tech';
|
||||||
|
static const String _apiKey = '67c830e4-b979-48ba-903d-a00c8f96fd4b';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<HomeData?> loadData({
|
||||||
|
OnErrorCallback? onError,
|
||||||
|
String? q,
|
||||||
|
int page = 1,
|
||||||
|
int pageSize = 7,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
const String url = '$_baseUrl/api/v2.2/films/premieres';
|
||||||
|
|
||||||
|
final List<String> months = [
|
||||||
|
'JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE',
|
||||||
|
'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER'
|
||||||
|
];
|
||||||
|
|
||||||
|
final List<FilmDataDto> allFilms = [];
|
||||||
|
|
||||||
|
for (final month in months) {
|
||||||
|
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
|
||||||
|
url,
|
||||||
|
queryParameters: {
|
||||||
|
'year': DateTime.now().year, // Используем текущий год
|
||||||
|
'month': month,
|
||||||
|
'page': page,
|
||||||
|
'pageSize': pageSize,
|
||||||
|
},
|
||||||
|
options: Options(
|
||||||
|
headers: {
|
||||||
|
'X-API-KEY': _apiKey,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final FilmsDto dto = FilmsDto.fromJson(response.data as Map<String, dynamic>);
|
||||||
|
allFilms.addAll(dto.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
final HomeData data = HomeData(
|
||||||
|
data: allFilms.map((e) => e.toDomain()).toList(),
|
||||||
|
nextPage: null, // Если нужно добавить поддержку пагинации, нужно будет добавить соответствующие поля в HomeData
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} on DioException catch (e) {
|
||||||
|
onError?.call(e.error?.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FilmDataDto?> getFilmById(int filmId) async {
|
||||||
|
try {
|
||||||
|
final String url = '$_baseUrl/api/v2.2/films/$filmId';
|
||||||
|
|
||||||
|
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
|
||||||
|
url,
|
||||||
|
options: Options(
|
||||||
|
headers: {
|
||||||
|
'X-API-KEY': _apiKey,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final FilmDataDto dto = FilmDataDto.fromJson(response.data as Map<String, dynamic>);
|
||||||
|
return dto;
|
||||||
|
} on DioException catch (e) {
|
||||||
|
print('Request failed with error: ${e.error}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ class PotterRepository extends ApiInterface {
|
|||||||
OnErrorCallback? onError,
|
OnErrorCallback? onError,
|
||||||
String? q,
|
String? q,
|
||||||
int page = 1,
|
int page = 1,
|
||||||
int pageSize = 5,
|
int pageSize = 7,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
const String url = '$_baseUrl/v1/characters';
|
const String url = '$_baseUrl/v1/characters';
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:project1/presentation/home_page/home_page.dart';
|
import 'package:project1/presentation/home_page/home_page.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:project1/presentation/home_page/bloc/bloc.dart';
|
import 'package:project1/presentation/home_page/bloc/bloc.dart';
|
||||||
import 'data/repositories/potter_repository.dart';
|
import 'data/repositories/films_repository.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
@ -20,12 +20,12 @@ class MyApp extends StatelessWidget {
|
|||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: RepositoryProvider<PotterRepository>(
|
home: RepositoryProvider<FilmsRepository>(
|
||||||
lazy: true,
|
lazy: true,
|
||||||
create: (_) => PotterRepository(),
|
create: (_) => FilmsRepository(),
|
||||||
child: BlocProvider<HomeBloc>(
|
child: BlocProvider<HomeBloc>(
|
||||||
lazy: false,
|
lazy: false,
|
||||||
create: (context) => HomeBloc(context.read<PotterRepository>()),
|
create: (context) => HomeBloc(context.read<FilmsRepository>()),
|
||||||
child: const MyHomePage(title: 'Булатова Каринa Раилевна'),
|
child: const MyHomePage(title: 'Булатова Каринa Раилевна'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:project1/data/repositories/potter_repository.dart';
|
import 'package:project1/data/repositories/films_repository.dart';
|
||||||
import 'package:project1/presentation/home_page/bloc/events.dart';
|
import 'package:project1/presentation/home_page/bloc/events.dart';
|
||||||
import 'package:project1/presentation/home_page/bloc/state.dart';
|
import 'package:project1/presentation/home_page/bloc/state.dart';
|
||||||
|
|
||||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||||
final PotterRepository repo;
|
final FilmsRepository repo;
|
||||||
|
|
||||||
HomeBloc(this.repo) : super(const HomeState()) {
|
HomeBloc(this.repo) : super(const HomeState()) {
|
||||||
on<HomeLoadDataEvent>(_onLoadData);
|
on<HomeLoadDataEvent>(_onLoadData);
|
||||||
|
@ -51,7 +51,7 @@ class BodyState extends State<Body> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onNextPageListener() {
|
void _onNextPageListener() {
|
||||||
if (scrollController.offset > scrollController.position.maxScrollExtent) {
|
if (scrollController.offset >= scrollController.position.maxScrollExtent) {
|
||||||
final bloc = context.read<HomeBloc>();
|
final bloc = context.read<HomeBloc>();
|
||||||
if (!bloc.state.isPaginationLoading) {
|
if (!bloc.state.isPaginationLoading) {
|
||||||
bloc.add(HomeLoadDataEvent(
|
bloc.add(HomeLoadDataEvent(
|
||||||
|
Loading…
Reference in New Issue
Block a user