diff --git a/lib/components/locale/l10n/app_locale.dart b/lib/components/locale/l10n/app_locale.dart index c819af2..0922eb1 100644 --- a/lib/components/locale/l10n/app_locale.dart +++ b/lib/components/locale/l10n/app_locale.dart @@ -82,8 +82,7 @@ abstract class AppLocale { /// Additional delegates can be added by appending to this list in /// MaterialApp. This list does not have to be used at all if a custom list /// of delegates is preferred or required. - static const List> localizationsDelegates = - >[ + static const List> localizationsDelegates = >[ delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, @@ -91,7 +90,10 @@ abstract class AppLocale { ]; /// A list of this localizations delegate's supported locales. - static const List supportedLocales = [Locale('en'), Locale('ru')]; + static const List supportedLocales = [ + Locale('en'), + Locale('ru') + ]; /// No description provided for @search. /// @@ -134,17 +136,18 @@ class _AppLocaleDelegate extends LocalizationsDelegate { } AppLocale lookupAppLocale(Locale locale) { + + // Lookup logic when only language code is specified. switch (locale.languageCode) { - case 'en': - return AppLocaleEn(); - case 'ru': - return AppLocaleRu(); + case 'en': return AppLocaleEn(); + case 'ru': return AppLocaleRu(); } throw FlutterError( - 'AppLocale.delegate failed to load unsupported locale "$locale". This is likely ' - 'an issue with the localizations generation tool. Please file an issue ' - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.'); + 'AppLocale.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.' + ); } diff --git a/lib/data/dtos/characters_dto.g.dart b/lib/data/dtos/characters_dto.g.dart index 6066b05..c4e1b40 100644 --- a/lib/data/dtos/characters_dto.g.dart +++ b/lib/data/dtos/characters_dto.g.dart @@ -6,22 +6,28 @@ part of 'characters_dto.dart'; // JsonSerializableGenerator // ************************************************************************** -CharactersDto _$CharactersDtoFromJson(Map json) => CharactersDto( +CharactersDto _$CharactersDtoFromJson(Map json) => + CharactersDto( data: (json['data'] as List?) ?.map((e) => CharacterDataDto.fromJson(e as Map)) .toList(), - meta: json['meta'] == null ? null : MetaDto.fromJson(json['meta'] as Map), + meta: json['meta'] == null + ? null + : MetaDto.fromJson(json['meta'] as Map), ); -CharacterDataDto _$CharacterDataDtoFromJson(Map json) => CharacterDataDto( +CharacterDataDto _$CharacterDataDtoFromJson(Map json) => + CharacterDataDto( id: json['id'] as String?, type: json['type'] as String?, attributes: json['attributes'] == null ? null - : CharacterAttributesDataDto.fromJson(json['attributes'] as Map), + : CharacterAttributesDataDto.fromJson( + json['attributes'] as Map), ); -CharacterAttributesDataDto _$CharacterAttributesDataDtoFromJson(Map json) => +CharacterAttributesDataDto _$CharacterAttributesDataDtoFromJson( + Map json) => CharacterAttributesDataDto( name: json['name'] as String?, born: json['born'] as String?, @@ -35,7 +41,8 @@ MetaDto _$MetaDtoFromJson(Map json) => MetaDto( : PaginationDto.fromJson(json['pagination'] as Map), ); -PaginationDto _$PaginationDtoFromJson(Map json) => PaginationDto( +PaginationDto _$PaginationDtoFromJson(Map json) => + PaginationDto( current: (json['current'] as num?)?.toInt(), next: (json['next'] as num?)?.toInt(), last: (json['last'] as num?)?.toInt(), diff --git a/lib/data/dtos/pokemon_dto.dart b/lib/data/dtos/pokemon_dto.dart index dcefbf6..de8e84e 100644 --- a/lib/data/dtos/pokemon_dto.dart +++ b/lib/data/dtos/pokemon_dto.dart @@ -4,56 +4,63 @@ part 'pokemon_dto.g.dart'; @JsonSerializable(createToJson: false) class PokemonDto { - final int id; - final String name; - final List types; - final PokemonSpritesDto sprites; + final List? results; + final int? count; + final String? next; + final String? previous; const PokemonDto({ - required this.id, - required this.name, - required this.types, - required this.sprites, + this.results, + this.count, + this.next, + this.previous, }); factory PokemonDto.fromJson(Map json) => _$PokemonDtoFromJson(json); } @JsonSerializable(createToJson: false) -class PokemonTypeDto { - final int slot; - final PokemonTypeDetailDto type; +class PokemonDataDto { + final String? name; + final String? url; - const PokemonTypeDto({ - required this.slot, - required this.type, + const PokemonDataDto({ + this.name, + this.url, }); - factory PokemonTypeDto.fromJson(Map json) => _$PokemonTypeDtoFromJson(json); + factory PokemonDataDto.fromJson(Map json) => _$PokemonDataDtoFromJson(json); } @JsonSerializable(createToJson: false) -class PokemonTypeDetailDto { - final String name; - final String url; +class PokemonDetailsDto { + final int? height; + final int? weight; + final List? abilities; - const PokemonTypeDetailDto({ - required this.name, - required this.url, + const PokemonDetailsDto({ + this.height, + this.weight, + this.abilities, }); - factory PokemonTypeDetailDto.fromJson(Map json) => - _$PokemonTypeDetailDtoFromJson(json); + factory PokemonDetailsDto.fromJson(Map json) => _$PokemonDetailsDtoFromJson(json); } @JsonSerializable(createToJson: false) -class PokemonSpritesDto { - final String front_default; +class AbilityDto { + final AbilityDetailDto? ability; - const PokemonSpritesDto({ - required this.front_default, - }); + const AbilityDto({this.ability}); - factory PokemonSpritesDto.fromJson(Map json) => - _$PokemonSpritesDtoFromJson(json); + factory AbilityDto.fromJson(Map json) => _$AbilityDtoFromJson(json); } + +@JsonSerializable(createToJson: false) +class AbilityDetailDto { + final String? name; + + const AbilityDetailDto({this.name}); + + factory AbilityDetailDto.fromJson(Map json) => _$AbilityDetailDtoFromJson(json); +} \ No newline at end of file diff --git a/lib/data/dtos/pokemon_dto.g.dart b/lib/data/dtos/pokemon_dto.g.dart index 8095bc9..7e7ce83 100644 --- a/lib/data/dtos/pokemon_dto.g.dart +++ b/lib/data/dtos/pokemon_dto.g.dart @@ -7,25 +7,36 @@ part of 'pokemon_dto.dart'; // ************************************************************************** PokemonDto _$PokemonDtoFromJson(Map json) => PokemonDto( - id: (json['id'] as num).toInt(), - name: json['name'] as String, - types: (json['types'] as List) - .map((e) => PokemonTypeDto.fromJson(e as Map)) + results: (json['results'] as List?) + ?.map((e) => PokemonDataDto.fromJson(e as Map)) .toList(), - sprites: PokemonSpritesDto.fromJson(json['sprites'] as Map), + count: (json['count'] as num?)?.toInt(), + next: json['next'] as String?, + previous: json['previous'] as String?, ); -PokemonTypeDto _$PokemonTypeDtoFromJson(Map json) => PokemonTypeDto( - slot: (json['slot'] as num).toInt(), - type: PokemonTypeDetailDto.fromJson(json['type'] as Map), +PokemonDataDto _$PokemonDataDtoFromJson(Map json) => + PokemonDataDto( + name: json['name'] as String?, + url: json['url'] as String?, ); -PokemonTypeDetailDto _$PokemonTypeDetailDtoFromJson(Map json) => - PokemonTypeDetailDto( - name: json['name'] as String, - url: json['url'] as String, +PokemonDetailsDto _$PokemonDetailsDtoFromJson(Map json) => + PokemonDetailsDto( + height: (json['height'] as num?)?.toInt(), + weight: (json['weight'] as num?)?.toInt(), + abilities: (json['abilities'] as List?) + ?.map((e) => AbilityDto.fromJson(e as Map)) + .toList(), ); -PokemonSpritesDto _$PokemonSpritesDtoFromJson(Map json) => PokemonSpritesDto( - front_default: json['front_default'] as String, +AbilityDto _$AbilityDtoFromJson(Map json) => AbilityDto( + ability: json['ability'] == null + ? null + : AbilityDetailDto.fromJson(json['ability'] as Map), + ); + +AbilityDetailDto _$AbilityDetailDtoFromJson(Map json) => + AbilityDetailDto( + name: json['name'] as String?, ); diff --git a/lib/data/mappers/pokemon_mapper.dart b/lib/data/mappers/pokemon_mapper.dart index 6c8e653..5b74a9b 100644 --- a/lib/data/mappers/pokemon_mapper.dart +++ b/lib/data/mappers/pokemon_mapper.dart @@ -1,17 +1,34 @@ import 'package:mobilki_lab1/data/dtos/pokemon_dto.dart'; import 'package:mobilki_lab1/domain/models/card.dart'; +import 'package:mobilki_lab1/domain/models/home.dart'; const _imagePlaceholder = 'https://upload.wikimedia.org/wikipedia/en/archive/b/b1/20210811082420%21Portrait_placeholder.png'; extension PokemonDtoToModel on PokemonDto { - CardData toDomain() => CardData( - name, - imageUrl: sprites.front_default ?? _imagePlaceholder, - descriptionText: _makeDescriptionText(types), - ); + HomeData toDomain() => HomeData( + data: results?.map((e) => e.toDomain()).toList(), + nextPage: next != null ? int.tryParse(next!.split('=')[1].split('&')[0]) : null, + ); +} - String _makeDescriptionText(List types) { - return 'Types: ${types.map((type) => type.type.name).join(', ')}'; +extension PokemonDataDtoToModel on PokemonDataDto { + CardData toDomain() { + final id = url?.split('/')[6] ?? 'UNKNOWN'; + final imageUrl = 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/$id.png'; + + return CardData( + name ?? 'UNKNOWN', + imageUrl: imageUrl, + descriptionText: 'ID: $id', + id: id, + ); } } + +extension PokemonDetailsDtoToModel on PokemonDetailsDto { + String toDescriptionText() { + final abilitiesText = abilities?.map((ability) => ability.ability?.name ?? 'UNKNOWN').join(', ') ?? 'UNKNOWN'; + return 'Height: ${height ?? 'UNKNOWN'} cm, Weight: ${weight ?? 'UNKNOWN'} kg, Abilities: $abilitiesText'; + } +} \ No newline at end of file diff --git a/lib/data/repositories/pokemon_repository.dart b/lib/data/repositories/pokemon_repository.dart index 8b13789..75e03ad 100644 --- a/lib/data/repositories/pokemon_repository.dart +++ b/lib/data/repositories/pokemon_repository.dart @@ -1 +1,72 @@ +import 'package:dio/dio.dart'; +import 'package:mobilki_lab1/data/dtos/pokemon_dto.dart'; +import 'package:mobilki_lab1/data/mappers/pokemon_mapper.dart'; +import 'package:mobilki_lab1/data/repositories/api_interface.dart'; +import 'package:mobilki_lab1/domain/models/home.dart'; +import 'package:pretty_dio_logger/pretty_dio_logger.dart'; +class PokeRepository extends ApiInterface { + static final Dio _dio = Dio() + ..interceptors.add(PrettyDioLogger( + requestHeader: true, + requestBody: true, + )); + + static const String _baseUrl = 'https://pokeapi.co/api/v2'; + + List _allPokemon = []; + bool _isAllPokemonLoaded = false; + + @override + Future loadData({ + OnErrorCallback? onError, + String? q, + int page = 1, + int pageSize = 25, + }) async { + try { + if (!_isAllPokemonLoaded) { + final String url = '$_baseUrl/pokemon'; + final Response response = await _dio.get>( + url, + queryParameters: { + 'offset': (page - 1) * pageSize, + 'limit': pageSize, + }, + ); + + final PokemonDto dto = PokemonDto.fromJson(response.data as Map); + _allPokemon = dto.results ?? []; + _isAllPokemonLoaded = true; + } + + final List filteredPokemon = q != null && q.isNotEmpty + ? _allPokemon.where((pokemon) => pokemon.name!.contains(q)).toList() + : _allPokemon; + + final int startIndex = (page - 1) * pageSize; + final int endIndex = startIndex + pageSize; + + if (startIndex >= filteredPokemon.length) { + return HomeData( + data: [], + nextPage: null, + ); + } + + final List paginatedPokemon = filteredPokemon.sublist( + startIndex, + endIndex > filteredPokemon.length ? filteredPokemon.length : endIndex, + ); + + final HomeData data = HomeData( + data: paginatedPokemon.map((e) => e.toDomain()).toList(), + nextPage: endIndex < filteredPokemon.length ? page + 1 : null, + ); + return data; + } on DioException catch (e) { + onError?.call(e.error?.toString()); + return null; + } + } +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index fafa3af..c570ad6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:mobilki_lab1/data/repositories/pokemon_repository.dart'; import 'package:mobilki_lab1/presentation/home_page/bloc/bloc.dart'; import 'package:mobilki_lab1/presentation/home_page/home_page.dart'; import 'package:mobilki_lab1/presentation/like_bloc/like_bloc.dart'; @@ -34,15 +35,15 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), - home: RepositoryProvider( + home: RepositoryProvider( lazy: true, - create: (_) => PotterRepository(), + create: (_) => PokeRepository(), child: BlocProvider( lazy: false, create: (context) => LikeBloc(), child: BlocProvider( lazy: false, - create: (context) => HomeBloc(context.read()), + create: (context) => HomeBloc(context.read()), child: const MyHomePage(title: 'Чубыкина Полина Павловна'), ), ), diff --git a/lib/presentation/home_page/bloc/bloc.dart b/lib/presentation/home_page/bloc/bloc.dart index 63b8f0f..44722fd 100644 --- a/lib/presentation/home_page/bloc/bloc.dart +++ b/lib/presentation/home_page/bloc/bloc.dart @@ -3,8 +3,11 @@ import 'package:mobilki_lab1/data/repositories/potter_repository.dart'; import 'package:mobilki_lab1/presentation/home_page/bloc/events.dart'; import 'package:mobilki_lab1/presentation/home_page/bloc/state.dart'; +import '../../../data/repositories/pokemon_repository.dart'; + class HomeBloc extends Bloc { - final PotterRepository repo; + // final PotterRepository repo; + final PokeRepository repo; HomeBloc(this.repo) : super(const HomeState()) { on(_onLoadData); diff --git a/lib/presentation/locale_bloc/locale_state.g.dart b/lib/presentation/locale_bloc/locale_state.g.dart index 23ee063..e374db6 100644 --- a/lib/presentation/locale_bloc/locale_state.g.dart +++ b/lib/presentation/locale_bloc/locale_state.g.dart @@ -27,7 +27,8 @@ class _$LocaleStateCWProxyImpl implements _$LocaleStateCWProxy { final LocaleState _value; @override - LocaleState currentLocale(Locale currentLocale) => this(currentLocale: currentLocale); + LocaleState currentLocale(Locale currentLocale) => + this(currentLocale: currentLocale); @override @@ -41,10 +42,11 @@ class _$LocaleStateCWProxyImpl implements _$LocaleStateCWProxy { Object? currentLocale = const $CopyWithPlaceholder(), }) { return LocaleState( - currentLocale: currentLocale == const $CopyWithPlaceholder() || currentLocale == null - ? _value.currentLocale - // ignore: cast_nullable_to_non_nullable - : currentLocale as Locale, + currentLocale: + currentLocale == const $CopyWithPlaceholder() || currentLocale == null + ? _value.currentLocale + // ignore: cast_nullable_to_non_nullable + : currentLocale as Locale, ); } }