Design fix

This commit is contained in:
Serxiolog 2024-11-24 21:19:36 +04:00
parent ff4530dae3
commit cc63344d86
24 changed files with 94 additions and 130 deletions

View File

@ -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)!;
}
}

View File

@ -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.');
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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?,

View File

@ -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,
);
}

View File

@ -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;
}
}
}
}

View File

@ -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});
}
}

View File

@ -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 {
),
]);
}
}
}

View File

@ -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 {
),
),
);
}
),
}),
);
}
}

View File

@ -32,4 +32,3 @@ class SvgGb extends StatelessWidget {
return SvgPicture.asset(R.ASSETS_SVG_GB_SVG);
}
}

View File

@ -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 {

View File

@ -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});
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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)),
),
),
]),

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -10,4 +10,4 @@ class ChangeLikeEvent extends LikeEvent {
final String id;
const ChangeLikeEvent(this.id);
}
}

View File

@ -11,4 +11,4 @@ class LikeState extends Equatable {
@override
List<Object?> get props => [likedIds];
}
}

View File

@ -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));
}
}
}

View File

@ -4,4 +4,4 @@ abstract class LocaleEvent {
class ChangeLocaleEvent extends LocaleEvent {
const ChangeLocaleEvent();
}
}

View File

@ -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];
}
}

View File

@ -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,
);
}
}