diff --git a/assets/icon.jpg b/assets/icon.jpg
new file mode 100644
index 0000000..9d71906
Binary files /dev/null and b/assets/icon.jpg differ
diff --git a/assets/svg/ru.svg b/assets/svg/ru.svg
new file mode 100644
index 0000000..ae12982
--- /dev/null
+++ b/assets/svg/ru.svg
@@ -0,0 +1,19 @@
+
+
+
\ No newline at end of file
diff --git a/assets/svg/uk.svg b/assets/svg/uk.svg
new file mode 100644
index 0000000..88e2211
--- /dev/null
+++ b/assets/svg/uk.svg
@@ -0,0 +1,23 @@
+
+
+
\ No newline at end of file
diff --git a/assets/svg/us.svg b/assets/svg/us.svg
new file mode 100644
index 0000000..f5a7a01
--- /dev/null
+++ b/assets/svg/us.svg
@@ -0,0 +1,34 @@
+
+
+
\ No newline at end of file
diff --git a/l10n.yaml b/l10n.yaml
new file mode 100644
index 0000000..d26d702
--- /dev/null
+++ b/l10n.yaml
@@ -0,0 +1,6 @@
+arb-dir: l10n
+template-arb-file: app_ru.arb
+output-localization-file: app_locale.dart
+output-dir: lib/components/locale/l10n
+output-class: AppLocale
+synthetic-package: false
\ No newline at end of file
diff --git a/l10n/app_en.arb b/l10n/app_en.arb
new file mode 100644
index 0000000..163cefe
--- /dev/null
+++ b/l10n/app_en.arb
@@ -0,0 +1,8 @@
+{
+ "@@locale": "en",
+
+ "search": "Search",
+ "liked": "liked!",
+ "disliked": "unliked"
+
+}
diff --git a/l10n/app_ru.arb b/l10n/app_ru.arb
new file mode 100644
index 0000000..27bca94
--- /dev/null
+++ b/l10n/app_ru.arb
@@ -0,0 +1,7 @@
+{
+ "@@locale": "ru",
+
+ "search": "Поиск",
+ "liked": "понравился!",
+ "disliked": "разонравился"
+}
diff --git a/lib/components/extension/context_x.dart b/lib/components/extension/context_x.dart
new file mode 100644
index 0000000..fb0db87
--- /dev/null
+++ b/lib/components/extension/context_x.dart
@@ -0,0 +1,6 @@
+import 'package:flutter/widgets.dart';
+import 'package:pmd_labs/components/locale/l10n/app_locale.dart';
+
+extension LocalContextX on BuildContext {
+ AppLocale get locale => AppLocale.of(this)!;
+}
diff --git a/lib/components/locale/l10n/app_locale.dart b/lib/components/locale/l10n/app_locale.dart
new file mode 100644
index 0000000..1c601cd
--- /dev/null
+++ b/lib/components/locale/l10n/app_locale.dart
@@ -0,0 +1,147 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:intl/intl.dart' as intl;
+
+import 'app_locale_en.dart';
+import 'app_locale_ru.dart';
+
+// ignore_for_file: type=lint
+
+/// Callers can lookup localized strings with an instance of AppLocale
+/// returned by `AppLocale.of(context)`.
+///
+/// Applications need to include `AppLocale.delegate()` in their app's
+/// `localizationDelegates` list, and the locales they support in the app's
+/// `supportedLocales` list. For example:
+///
+/// ```dart
+/// import 'l10n/app_locale.dart';
+///
+/// return MaterialApp(
+/// localizationsDelegates: AppLocale.localizationsDelegates,
+/// supportedLocales: AppLocale.supportedLocales,
+/// home: MyApplicationHome(),
+/// );
+/// ```
+///
+/// ## Update pubspec.yaml
+///
+/// Please make sure to update your pubspec.yaml to include the following
+/// packages:
+///
+/// ```yaml
+/// dependencies:
+/// # Internationalization support.
+/// flutter_localizations:
+/// sdk: flutter
+/// intl: any # Use the pinned version from flutter_localizations
+///
+/// # Rest of dependencies
+/// ```
+///
+/// ## iOS Applications
+///
+/// iOS applications define key application metadata, including supported
+/// locales, in an Info.plist file that is built into the application bundle.
+/// To configure the locales supported by your app, you’ll need to edit this
+/// file.
+///
+/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
+/// Then, in the Project Navigator, open the Info.plist file under the Runner
+/// project’s Runner folder.
+///
+/// Next, select the Information Property List item, select Add Item from the
+/// Editor menu, then select Localizations from the pop-up menu.
+///
+/// Select and expand the newly-created Localizations item then, for each
+/// locale your application supports, add a new item and select the locale
+/// you wish to add from the pop-up menu in the Value field. This list should
+/// be consistent with the languages listed in the AppLocale.supportedLocales
+/// property.
+abstract class AppLocale {
+ AppLocale(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());
+
+ final String localeName;
+
+ static AppLocale? of(BuildContext context) {
+ return Localizations.of(context, AppLocale);
+ }
+
+ static const LocalizationsDelegate delegate = _AppLocaleDelegate();
+
+ /// A list of this localizations delegate along with the default localizations
+ /// delegates.
+ ///
+ /// Returns a list of localizations delegates containing this delegate along with
+ /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
+ /// and GlobalWidgetsLocalizations.delegate.
+ ///
+ /// 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 = >[
+ delegate,
+ GlobalMaterialLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ ];
+
+ /// A list of this localizations delegate's supported locales.
+ static const List supportedLocales = [
+ Locale('en'),
+ Locale('ru')
+ ];
+
+ /// No description provided for @search.
+ ///
+ /// In ru, this message translates to:
+ /// **'Поиск'**
+ String get search;
+
+ /// No description provided for @liked.
+ ///
+ /// In ru, this message translates to:
+ /// **'понравился!'**
+ String get liked;
+
+ /// No description provided for @disliked.
+ ///
+ /// In ru, this message translates to:
+ /// **'разонравился'**
+ String get disliked;
+}
+
+class _AppLocaleDelegate extends LocalizationsDelegate {
+ const _AppLocaleDelegate();
+
+ @override
+ Future load(Locale locale) {
+ return SynchronousFuture(lookupAppLocale(locale));
+ }
+
+ @override
+ bool isSupported(Locale locale) => ['en', 'ru'].contains(locale.languageCode);
+
+ @override
+ bool shouldReload(_AppLocaleDelegate old) => false;
+}
+
+AppLocale lookupAppLocale(Locale locale) {
+
+
+ // Lookup logic when only language code is specified.
+ switch (locale.languageCode) {
+ 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.'
+ );
+}
diff --git a/lib/components/locale/l10n/app_locale_en.dart b/lib/components/locale/l10n/app_locale_en.dart
new file mode 100644
index 0000000..362a0d2
--- /dev/null
+++ b/lib/components/locale/l10n/app_locale_en.dart
@@ -0,0 +1,17 @@
+import 'app_locale.dart';
+
+// ignore_for_file: type=lint
+
+/// The translations for English (`en`).
+class AppLocaleEn extends AppLocale {
+ AppLocaleEn([String locale = 'en']) : super(locale);
+
+ @override
+ String get search => 'Search';
+
+ @override
+ String get liked => 'liked!';
+
+ @override
+ String get disliked => 'unliked';
+}
diff --git a/lib/components/locale/l10n/app_locale_ru.dart b/lib/components/locale/l10n/app_locale_ru.dart
new file mode 100644
index 0000000..e1485c0
--- /dev/null
+++ b/lib/components/locale/l10n/app_locale_ru.dart
@@ -0,0 +1,17 @@
+import 'app_locale.dart';
+
+// ignore_for_file: type=lint
+
+/// The translations for Russian (`ru`).
+class AppLocaleRu extends AppLocale {
+ AppLocaleRu([String locale = 'ru']) : super(locale);
+
+ @override
+ String get search => 'Поиск';
+
+ @override
+ String get liked => 'понравился!';
+
+ @override
+ String get disliked => 'разонравился';
+}
diff --git a/lib/components/resources.g.dart b/lib/components/resources.g.dart
new file mode 100644
index 0000000..915a8d9
--- /dev/null
+++ b/lib/components/resources.g.dart
@@ -0,0 +1,10 @@
+/// 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._();
+
+ static const String ASSETS_SVG_RU_SVG = 'assets/svg/ru.svg';
+
+ static const String ASSETS_SVG_US_SVG = 'assets/svg/us.svg';
+}
diff --git a/lib/components/utils/debounce.dart b/lib/components/utils/debounce.dart
new file mode 100644
index 0000000..c5d641a
--- /dev/null
+++ b/lib/components/utils/debounce.dart
@@ -0,0 +1,22 @@
+import 'dart:async';
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+
+class Debounce {
+ factory Debounce() => _instance;
+
+ Debounce._();
+
+ static final Debounce _instance = Debounce._();
+
+ static Timer? _timer;
+
+ static void run(
+ VoidCallback action, {
+ Duration delay = const Duration(milliseconds: 500),
+ }) {
+ _timer?.cancel();
+ _timer = Timer(delay, action);
+ }
+}
diff --git a/lib/components/utils/error_callback.dart b/lib/components/utils/error_callback.dart
new file mode 100644
index 0000000..5e3110f
--- /dev/null
+++ b/lib/components/utils/error_callback.dart
@@ -0,0 +1 @@
+typedef OnErrorCallback = void Function(String? error)?;
\ No newline at end of file
diff --git a/lib/data/dtos/movies_dto.dart b/lib/data/dtos/movies_dto.dart
new file mode 100644
index 0000000..25ae0e8
--- /dev/null
+++ b/lib/data/dtos/movies_dto.dart
@@ -0,0 +1,54 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'movies_dto.g.dart';
+
+@JsonSerializable(createToJson: false)
+class MoviesDto {
+ final List? docs;
+ final MoviePaginationDto? pagination;
+
+ const MoviesDto({this.docs, this.pagination});
+
+ factory MoviesDto.fromJson(Map json) =>
+ _$MoviesDtoFromJson(json);
+}
+
+@JsonSerializable(createToJson: false)
+class MoviePaginationDto {
+ @JsonKey(name: 'current_page')
+ final int? currentPage;
+ @JsonKey(name: 'has_next_page')
+ final bool? hasNextPage;
+ @JsonKey(name: 'last_visible_page')
+ final int? lastVisiblePage;
+
+ const MoviePaginationDto({this.currentPage, this.hasNextPage, this.lastVisiblePage});
+
+ factory MoviePaginationDto.fromJson(Map json) =>
+ _$MoviePaginationDtoFromJson(json);
+}
+
+@JsonSerializable(createToJson: false)
+class MovieDataDto {
+ @JsonKey(name: 'id')
+ final int? id; // Это поле может использоваться для идентификации фильма
+ final String? name; // Название фильма
+ final String? description; // Описание фильма
+ final PosterDto? poster; // Постер фильма
+
+ const MovieDataDto(this.name, this.description, this.poster, {this.id});
+
+ factory MovieDataDto.fromJson(Map json) =>
+ _$MovieDataDtoFromJson(json);
+}
+
+@JsonSerializable(createToJson: false)
+class PosterDto {
+ final String? url; // URL постера
+ final String? previewUrl; // URL миниатюры постера
+
+ const PosterDto({this.url, this.previewUrl});
+
+ factory PosterDto.fromJson(Map json) =>
+ _$PosterDtoFromJson(json);
+}
\ No newline at end of file
diff --git a/lib/data/dtos/movies_dto.g.dart b/lib/data/dtos/movies_dto.g.dart
new file mode 100644
index 0000000..b328948
--- /dev/null
+++ b/lib/data/dtos/movies_dto.g.dart
@@ -0,0 +1,38 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'movies_dto.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+MoviesDto _$MoviesDtoFromJson(Map json) => MoviesDto(
+ docs: (json['docs'] as List?)
+ ?.map((e) => MovieDataDto.fromJson(e as Map))
+ .toList(),
+ pagination: json['pagination'] == null
+ ? null
+ : MoviePaginationDto.fromJson(
+ json['pagination'] as Map),
+ );
+
+MoviePaginationDto _$MoviePaginationDtoFromJson(Map json) =>
+ MoviePaginationDto(
+ currentPage: (json['current_page'] as num?)?.toInt(),
+ hasNextPage: json['has_next_page'] as bool?,
+ lastVisiblePage: (json['last_visible_page'] as num?)?.toInt(),
+ );
+
+MovieDataDto _$MovieDataDtoFromJson(Map json) => MovieDataDto(
+ json['name'] as String?,
+ json['description'] as String?,
+ json['poster'] == null
+ ? null
+ : PosterDto.fromJson(json['poster'] as Map),
+ id: (json['id'] as num?)?.toInt(),
+ );
+
+PosterDto _$PosterDtoFromJson(Map json) => PosterDto(
+ url: json['url'] as String?,
+ previewUrl: json['previewUrl'] as String?,
+ );
diff --git a/lib/data/mappers/movies_mapper.dart b/lib/data/mappers/movies_mapper.dart
new file mode 100644
index 0000000..366ef38
--- /dev/null
+++ b/lib/data/mappers/movies_mapper.dart
@@ -0,0 +1,21 @@
+import 'package:pmd_labs/data/dtos/movies_dto.dart';
+import 'package:pmd_labs/domain/models/carddata.dart';
+import 'package:pmd_labs/presentation/home_page/home_page.dart';
+
+extension MovieDataDtoMapper on MovieDataDto {
+ CardData toDomain() => CardData(
+ name ?? 'UNKNOWN', // Исправлено с title на name
+ imageUrl: poster?.url, // Обратите внимание, что используем правильно поле
+ id: id?.toString() ?? '0', // Защита от null, если id нет
+ descriptionText: description ?? 'Нет описания', // Используем реальное описание
+ );
+}
+
+extension MoviesDtoToModel on MoviesDto {
+ HomeData toDomain() => HomeData(
+ data: docs?.map((e) => e.toDomain()).toList(), // Изменено с data на docs
+ nextPage: (pagination?.hasNextPage ?? false)
+ ? ((pagination?.currentPage ?? 0) + 1)
+ : null
+ );
+}
\ No newline at end of file
diff --git a/lib/data/repositories/api_interface.dart b/lib/data/repositories/api_interface.dart
index 25787ca..a7af9a4 100644
--- a/lib/data/repositories/api_interface.dart
+++ b/lib/data/repositories/api_interface.dart
@@ -1,7 +1,6 @@
-import 'package:pmd_labs/domain/models/card.dart';
-
-typedef OnErrorCallback = void Function(String? error);
+import 'package:pmd_labs/components/utils/error_callback.dart';
+import 'package:pmd_labs/presentation/home_page/home_page.dart';
abstract class ApiInterface {
- Future?> loadData({OnErrorCallback? onError});
+ Future loadData({OnErrorCallback? onError});
}
\ No newline at end of file
diff --git a/lib/data/repositories/mock_repository.dart b/lib/data/repositories/mock_repository.dart
index dfac6bd..ad0f969 100644
--- a/lib/data/repositories/mock_repository.dart
+++ b/lib/data/repositories/mock_repository.dart
@@ -1,35 +1,18 @@
-import 'package:flutter/material.dart';
+import 'package:pmd_labs/components/utils/error_callback.dart';
import 'package:pmd_labs/data/repositories/api_interface.dart';
-import 'package:pmd_labs/domain/models/card.dart';
+import 'package:pmd_labs/domain/models/carddata.dart';
+import 'package:pmd_labs/presentation/home_page/home_page.dart';
class MockRepository extends ApiInterface {
@override
- Future?> loadData({OnErrorCallback? onError}) async {
- return [
- CardData(
- 'Анекдот №1',
- descriptionText: '— Итак, Сара, вот ваша диета: одно яблоко, одно вареное яйцо, нежирный творог, зелень\n'
- '— Ясно, доктор, но это до или после еды?',
- icon: Icons.favorite,
- imageUrl: 'https://i.pinimg.com/474x/97/ee/af/97eeaffa3171b0a53bb4161bfc9e3756.jpg',
- ),
- CardData(
- 'Анекдот №2',
- descriptionText: '– Яна Моисеевна, а что вам муженек на день рождения подарил?\n'
- '– Вон видите феррари за окном стоит?\n'
- '– Не может быть!\n'
- '– Вот точно такого же цвета шарфик.\n',
- icon: Icons.favorite,
- imageUrl:
- 'https://i.pinimg.com/474x/8c/4f/40/8c4f4093ab3ff9ddb1d8880a94b4d586.jpg',
- ),
- CardData(
- 'Анекдот №3',
- descriptionText: 'Чем дольше звонят в дверь, тем больше Рабиновича нет дома.',
- icon: Icons.favorite,
- imageUrl:
- 'https://i.pinimg.com/474x/3a/1f/3c/3a1f3ca4a57ff8a16882093c2f278922.jpg',
- ),
- ];
+ Future loadData({OnErrorCallback? onError}) async {
+ return HomeData(
+ data: [
+ CardData('JoJo’s Bizarre Adventure', descriptionText: 'kono dio da', imageUrl: 'https://i1.sndcdn.com/avatars-253MmMf9QZzxVBJi-rvlyeg-t1080x1080.jpg'),
+ CardData('Example', descriptionText: 'what is this?', imageUrl: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQvaBQ6nAedlqvXsh-dLXZi2Gexy1RkDbTUKQ&s'),
+ CardData('Mock data', descriptionText: 'Mock data description', imageUrl: 'https://cdn-user30887.skyeng.ru/uploads/6692a339c6989979804399.png'),
+ ],
+ );
}
+
}
\ No newline at end of file
diff --git a/lib/data/repositories/movie_repository.dart b/lib/data/repositories/movie_repository.dart
new file mode 100644
index 0000000..def30c1
--- /dev/null
+++ b/lib/data/repositories/movie_repository.dart
@@ -0,0 +1,53 @@
+import 'package:pmd_labs/components/utils/error_callback.dart';
+import 'package:pmd_labs/data/dtos/movies_dto.dart';
+import 'package:pmd_labs/data/mappers/movies_mapper.dart';
+import 'package:pmd_labs/data/repositories/api_interface.dart';
+import 'package:dio/dio.dart';
+import 'package:pmd_labs/presentation/home_page/home_page.dart';
+import 'package:pretty_dio_logger/pretty_dio_logger.dart';
+
+class MovieRepository extends ApiInterface {
+ static final Dio _dio = Dio()
+ ..interceptors.add(PrettyDioLogger(
+ requestHeader: true,
+ requestBody: true,
+ ));
+
+ static const String _baseUrl = 'https://api.kinopoisk.dev';
+ static const String _apiKey = 'HQTFY5N-8D34FT0-HXQQQ1S-KPREHDX'; // Ваш API-ключ
+
+ @override
+ Future loadData({
+ OnErrorCallback? onError,
+ String? q,
+ int page = 1,
+ int pageSize = 15,
+ }) async {
+ try {
+ const String url = '$_baseUrl/v1.4/movie/search';
+
+ final Response response = await _dio.get