чистка
This commit is contained in:
parent
79b87d2626
commit
88711e8f27
@ -1,12 +0,0 @@
|
|||||||
/// Generate by [asset_generator](https://github.com/fluttercandies/flutter_asset_generator) library.
|
|
||||||
/// PLEASE DO NOT EDIT MANUALLY.
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
class R {
|
|
||||||
const R._();
|
|
||||||
|
|
||||||
/// ![preview](file://C:\Users\89176\StudioProjects\first_project\assets\svg\gb.svg)
|
|
||||||
static const String ASSETS_SVG_GB_SVG = 'assets/svg/gb.svg';
|
|
||||||
|
|
||||||
/// ![preview](file://C:\Users\89176\StudioProjects\first_project\assets\svg\ru.svg)
|
|
||||||
static const String ASSETS_SVG_RU_SVG = 'assets/svg/ru.svg';
|
|
||||||
}
|
|
@ -23,7 +23,8 @@ class HeroDetailScreen extends StatelessWidget {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is HeroDetailLoaded) {
|
} else if (state is HeroDetailLoaded) {
|
||||||
final hero = state.hero;
|
final hero = state.hero;
|
||||||
return SingleChildScrollView( // Добавляем прокрутку
|
return SingleChildScrollView(
|
||||||
|
// Добавляем прокрутку
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
|
|
||||||
part 'anime_dto.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable(createToJson: false)
|
|
||||||
class AnimesDto {
|
|
||||||
final List<AnimeDataDto>? data;
|
|
||||||
final PaginationDto? pagination;
|
|
||||||
|
|
||||||
const AnimesDto({this.data, this.pagination});
|
|
||||||
|
|
||||||
factory AnimesDto.fromJson(Map<String, dynamic> json) => _$AnimesDtoFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable(createToJson: false)
|
|
||||||
class AnimeDataDto {
|
|
||||||
@JsonKey(name: 'mal_id')
|
|
||||||
final int? id;
|
|
||||||
final String? title;
|
|
||||||
final String? synopsis;
|
|
||||||
final double? score;
|
|
||||||
final AnimeImagesDto? images;
|
|
||||||
|
|
||||||
const AnimeDataDto(this.id, this.title, this.synopsis, this.images, this.score);
|
|
||||||
factory AnimeDataDto.fromJson(Map<String, dynamic> json) => _$AnimeDataDtoFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable(createToJson: false)
|
|
||||||
class AnimeImagesDto {
|
|
||||||
final AnimeImagesJpgDto? jpg;
|
|
||||||
|
|
||||||
const AnimeImagesDto({this.jpg});
|
|
||||||
|
|
||||||
factory AnimeImagesDto.fromJson(Map<String, dynamic> json) => _$AnimeImagesDtoFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable(createToJson: false)
|
|
||||||
class AnimeImagesJpgDto {
|
|
||||||
@JsonKey(name: 'image_url')
|
|
||||||
final String? image;
|
|
||||||
@JsonKey(name: 'small_image_url')
|
|
||||||
final String? smallImage;
|
|
||||||
@JsonKey(name: 'large_image_url')
|
|
||||||
final String? largeImage;
|
|
||||||
|
|
||||||
const AnimeImagesJpgDto(this.image, this.smallImage, this.largeImage);
|
|
||||||
factory AnimeImagesJpgDto.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$AnimeImagesJpgDtoFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonSerializable(createToJson: false)
|
|
||||||
class PaginationDto {
|
|
||||||
@JsonKey(name: "last_visible_page")
|
|
||||||
final int? last;
|
|
||||||
@JsonKey(name: "current_page")
|
|
||||||
final int? current;
|
|
||||||
@JsonKey(name: "has_next_page")
|
|
||||||
final bool? next;
|
|
||||||
|
|
||||||
const PaginationDto({this.current, this.last, this.next});
|
|
||||||
factory PaginationDto.fromJson(Map<String, dynamic> json) => _$PaginationDtoFromJson(json);
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'anime_dto.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// JsonSerializableGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
AnimesDto _$AnimesDtoFromJson(Map<String, dynamic> json) => AnimesDto(
|
|
||||||
data: (json['data'] as List<dynamic>?)
|
|
||||||
?.map((e) => AnimeDataDto.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
|
||||||
pagination: json['pagination'] == null
|
|
||||||
? null
|
|
||||||
: PaginationDto.fromJson(json['pagination'] as Map<String, dynamic>),
|
|
||||||
);
|
|
||||||
|
|
||||||
AnimeDataDto _$AnimeDataDtoFromJson(Map<String, dynamic> json) => AnimeDataDto(
|
|
||||||
(json['mal_id'] as num?)?.toInt(),
|
|
||||||
json['title'] as String?,
|
|
||||||
json['synopsis'] as String?,
|
|
||||||
json['images'] == null
|
|
||||||
? null
|
|
||||||
: AnimeImagesDto.fromJson(json['images'] as Map<String, dynamic>),
|
|
||||||
(json['score'] as num?)?.toDouble(),
|
|
||||||
);
|
|
||||||
|
|
||||||
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(
|
|
||||||
json['image_url'] as String?,
|
|
||||||
json['small_image_url'] as String?,
|
|
||||||
json['large_image_url'] as String?,
|
|
||||||
);
|
|
||||||
|
|
||||||
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?,
|
|
||||||
);
|
|
@ -1,22 +0,0 @@
|
|||||||
import 'package:first_project/data/dtos/anime_dto.dart';
|
|
||||||
import 'package:first_project/domain/models/home.dart';
|
|
||||||
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(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AnimesDataDtoToModel on AnimesDto {
|
|
||||||
HomeData toDomain() => HomeData(
|
|
||||||
data: data?.map((e) => e.toDomain()).toList(),
|
|
||||||
nextPage: pagination!.next! ? pagination!.current! + 1 : 0,
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:first_project/data/dtos/anime_dto.dart';
|
|
||||||
import 'package:first_project/data/mappers/anime_mapper.dart';
|
|
||||||
import 'package:first_project/data/repositories/api_interface.dart';
|
|
||||||
import 'package:first_project/domain/models/home.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/home_page.dart';
|
|
||||||
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,
|
|
||||||
));
|
|
||||||
|
|
||||||
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) {
|
|
||||||
onError?.call(e.error?.toString());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import 'package:first_project/domain/models/home.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/home_page.dart';
|
|
||||||
|
|
||||||
typedef OnErrorCallback = void Function(String? error);
|
|
||||||
|
|
||||||
abstract class ApiInterface {
|
|
||||||
Future<HomeData?> loadData({OnErrorCallback? onError});
|
|
||||||
}
|
|
@ -15,4 +15,4 @@ class HeroRepository {
|
|||||||
final hero = await apiService.fetchHeroDetails(id);
|
final hero = await apiService.fetchHeroDetails(id);
|
||||||
return HeroDto.fromJson(hero);
|
return HeroDto.fromJson(hero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
import 'package:first_project/data/repositories/api_interface.dart';
|
|
||||||
import 'package:first_project/domain/models/home.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/home_page.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class MockRepository extends ApiInterface {
|
|
||||||
@override
|
|
||||||
Future<HomeData?> loadData({OnErrorCallback? onError}) async {
|
|
||||||
return HomeData(data: [
|
|
||||||
const CardData(
|
|
||||||
"First",
|
|
||||||
score: 5,
|
|
||||||
description: "SomeText",
|
|
||||||
),
|
|
||||||
const CardData(
|
|
||||||
"Second",
|
|
||||||
score: 8,
|
|
||||||
icon: Icons.gamepad,
|
|
||||||
description: "ManyText",
|
|
||||||
imageUrl: "https://i.pinimg.com/originals/21/73/24/217324138d1bbc91663d4943ebe5de60.jpg",
|
|
||||||
),
|
|
||||||
const CardData(
|
|
||||||
"Third",
|
|
||||||
score: 9,
|
|
||||||
icon: Icons.offline_bolt_outlined,
|
|
||||||
description: "Wow >_<",
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import 'package:first_project/presentation/home_page/home_page.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class DetailsPage extends StatelessWidget {
|
|
||||||
final CardData data;
|
|
||||||
|
|
||||||
const DetailsPage(this.data, {super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(),
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
|
||||||
child: Image.network(
|
|
||||||
data.imageUrl,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 4),
|
|
||||||
child: Text(
|
|
||||||
data.text,
|
|
||||||
style: Theme.of(context).textTheme.headlineLarge,
|
|
||||||
)),
|
|
||||||
Text(
|
|
||||||
data.description,
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import 'package:first_project/presentation/home_page/home_page.dart';
|
|
||||||
|
|
||||||
class HomeData {
|
|
||||||
final List<CardData>? data;
|
|
||||||
final int? nextPage;
|
|
||||||
|
|
||||||
HomeData({this.data, this.nextPage});
|
|
||||||
}
|
|
@ -21,8 +21,7 @@ class LocaleNotifier with ChangeNotifier {
|
|||||||
Locale get currentLocale => _currentLocale;
|
Locale get currentLocale => _currentLocale;
|
||||||
|
|
||||||
void toggleLocale() {
|
void toggleLocale() {
|
||||||
_currentLocale =
|
_currentLocale = _currentLocale.languageCode == 'en' ? const Locale('ru') : const Locale('en');
|
||||||
_currentLocale.languageCode == 'en' ? const Locale('ru') : const Locale('en');
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,8 +54,7 @@ class MyApp extends StatelessWidget {
|
|||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (_) =>
|
create: (_) => HeroSearchBloc(allHeroes: state.heroes),
|
||||||
HeroSearchBloc(allHeroes: state.heroes),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const HeroListScreen(),
|
child: const HeroListScreen(),
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:first_project/Components/resources.g.dart';
|
|
||||||
|
|
||||||
abstract class SvgObjects {
|
|
||||||
static void init() {
|
|
||||||
final pics = <String>[
|
|
||||||
R.ASSETS_SVG_RU_SVG,
|
|
||||||
R.ASSETS_SVG_GB_SVG,
|
|
||||||
];
|
|
||||||
for (final String p in pics) {
|
|
||||||
final loader = SvgAssetLoader(p);
|
|
||||||
svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SvgRu extends StatelessWidget {
|
|
||||||
const SvgRu({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SvgPicture.asset(R.ASSETS_SVG_RU_SVG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SvgGb extends StatelessWidget {
|
|
||||||
const SvgGb({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SvgPicture.asset(R.ASSETS_SVG_GB_SVG);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,38 +0,0 @@
|
|||||||
import 'package:first_project/data/repositories/anime_repository.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/bloc/events.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/bloc/state.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
|
|
||||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|
||||||
final AnimeRepository repo;
|
|
||||||
|
|
||||||
HomeBloc(this.repo) : super(const HomeState()) {
|
|
||||||
on<HomeLoadDataEvent>(_onLoadData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
|
|
||||||
if (event.nextPage == null) {
|
|
||||||
emit(state.copyWith(isLoading: true));
|
|
||||||
} else {
|
|
||||||
emit(state.copyWith(isPaginationLoading: true));
|
|
||||||
}
|
|
||||||
|
|
||||||
String? error;
|
|
||||||
|
|
||||||
final data = await repo.loadData(
|
|
||||||
q: event.search,
|
|
||||||
page: event.nextPage ?? 1,
|
|
||||||
onError: (e) => error = e,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (event.nextPage != null) {
|
|
||||||
data?.data?.insertAll(0, state.data?.data ?? []);
|
|
||||||
}
|
|
||||||
emit(state.copyWith(
|
|
||||||
data: data,
|
|
||||||
isLoading: false,
|
|
||||||
isPaginationLoading: false,
|
|
||||||
error: error,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
abstract class HomeEvent {
|
|
||||||
const HomeEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class HomeLoadDataEvent extends HomeEvent {
|
|
||||||
final String? search;
|
|
||||||
final int? nextPage;
|
|
||||||
|
|
||||||
const HomeLoadDataEvent({this.search, this.nextPage});
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:first_project/domain/models/card.dart';
|
|
||||||
import 'package:first_project/domain/models/home.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/home_page.dart';
|
|
||||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
|
||||||
|
|
||||||
part 'state.g.dart';
|
|
||||||
|
|
||||||
@CopyWith()
|
|
||||||
class HomeState extends Equatable {
|
|
||||||
final HomeData? data;
|
|
||||||
final bool isLoading;
|
|
||||||
final bool isPaginationLoading;
|
|
||||||
final String? error;
|
|
||||||
|
|
||||||
const HomeState({
|
|
||||||
this.data,
|
|
||||||
this.isLoading = false,
|
|
||||||
this.isPaginationLoading = false,
|
|
||||||
this.error,
|
|
||||||
});
|
|
||||||
|
|
||||||
HomeState copyWith({HomeData? data, bool? isLoading, bool? isPaginationLoading, String? error}) =>
|
|
||||||
HomeState(
|
|
||||||
data: data ?? this.data,
|
|
||||||
isLoading: isLoading ?? this.isLoading,
|
|
||||||
isPaginationLoading: isPaginationLoading ?? this.isPaginationLoading,
|
|
||||||
error: error ?? this.error,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [
|
|
||||||
data,
|
|
||||||
isLoading,
|
|
||||||
isPaginationLoading,
|
|
||||||
error,
|
|
||||||
];
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'state.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// CopyWithGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
abstract class _$HomeStateCWProxy {
|
|
||||||
HomeState data(HomeData? data);
|
|
||||||
|
|
||||||
HomeState isLoading(bool isLoading);
|
|
||||||
|
|
||||||
HomeState isPaginationLoading(bool isPaginationLoading);
|
|
||||||
|
|
||||||
HomeState error(String? error);
|
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `HomeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
|
||||||
///
|
|
||||||
/// Usage
|
|
||||||
/// ```dart
|
|
||||||
/// HomeState(...).copyWith(id: 12, name: "My name")
|
|
||||||
/// ````
|
|
||||||
HomeState call({
|
|
||||||
HomeData? data,
|
|
||||||
bool? isLoading,
|
|
||||||
bool? isPaginationLoading,
|
|
||||||
String? error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfHomeState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfHomeState.copyWith.fieldName(...)`
|
|
||||||
class _$HomeStateCWProxyImpl implements _$HomeStateCWProxy {
|
|
||||||
const _$HomeStateCWProxyImpl(this._value);
|
|
||||||
|
|
||||||
final HomeState _value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
HomeState data(HomeData? data) => this(data: data);
|
|
||||||
|
|
||||||
@override
|
|
||||||
HomeState isLoading(bool isLoading) => this(isLoading: isLoading);
|
|
||||||
|
|
||||||
@override
|
|
||||||
HomeState isPaginationLoading(bool isPaginationLoading) =>
|
|
||||||
this(isPaginationLoading: isPaginationLoading);
|
|
||||||
|
|
||||||
@override
|
|
||||||
HomeState error(String? error) => this(error: error);
|
|
||||||
|
|
||||||
@override
|
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `HomeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
|
||||||
///
|
|
||||||
/// Usage
|
|
||||||
/// ```dart
|
|
||||||
/// HomeState(...).copyWith(id: 12, name: "My name")
|
|
||||||
/// ````
|
|
||||||
HomeState call({
|
|
||||||
Object? data = const $CopyWithPlaceholder(),
|
|
||||||
Object? isLoading = const $CopyWithPlaceholder(),
|
|
||||||
Object? isPaginationLoading = const $CopyWithPlaceholder(),
|
|
||||||
Object? error = const $CopyWithPlaceholder(),
|
|
||||||
}) {
|
|
||||||
return HomeState(
|
|
||||||
data: data == const $CopyWithPlaceholder()
|
|
||||||
? _value.data
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
: data as HomeData?,
|
|
||||||
isLoading: isLoading == const $CopyWithPlaceholder() || isLoading == null
|
|
||||||
? _value.isLoading
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
: isLoading as bool,
|
|
||||||
isPaginationLoading:
|
|
||||||
isPaginationLoading == const $CopyWithPlaceholder() || isPaginationLoading == null
|
|
||||||
? _value.isPaginationLoading
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
: isPaginationLoading as bool,
|
|
||||||
error: error == const $CopyWithPlaceholder()
|
|
||||||
? _value.error
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
: error as String?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension $HomeStateCopyWith on HomeState {
|
|
||||||
/// Returns a callable class that can be used as follows: `instanceOfHomeState.copyWith(...)` or like so:`instanceOfHomeState.copyWith.fieldName(...)`.
|
|
||||||
// ignore: library_private_types_in_public_api
|
|
||||||
_$HomeStateCWProxy get copyWith => _$HomeStateCWProxyImpl(this);
|
|
||||||
}
|
|
@ -1,179 +0,0 @@
|
|||||||
part of 'home_page.dart';
|
|
||||||
|
|
||||||
typedef OnLikeCallback = void Function(String? id, String title, bool isLiked)?;
|
|
||||||
|
|
||||||
class CardData {
|
|
||||||
final String text;
|
|
||||||
final String description;
|
|
||||||
final IconData icon;
|
|
||||||
final double score;
|
|
||||||
final String imageUrl;
|
|
||||||
final String? id;
|
|
||||||
|
|
||||||
const CardData(
|
|
||||||
this.text, {
|
|
||||||
required this.description,
|
|
||||||
this.icon = Icons.add_call,
|
|
||||||
this.score = 0,
|
|
||||||
this.imageUrl = "https://i.pinimg.com/736x/5f/14/b3/5f14b3f14fcd157bc4dffa39085396cc.jpg",
|
|
||||||
this.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Card extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final String description;
|
|
||||||
final String? imageUrl;
|
|
||||||
final double score;
|
|
||||||
final OnLikeCallback onLike;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
final String? id;
|
|
||||||
final bool isLiked;
|
|
||||||
|
|
||||||
const _Card(
|
|
||||||
this.text, {
|
|
||||||
required this.description,
|
|
||||||
required this.imageUrl,
|
|
||||||
required this.score,
|
|
||||||
this.onLike,
|
|
||||||
this.onTap,
|
|
||||||
this.id,
|
|
||||||
this.isLiked = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory _Card.fromData(
|
|
||||||
CardData data, {
|
|
||||||
OnLikeCallback onLike,
|
|
||||||
VoidCallback? onTap,
|
|
||||||
bool isLiked = false,
|
|
||||||
}) =>
|
|
||||||
_Card(
|
|
||||||
data.text,
|
|
||||||
description: data.description,
|
|
||||||
imageUrl: data.imageUrl,
|
|
||||||
score: data.score,
|
|
||||||
onLike: onLike,
|
|
||||||
onTap: onTap,
|
|
||||||
id: data.id,
|
|
||||||
isLiked: isLiked,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
List<String> desc = description.split("\n");
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.all(10),
|
|
||||||
constraints: const BoxConstraints(minHeight: 140),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
border: Border.all(color: Colors.green, width: 2),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.grey.withOpacity(.4),
|
|
||||||
offset: const Offset(0, 5),
|
|
||||||
blurRadius: 6,
|
|
||||||
spreadRadius: 3),
|
|
||||||
]),
|
|
||||||
child: IntrinsicHeight(
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Stack(children: [
|
|
||||||
ClipRRect(
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
bottomLeft: Radius.circular(18),
|
|
||||||
topLeft: Radius.circular(18),
|
|
||||||
),
|
|
||||||
child: SizedBox(
|
|
||||||
height: 300,
|
|
||||||
width: 120,
|
|
||||||
child: Image.network(
|
|
||||||
imageUrl ?? '',
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
errorBuilder: (_, __, ___) => const Placeholder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomLeft,
|
|
||||||
child: Container(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.purple,
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
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)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
desc[0],
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 8,
|
|
||||||
right: 8,
|
|
||||||
bottom: 8,
|
|
||||||
),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () => onLike?.call(id, text, isLiked),
|
|
||||||
child: AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: isLiked
|
|
||||||
? const Icon(
|
|
||||||
Icons.favorite,
|
|
||||||
color: Colors.red,
|
|
||||||
key: ValueKey<int>(0),
|
|
||||||
)
|
|
||||||
: const Icon(
|
|
||||||
Icons.favorite_border,
|
|
||||||
key: ValueKey<int>(1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String generateStars(double rating) {
|
|
||||||
rating = rating / 2;
|
|
||||||
int fullStars = rating.floor();
|
|
||||||
bool hasHalfStar = (rating - fullStars) >= 0.5;
|
|
||||||
String stars = '★' * fullStars;
|
|
||||||
if (hasHalfStar) {
|
|
||||||
stars += '✪';
|
|
||||||
}
|
|
||||||
int emptyStars = 5 - stars.length;
|
|
||||||
stars += '☆' * emptyStars;
|
|
||||||
return stars;
|
|
||||||
}
|
|
@ -1,208 +0,0 @@
|
|||||||
import 'package:first_project/Components/extensions/context_x.dart';
|
|
||||||
import 'package:first_project/data/repositories/anime_repository.dart';
|
|
||||||
import 'package:first_project/data/repositories/mock_repository.dart';
|
|
||||||
import 'package:first_project/domain/models/card.dart';
|
|
||||||
import 'package:first_project/presentation/common/svg_objects.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/bloc/bloc.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/bloc/events.dart';
|
|
||||||
import 'package:first_project/presentation/home_page/bloc/state.dart';
|
|
||||||
import 'package:first_project/presentation/like_bloc/like_bloc.dart';
|
|
||||||
import 'package:first_project/presentation/like_bloc/like_event.dart';
|
|
||||||
import 'package:first_project/presentation/like_bloc/like_state.dart';
|
|
||||||
import 'package:first_project/presentation/locale_bloc/locale_events.dart';
|
|
||||||
import 'package:first_project/presentation/locale_bloc/locale_state.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
|
|
||||||
import 'package:first_project/Components/utils/debounce.dart';
|
|
||||||
|
|
||||||
import '../locale_bloc/locale_bloc.dart';
|
|
||||||
|
|
||||||
part 'card.dart';
|
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
|
||||||
const MyHomePage({super.key, required this.title});
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
||||||
content: Text(
|
|
||||||
'Hello',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.purple,
|
|
||||||
duration: const Duration(seconds: 1),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: Colors.purple,
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
|
||||||
body: const _Body(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Body extends StatefulWidget {
|
|
||||||
const _Body({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_Body> createState() => _BodyState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BodyState extends State<_Body> {
|
|
||||||
final searchController = TextEditingController();
|
|
||||||
final scrollController = ScrollController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
SvgObjects.init();
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
context.read<HomeBloc>().add(const HomeLoadDataEvent());
|
|
||||||
context.read<LikeBloc>().add(const LoadLikesEvent());
|
|
||||||
});
|
|
||||||
|
|
||||||
scrollController.addListener(_onNextPageListener);
|
|
||||||
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onNextPageListener() {
|
|
||||||
if (scrollController.offset > scrollController.position.maxScrollExtent - 50) {
|
|
||||||
final bloc = context.read<HomeBloc>();
|
|
||||||
if (!bloc.state.isPaginationLoading) {
|
|
||||||
bloc.add(HomeLoadDataEvent(
|
|
||||||
search: searchController.text,
|
|
||||||
nextPage: bloc.state.data?.nextPage,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
searchController.dispose();
|
|
||||||
scrollController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocBuilder<HomeBloc, HomeState>(
|
|
||||||
builder: (context, state) => state.error != null
|
|
||||||
? Text(
|
|
||||||
state.error ?? '',
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.red),
|
|
||||||
)
|
|
||||||
: state.isLoading
|
|
||||||
? const CircularProgressIndicator()
|
|
||||||
: Column(children: [
|
|
||||||
Row(children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 4,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: CupertinoSearchTextField(
|
|
||||||
controller: searchController,
|
|
||||||
placeholder: context.locale.search,
|
|
||||||
onChanged: (search) {
|
|
||||||
Debouce.run(() =>
|
|
||||||
context.read<HomeBloc>().add(HomeLoadDataEvent(search: search)));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
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'
|
|
||||||
? const SvgRu()
|
|
||||||
: const SvgGb();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
]),
|
|
||||||
BlocBuilder<LikeBloc, LikeState>(builder: (context, likeState) {
|
|
||||||
return Expanded(
|
|
||||||
child: RefreshIndicator(
|
|
||||||
onRefresh: _onRefresh,
|
|
||||||
child: ListView.builder(
|
|
||||||
controller: scrollController,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
itemCount: state.data?.data?.length ?? 0,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final data = state.data?.data?[index];
|
|
||||||
return data != null
|
|
||||||
? _Card.fromData(
|
|
||||||
data,
|
|
||||||
isLiked: likeState.likedIds?.contains(data.id) == true,
|
|
||||||
onLike: _onLike,
|
|
||||||
onTap: () => _navToDetails(context, data),
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink();
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
BlocBuilder<HomeBloc, HomeState>(
|
|
||||||
builder: (context, state) => state.isPaginationLoading
|
|
||||||
? const CircularProgressIndicator()
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
)
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _navToDetails(BuildContext context, CardData data) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
CupertinoPageRoute(builder: (context) => DetailsPage(data)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onLike(String? id, String title, bool isLiked) {
|
|
||||||
if (id != null) {
|
|
||||||
context.read<LikeBloc>().add(ChangeLikeEvent(id));
|
|
||||||
_showLiked(context, title, !isLiked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showLiked(BuildContext context, String title, bool isLiked) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
||||||
content: Text(
|
|
||||||
'$title ${isLiked ? context.locale.liked : context.locale.disliked}',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.purple,
|
|
||||||
duration: const Duration(seconds: 1),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onRefresh() {
|
|
||||||
context.read<HomeBloc>().add(HomeLoadDataEvent(search: searchController.text));
|
|
||||||
return Future.value(null);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
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';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
|
|
||||||
const String _likedPrefsKey = 'liked';
|
|
||||||
|
|
||||||
class LikeBloc extends Bloc<LikeEvent, LikeState> {
|
|
||||||
LikeBloc() : super(const LikeState(likedIds: [])) {
|
|
||||||
on<ChangeLikeEvent>(_onChangeLike);
|
|
||||||
on<LoadLikesEvent>(_onLoadLikes);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onLoadLikes(LoadLikesEvent event, Emitter<LikeState> emit) async {
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
final data = prefs.getStringList(_likedPrefsKey);
|
|
||||||
|
|
||||||
emit(state.copyWith(likedIds: data));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onChangeLike(ChangeLikeEvent event, Emitter<LikeState> emit) async {
|
|
||||||
final updatedList = List<String>.from(state.likedIds ?? []);
|
|
||||||
|
|
||||||
if (updatedList.contains(event.id)) {
|
|
||||||
updatedList.remove(event.id);
|
|
||||||
} else {
|
|
||||||
updatedList.add(event.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
prefs.setStringList(_likedPrefsKey, updatedList);
|
|
||||||
|
|
||||||
emit(state.copyWith(likedIds: updatedList));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
abstract class LikeEvent {
|
|
||||||
const LikeEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoadLikesEvent extends LikeEvent {
|
|
||||||
const LoadLikesEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChangeLikeEvent extends LikeEvent {
|
|
||||||
final String id;
|
|
||||||
|
|
||||||
const ChangeLikeEvent(this.id);
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
|
||||||
|
|
||||||
part "like_state.g.dart";
|
|
||||||
|
|
||||||
@CopyWith()
|
|
||||||
class LikeState extends Equatable {
|
|
||||||
final List<String>? likedIds;
|
|
||||||
|
|
||||||
const LikeState({required this.likedIds});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [likedIds];
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'like_state.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// CopyWithGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
abstract class _$LikeStateCWProxy {
|
|
||||||
LikeState likedIds(List<String>? likedIds);
|
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LikeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
|
||||||
///
|
|
||||||
/// Usage
|
|
||||||
/// ```dart
|
|
||||||
/// LikeState(...).copyWith(id: 12, name: "My name")
|
|
||||||
/// ````
|
|
||||||
LikeState call({
|
|
||||||
List<String>? likedIds,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfLikeState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfLikeState.copyWith.fieldName(...)`
|
|
||||||
class _$LikeStateCWProxyImpl implements _$LikeStateCWProxy {
|
|
||||||
const _$LikeStateCWProxyImpl(this._value);
|
|
||||||
|
|
||||||
final LikeState _value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
LikeState likedIds(List<String>? likedIds) => this(likedIds: likedIds);
|
|
||||||
|
|
||||||
@override
|
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LikeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
|
||||||
///
|
|
||||||
/// Usage
|
|
||||||
/// ```dart
|
|
||||||
/// LikeState(...).copyWith(id: 12, name: "My name")
|
|
||||||
/// ````
|
|
||||||
LikeState call({
|
|
||||||
Object? likedIds = const $CopyWithPlaceholder(),
|
|
||||||
}) {
|
|
||||||
return LikeState(
|
|
||||||
likedIds: likedIds == const $CopyWithPlaceholder()
|
|
||||||
? _value.likedIds
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
: likedIds as List<String>?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension $LikeStateCopyWith on LikeState {
|
|
||||||
/// Returns a callable class that can be used as follows: `instanceOfLikeState.copyWith(...)` or like so:`instanceOfLikeState.copyWith.fieldName(...)`.
|
|
||||||
// ignore: library_private_types_in_public_api
|
|
||||||
_$LikeStateCWProxy get copyWith => _$LikeStateCWProxyImpl(this);
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:first_project/Components/locale/l10n/app_locale.dart';
|
|
||||||
import 'package:first_project/presentation/locale_bloc/locale_events.dart';
|
|
||||||
import 'package:first_project/presentation/locale_bloc/locale_state.dart';
|
|
||||||
|
|
||||||
class LocaleBloc extends Bloc<LocaleEvent, LocaleState> {
|
|
||||||
LocaleBloc(Locale defaultLocale) : super(LocaleState(currentLocale: defaultLocale)) {
|
|
||||||
on<ChangeLocaleEvent>(_onChangeLocale);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onChangeLocale(ChangeLocaleEvent event, Emitter<LocaleState> emit) async {
|
|
||||||
final toChange = AppLocale.supportedLocales
|
|
||||||
.firstWhere((e) => e.languageCode != state.currentLocale.languageCode);
|
|
||||||
emit(state.copyWith(currentLocale: toChange));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
abstract class LocaleEvent {
|
|
||||||
const LocaleEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChangeLocaleEvent extends LocaleEvent {
|
|
||||||
const ChangeLocaleEvent();
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import 'dart:ui';
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
|
||||||
|
|
||||||
part 'locale_state.g.dart';
|
|
||||||
|
|
||||||
@CopyWith()
|
|
||||||
class LocaleState extends Equatable {
|
|
||||||
final Locale currentLocale;
|
|
||||||
|
|
||||||
const LocaleState({required this.currentLocale});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [currentLocale];
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
|
|
||||||
part of 'locale_state.dart';
|
|
||||||
|
|
||||||
// **************************************************************************
|
|
||||||
// CopyWithGenerator
|
|
||||||
// **************************************************************************
|
|
||||||
|
|
||||||
abstract class _$LocaleStateCWProxy {
|
|
||||||
LocaleState currentLocale(Locale currentLocale);
|
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LocaleState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
|
||||||
///
|
|
||||||
/// Usage
|
|
||||||
/// ```dart
|
|
||||||
/// LocaleState(...).copyWith(id: 12, name: "My name")
|
|
||||||
/// ````
|
|
||||||
LocaleState call({
|
|
||||||
Locale? currentLocale,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfLocaleState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfLocaleState.copyWith.fieldName(...)`
|
|
||||||
class _$LocaleStateCWProxyImpl implements _$LocaleStateCWProxy {
|
|
||||||
const _$LocaleStateCWProxyImpl(this._value);
|
|
||||||
|
|
||||||
final LocaleState _value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
LocaleState currentLocale(Locale currentLocale) => this(currentLocale: currentLocale);
|
|
||||||
|
|
||||||
@override
|
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LocaleState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
|
||||||
///
|
|
||||||
/// Usage
|
|
||||||
/// ```dart
|
|
||||||
/// LocaleState(...).copyWith(id: 12, name: "My name")
|
|
||||||
/// ````
|
|
||||||
LocaleState call({
|
|
||||||
Object? currentLocale = const $CopyWithPlaceholder(),
|
|
||||||
}) {
|
|
||||||
return LocaleState(
|
|
||||||
currentLocale: currentLocale == const $CopyWithPlaceholder() || currentLocale == null
|
|
||||||
? _value.currentLocale
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
: currentLocale as Locale,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension $LocaleStateCopyWith on LocaleState {
|
|
||||||
/// Returns a callable class that can be used as follows: `instanceOfLocaleState.copyWith(...)` or like so:`instanceOfLocaleState.copyWith.fieldName(...)`.
|
|
||||||
// ignore: library_private_types_in_public_api
|
|
||||||
_$LocaleStateCWProxy get copyWith => _$LocaleStateCWProxyImpl(this);
|
|
||||||
}
|
|
@ -46,7 +46,7 @@ class _HeroCardState extends State<HeroCard> {
|
|||||||
_isLiked = !_isLiked;
|
_isLiked = !_isLiked;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Отображение Snackbar с локализованным сообщением
|
// Отображение Snackbar
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(message),
|
content: Text(message),
|
||||||
|
Loading…
Reference in New Issue
Block a user