Design fix
This commit is contained in:
parent
ff4530dae3
commit
cc63344d86
@ -1,7 +1,6 @@
|
||||
|
||||
import 'package:first_project/Components/locale/l10n/app_locale.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
extension LocalContextX on BuildContext {
|
||||
AppLocale get locale => AppLocale.of(this)!;
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,8 @@ 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<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
|
||||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
||||
<LocalizationsDelegate<dynamic>>[
|
||||
delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
@ -90,10 +91,7 @@ abstract class AppLocale {
|
||||
];
|
||||
|
||||
/// A list of this localizations delegate's supported locales.
|
||||
static const List<Locale> supportedLocales = <Locale>[
|
||||
Locale('en'),
|
||||
Locale('ru')
|
||||
];
|
||||
static const List<Locale> supportedLocales = <Locale>[Locale('en'), Locale('ru')];
|
||||
|
||||
/// No description provided for @search.
|
||||
///
|
||||
@ -136,18 +134,17 @@ class _AppLocaleDelegate extends LocalizationsDelegate<AppLocale> {
|
||||
}
|
||||
|
||||
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.');
|
||||
}
|
||||
|
@ -10,8 +10,11 @@ class Debouce {
|
||||
|
||||
static Timer? _timer;
|
||||
|
||||
static void run(VoidCallback action, {Duration delay = const Duration(milliseconds: 500),}) {
|
||||
static void run(
|
||||
VoidCallback action, {
|
||||
Duration delay = const Duration(milliseconds: 500),
|
||||
}) {
|
||||
_timer?.cancel();
|
||||
_timer = Timer(delay, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,10 +44,10 @@ class AnimeImagesJpgDto {
|
||||
final String? largeImage;
|
||||
|
||||
const AnimeImagesJpgDto(this.image, this.smallImage, this.largeImage);
|
||||
factory AnimeImagesJpgDto.fromJson(Map<String, dynamic> json) => _$AnimeImagesJpgDtoFromJson(json);
|
||||
factory AnimeImagesJpgDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$AnimeImagesJpgDtoFromJson(json);
|
||||
}
|
||||
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class PaginationDto {
|
||||
@JsonKey(name: "last_visible_page")
|
||||
@ -59,4 +59,4 @@ class PaginationDto {
|
||||
|
||||
const PaginationDto({this.current, this.last, this.next});
|
||||
factory PaginationDto.fromJson(Map<String, dynamic> json) => _$PaginationDtoFromJson(json);
|
||||
}
|
||||
}
|
||||
|
@ -25,22 +25,19 @@ AnimeDataDto _$AnimeDataDtoFromJson(Map<String, dynamic> json) => AnimeDataDto(
|
||||
(json['score'] as num?)?.toDouble(),
|
||||
);
|
||||
|
||||
AnimeImagesDto _$AnimeImagesDtoFromJson(Map<String, dynamic> json) =>
|
||||
AnimeImagesDto(
|
||||
AnimeImagesDto _$AnimeImagesDtoFromJson(Map<String, dynamic> json) => AnimeImagesDto(
|
||||
jpg: json['jpg'] == null
|
||||
? null
|
||||
: AnimeImagesJpgDto.fromJson(json['jpg'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
AnimeImagesJpgDto _$AnimeImagesJpgDtoFromJson(Map<String, dynamic> json) =>
|
||||
AnimeImagesJpgDto(
|
||||
AnimeImagesJpgDto _$AnimeImagesJpgDtoFromJson(Map<String, dynamic> json) => AnimeImagesJpgDto(
|
||||
json['image_url'] as String?,
|
||||
json['small_image_url'] as String?,
|
||||
json['large_image_url'] as String?,
|
||||
);
|
||||
|
||||
PaginationDto _$PaginationDtoFromJson(Map<String, dynamic> json) =>
|
||||
PaginationDto(
|
||||
PaginationDto _$PaginationDtoFromJson(Map<String, dynamic> json) => PaginationDto(
|
||||
current: (json['current_page'] as num?)?.toInt(),
|
||||
last: (json['last_visible_page'] as num?)?.toInt(),
|
||||
next: json['has_next_page'] as bool?,
|
||||
|
@ -4,17 +4,19 @@ import 'package:first_project/presentation/home_page/home_page.dart';
|
||||
|
||||
extension AnimeDataDtoToModel on AnimeDataDto {
|
||||
CardData toDomain() => CardData(
|
||||
title ?? 'NOT',
|
||||
imageUrl: images?.jpg?.image ?? "NONE",
|
||||
score: score ?? 0,
|
||||
description: synopsis == null ? "NONE" : synopsis!.split('\n').sublist(0, synopsis!.split('\n').length - 1).join('\n'),
|
||||
id: id.toString(),
|
||||
);
|
||||
title ?? 'NOT',
|
||||
imageUrl: images?.jpg?.image ?? "NONE",
|
||||
score: score ?? 0,
|
||||
description: synopsis == null
|
||||
? "NONE"
|
||||
: synopsis!.split('\n').sublist(0, synopsis!.split('\n').length - 1).join('\n'),
|
||||
id: id.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
extension AnimesDataDtoToModel on AnimesDto {
|
||||
HomeData toDomain() => HomeData(
|
||||
data: data?.map((e) => e.toDomain()).toList(),
|
||||
nextPage: pagination!.next! ? pagination!.current! + 1 : 0,
|
||||
);
|
||||
}
|
||||
data: data?.map((e) => e.toDomain()).toList(),
|
||||
nextPage: pagination!.next! ? pagination!.current! + 1 : 0,
|
||||
);
|
||||
}
|
||||
|
@ -8,31 +8,29 @@ import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
|
||||
class AnimeRepository extends ApiInterface {
|
||||
static final Dio _dio = Dio()
|
||||
..interceptors.add(PrettyDioLogger(
|
||||
requestHeader: true,
|
||||
requestBody: true,
|
||||
));
|
||||
..interceptors.add(PrettyDioLogger(
|
||||
requestHeader: true,
|
||||
requestBody: true,
|
||||
));
|
||||
|
||||
static const String _baseUrl = 'https://api.jikan.moe';
|
||||
|
||||
@override
|
||||
Future<HomeData?> loadData({OnErrorCallback? onError,String? q, int page = 1, int pageSize = 25}) async{
|
||||
try
|
||||
{
|
||||
const String url = '$_baseUrl/v4/anime';
|
||||
Map<String, dynamic> query = {'q' : q, 'page' : page, 'limit' : pageSize};
|
||||
final Response<dynamic> response =
|
||||
await _dio.get<Map<dynamic, dynamic>>(
|
||||
url,
|
||||
queryParameters: query,
|
||||
);
|
||||
final AnimesDto dto = AnimesDto.fromJson(response.data as Map<String, dynamic>);
|
||||
final HomeData data = dto.toDomain();
|
||||
return data;
|
||||
} on DioException catch (e)
|
||||
{
|
||||
Future<HomeData?> loadData(
|
||||
{OnErrorCallback? onError, String? q, int page = 1, int pageSize = 25}) async {
|
||||
try {
|
||||
const String url = '$_baseUrl/v4/anime';
|
||||
Map<String, dynamic> query = {'q': q, 'page': page, 'limit': pageSize};
|
||||
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
|
||||
url,
|
||||
queryParameters: query,
|
||||
);
|
||||
final AnimesDto dto = AnimesDto.fromJson(response.data as Map<String, dynamic>);
|
||||
final HomeData data = dto.toDomain();
|
||||
return data;
|
||||
} on DioException catch (e) {
|
||||
onError?.call(e.error?.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:first_project/domain/models/home.dart';
|
||||
import 'package:first_project/presentation/home_page/home_page.dart';
|
||||
|
||||
@ -6,4 +5,4 @@ typedef OnErrorCallback = void Function(String? error);
|
||||
|
||||
abstract class ApiInterface {
|
||||
Future<HomeData?> loadData({OnErrorCallback? onError});
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
||||
class MockRepository extends ApiInterface {
|
||||
@override
|
||||
Future<HomeData?> loadData({OnErrorCallback? onError}) async {
|
||||
return HomeData(data:[
|
||||
return HomeData(data: [
|
||||
const CardData(
|
||||
"First",
|
||||
score: 5,
|
||||
@ -17,8 +17,7 @@ class MockRepository extends ApiInterface {
|
||||
score: 8,
|
||||
icon: Icons.gamepad,
|
||||
description: "ManyText",
|
||||
imageUrl:
|
||||
"https://i.pinimg.com/originals/21/73/24/217324138d1bbc91663d4943ebe5de60.jpg",
|
||||
imageUrl: "https://i.pinimg.com/originals/21/73/24/217324138d1bbc91663d4943ebe5de60.jpg",
|
||||
),
|
||||
const CardData(
|
||||
"Third",
|
||||
@ -28,4 +27,4 @@ class MockRepository extends ApiInterface {
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ class MyApp extends StatelessWidget {
|
||||
return BlocProvider<LocaleBloc>(
|
||||
lazy: false,
|
||||
create: (context) => LocaleBloc(Locale(Platform.localeName)),
|
||||
child: BlocBuilder<LocaleBloc, LocaleState>(
|
||||
builder: (context, state) { return MaterialApp(
|
||||
child: BlocBuilder<LocaleBloc, LocaleState>(builder: (context, state) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
locale: state.currentLocale,
|
||||
localizationsDelegates: AppLocale.localizationsDelegates,
|
||||
@ -48,8 +48,7 @@ class MyApp extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -32,4 +32,3 @@ class SvgGb extends StatelessWidget {
|
||||
return SvgPicture.asset(R.ASSETS_SVG_GB_SVG);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
on<HomeLoadDataEvent>(_onLoadData);
|
||||
}
|
||||
|
||||
Future<void> _onLoadData(
|
||||
HomeLoadDataEvent event, Emitter<HomeState> emit) async {
|
||||
Future<void> _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
|
||||
if (event.nextPage == null) {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
} else {
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
abstract class HomeEvent {
|
||||
const HomeEvent();
|
||||
}
|
||||
@ -8,4 +7,4 @@ class HomeLoadDataEvent extends HomeEvent {
|
||||
final int? nextPage;
|
||||
|
||||
const HomeLoadDataEvent({this.search, this.nextPage});
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,7 @@ class HomeState extends Equatable {
|
||||
this.error,
|
||||
});
|
||||
|
||||
HomeState copyWith(
|
||||
{HomeData? data,
|
||||
bool? isLoading,
|
||||
bool? isPaginationLoading,
|
||||
String? error}) =>
|
||||
HomeState copyWith({HomeData? data, bool? isLoading, bool? isPaginationLoading, String? error}) =>
|
||||
HomeState(
|
||||
data: data ?? this.data,
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
|
@ -72,8 +72,7 @@ class _$HomeStateCWProxyImpl implements _$HomeStateCWProxy {
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: isLoading as bool,
|
||||
isPaginationLoading:
|
||||
isPaginationLoading == const $CopyWithPlaceholder() ||
|
||||
isPaginationLoading == null
|
||||
isPaginationLoading == const $CopyWithPlaceholder() || isPaginationLoading == null
|
||||
? _value.isPaginationLoading
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: isPaginationLoading as bool,
|
||||
|
@ -15,8 +15,7 @@ class CardData {
|
||||
required this.description,
|
||||
this.icon = Icons.add_call,
|
||||
this.score = 0,
|
||||
this.imageUrl =
|
||||
"https://i.pinimg.com/736x/5f/14/b3/5f14b3f14fcd157bc4dffa39085396cc.jpg",
|
||||
this.imageUrl = "https://i.pinimg.com/736x/5f/14/b3/5f14b3f14fcd157bc4dffa39085396cc.jpg",
|
||||
this.id,
|
||||
});
|
||||
}
|
||||
@ -106,14 +105,11 @@ class _Card extends StatelessWidget {
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.purple,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(20),
|
||||
bottomLeft: Radius.circular(18))),
|
||||
topRight: Radius.circular(20), bottomLeft: Radius.circular(18))),
|
||||
padding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||
child: Text(generateStars(score),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: Colors.white)),
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
]),
|
||||
|
@ -84,8 +84,7 @@ class _BodyState extends State<_Body> {
|
||||
}
|
||||
|
||||
void _onNextPageListener() {
|
||||
if (scrollController.offset >
|
||||
scrollController.position.maxScrollExtent - 50) {
|
||||
if (scrollController.offset > scrollController.position.maxScrollExtent - 50) {
|
||||
final bloc = context.read<HomeBloc>();
|
||||
if (!bloc.state.isPaginationLoading) {
|
||||
bloc.add(HomeLoadDataEvent(
|
||||
@ -109,10 +108,7 @@ class _BodyState extends State<_Body> {
|
||||
builder: (context, state) => state.error != null
|
||||
? Text(
|
||||
state.error ?? '',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall
|
||||
?.copyWith(color: Colors.red),
|
||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.red),
|
||||
)
|
||||
: state.isLoading
|
||||
? const CircularProgressIndicator()
|
||||
@ -126,25 +122,21 @@ class _BodyState extends State<_Body> {
|
||||
controller: searchController,
|
||||
placeholder: context.locale.search,
|
||||
onChanged: (search) {
|
||||
Debouce.run(() => context
|
||||
.read<HomeBloc>()
|
||||
.add(HomeLoadDataEvent(search: search)));
|
||||
Debouce.run(() =>
|
||||
context.read<HomeBloc>().add(HomeLoadDataEvent(search: search)));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => context
|
||||
.read<LocaleBloc>()
|
||||
.add(const ChangeLocaleEvent()),
|
||||
onTap: () => context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
|
||||
child: SizedBox.square(
|
||||
dimension: 50,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 15),
|
||||
child: BlocBuilder<LocaleBloc, LocaleState>(
|
||||
builder: (context, state) {
|
||||
return state.currentLocale.languageCode ==
|
||||
'ru'
|
||||
return state.currentLocale.languageCode == 'ru'
|
||||
? const SvgRu()
|
||||
: const SvgGb();
|
||||
},
|
||||
@ -152,8 +144,7 @@ class _BodyState extends State<_Body> {
|
||||
),
|
||||
)),
|
||||
]),
|
||||
BlocBuilder<LikeBloc, LikeState>(
|
||||
builder: (context, likeState) {
|
||||
BlocBuilder<LikeBloc, LikeState>(builder: (context, likeState) {
|
||||
return Expanded(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: _onRefresh,
|
||||
@ -168,8 +159,7 @@ class _BodyState extends State<_Body> {
|
||||
data,
|
||||
isLiked: likeState.likedIds?.contains(data.id) == true,
|
||||
onLike: _onLike,
|
||||
onTap: () =>
|
||||
_navToDetails(context, data),
|
||||
onTap: () => _navToDetails(context, data),
|
||||
)
|
||||
: const SizedBox.shrink();
|
||||
}),
|
||||
@ -192,11 +182,10 @@ class _BodyState extends State<_Body> {
|
||||
}
|
||||
|
||||
void _onLike(String? id, String title, bool isLiked) {
|
||||
if (id != null)
|
||||
{
|
||||
context.read<LikeBloc>().add(ChangeLikeEvent(id));
|
||||
_showLiked(context, title, !isLiked);
|
||||
}
|
||||
if (id != null) {
|
||||
context.read<LikeBloc>().add(ChangeLikeEvent(id));
|
||||
_showLiked(context, title, !isLiked);
|
||||
}
|
||||
}
|
||||
|
||||
void _showLiked(BuildContext context, String title, bool isLiked) {
|
||||
@ -213,9 +202,7 @@ class _BodyState extends State<_Body> {
|
||||
}
|
||||
|
||||
Future<void> _onRefresh() {
|
||||
context
|
||||
.read<HomeBloc>()
|
||||
.add(HomeLoadDataEvent(search: searchController.text));
|
||||
context.read<HomeBloc>().add(HomeLoadDataEvent(search: searchController.text));
|
||||
return Future.value(null);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:first_project/presentation/like_bloc/like_event.dart';
|
||||
import 'package:first_project/presentation/like_bloc/like_state.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -24,7 +23,7 @@ class LikeBloc extends Bloc<LikeEvent, LikeState> {
|
||||
|
||||
if (updatedList.contains(event.id)) {
|
||||
updatedList.remove(event.id);
|
||||
}else {
|
||||
} else {
|
||||
updatedList.add(event.id);
|
||||
}
|
||||
|
||||
@ -33,5 +32,4 @@ class LikeBloc extends Bloc<LikeEvent, LikeState> {
|
||||
|
||||
emit(state.copyWith(likedIds: updatedList));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,4 +10,4 @@ class ChangeLikeEvent extends LikeEvent {
|
||||
final String id;
|
||||
|
||||
const ChangeLikeEvent(this.id);
|
||||
}
|
||||
}
|
||||
|
@ -11,4 +11,4 @@ class LikeState extends Equatable {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [likedIds];
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ class LocaleBloc extends Bloc<LocaleEvent, LocaleState> {
|
||||
}
|
||||
|
||||
Future<void> _onChangeLocale(ChangeLocaleEvent event, Emitter<LocaleState> emit) async {
|
||||
final toChange = AppLocale.supportedLocales.firstWhere((e) => e.languageCode != state.currentLocale.languageCode);
|
||||
final toChange = AppLocale.supportedLocales
|
||||
.firstWhere((e) => e.languageCode != state.currentLocale.languageCode);
|
||||
emit(state.copyWith(currentLocale: toChange));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,4 +4,4 @@ abstract class LocaleEvent {
|
||||
|
||||
class ChangeLocaleEvent extends LocaleEvent {
|
||||
const ChangeLocaleEvent();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import 'dart:ui';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
@ -13,4 +12,4 @@ class LocaleState extends Equatable {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [currentLocale];
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,7 @@ class _$LocaleStateCWProxyImpl implements _$LocaleStateCWProxy {
|
||||
final LocaleState _value;
|
||||
|
||||
@override
|
||||
LocaleState currentLocale(Locale currentLocale) =>
|
||||
this(currentLocale: currentLocale);
|
||||
LocaleState currentLocale(Locale currentLocale) => this(currentLocale: currentLocale);
|
||||
|
||||
@override
|
||||
|
||||
@ -42,11 +41,10 @@ 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user