diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
index db77bb4..63d892c 100644
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
index 17987b7..313ab8e 100644
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index 09d4391..172785d 100644
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index d5f1c8d..b075f5b 100644
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index 4d6372e..9fbeed2 100644
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/assets/app_icon.png b/assets/app_icon.png
new file mode 100644
index 0000000..1586fd0
Binary files /dev/null and b/assets/app_icon.png 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/l10n.yaml b/l10n.yaml
new file mode 100644
index 0000000..810ca4a
--- /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/l10n
+output-class: AppLocale
+synthetic-package: false
diff --git a/l10n/app_en.arb b/l10n/app_en.arb
new file mode 100644
index 0000000..b39a396
--- /dev/null
+++ b/l10n/app_en.arb
@@ -0,0 +1,7 @@
+{
+ "@@locale": "en",
+
+ "cardLiked": "Card added in favourite",
+ "cardDisliked": "Card deleted from favourite",
+ "searchInputPlaceholder": "Search..."
+}
\ No newline at end of file
diff --git a/l10n/app_ru.arb b/l10n/app_ru.arb
new file mode 100644
index 0000000..4bacd91
--- /dev/null
+++ b/l10n/app_ru.arb
@@ -0,0 +1,7 @@
+{
+ "@@locale": "ru",
+
+ "cardLiked": "Карточка добавлена в Избранное",
+ "cardDisliked": "Карточка удалена из Избранного",
+ "searchInputPlaceholder": "Поиск..."
+}
\ No newline at end of file
diff --git a/lib/components/extensions/local_context_x.dart b/lib/components/extensions/local_context_x.dart
new file mode 100644
index 0000000..52a9a2e
--- /dev/null
+++ b/lib/components/extensions/local_context_x.dart
@@ -0,0 +1,6 @@
+import 'package:flutter/cupertino.dart';
+import 'package:lab/components/l10n/app_locale.dart';
+
+extension LocalContextX on BuildContext {
+ AppLocale get locale => AppLocale.of(this)!;
+}
diff --git a/lib/components/l10n/app_locale.dart b/lib/components/l10n/app_locale.dart
new file mode 100644
index 0000000..33fc87a
--- /dev/null
+++ b/lib/components/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 @cardLiked.
+ ///
+ /// In ru, this message translates to:
+ /// **'Карточка добавлена в Избранное'**
+ String get cardLiked;
+
+ /// No description provided for @cardDisliked.
+ ///
+ /// In ru, this message translates to:
+ /// **'Карточка удалена из Избранного'**
+ String get cardDisliked;
+
+ /// No description provided for @searchInputPlaceholder.
+ ///
+ /// In ru, this message translates to:
+ /// **'Поиск...'**
+ String get searchInputPlaceholder;
+}
+
+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/l10n/app_locale_en.dart b/lib/components/l10n/app_locale_en.dart
new file mode 100644
index 0000000..c6084cd
--- /dev/null
+++ b/lib/components/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 cardLiked => 'Card added in favourite';
+
+ @override
+ String get cardDisliked => 'Card deleted from favourite';
+
+ @override
+ String get searchInputPlaceholder => 'Search...';
+}
diff --git a/lib/components/l10n/app_locale_ru.dart b/lib/components/l10n/app_locale_ru.dart
new file mode 100644
index 0000000..7191228
--- /dev/null
+++ b/lib/components/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 cardLiked => 'Карточка добавлена в Избранное';
+
+ @override
+ String get cardDisliked => 'Карточка удалена из Избранного';
+
+ @override
+ String get searchInputPlaceholder => 'Поиск...';
+}
diff --git a/lib/components/resources/resources.g.dart b/lib/components/resources/resources.g.dart
new file mode 100644
index 0000000..cfeb1ff
--- /dev/null
+++ b/lib/components/resources/resources.g.dart
@@ -0,0 +1,12 @@
+/// 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_UK_SVG = 'assets/svg/uk.svg';
+}
diff --git a/lib/components/utils/debounce.dart b/lib/components/utils/debounce.dart
index 6ebc241..e82ea45 100644
--- a/lib/components/utils/debounce.dart
+++ b/lib/components/utils/debounce.dart
@@ -12,8 +12,7 @@ class Debounce {
static Timer? _timer;
static void run(
- {required VoidCallback action,
- Duration delay = const Duration(milliseconds: 500)}) {
+ {required VoidCallback action, Duration delay = const Duration(milliseconds: 500)}) {
_timer?.cancel();
_timer = Timer(delay, action);
}
diff --git a/lib/data/dtos/characters_dto.dart b/lib/data/dtos/characters_dto.dart
index dbf04a1..3dae693 100644
--- a/lib/data/dtos/characters_dto.dart
+++ b/lib/data/dtos/characters_dto.dart
@@ -11,8 +11,7 @@ class CharactersDto {
const CharactersDto({this.data, this.meta});
- factory CharactersDto.fromJson(Map json) =>
- _$CharactersDtoFromJson(json);
+ factory CharactersDto.fromJson(Map json) => _$CharactersDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
@@ -21,8 +20,7 @@ class MetaDto {
const MetaDto({this.pagination});
- factory MetaDto.fromJson(Map json) =>
- _$MetaDtoFromJson(json);
+ factory MetaDto.fromJson(Map json) => _$MetaDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
@@ -33,8 +31,7 @@ class PaginationDto {
const PaginationDto({this.current, this.last, this.next});
- factory PaginationDto.fromJson(Map json) =>
- _$PaginationDtoFromJson(json);
+ factory PaginationDto.fromJson(Map json) => _$PaginationDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
@@ -44,8 +41,7 @@ class CharacterDataDto {
const CharacterDataDto(this.id, this.attributes);
- factory CharacterDataDto.fromJson(Map json) =>
- _$CharacterDataDtoFromJson(json);
+ factory CharacterDataDto.fromJson(Map json) => _$CharacterDataDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
diff --git a/lib/data/dtos/characters_dto.g.dart b/lib/data/dtos/characters_dto.g.dart
index 06ce41d..b221805 100644
--- a/lib/data/dtos/characters_dto.g.dart
+++ b/lib/data/dtos/characters_dto.g.dart
@@ -6,14 +6,11 @@ part of 'characters_dto.dart';
// JsonSerializableGenerator
// **************************************************************************
-CharactersDto _$CharactersDtoFromJson(Map json) =>
- CharactersDto(
+CharactersDto _$CharactersDtoFromJson(Map json) => CharactersDto(
data: (json['data'] as List?)
?.map((e) => CharacterDataDto.fromJson(e as Map))
.toList(),
- meta: json['meta'] == null
- ? null
- : MetaDto.fromJson(json['meta'] as Map),
+ meta: json['meta'] == null ? null : MetaDto.fromJson(json['meta'] as Map),
);
MetaDto _$MetaDtoFromJson(Map json) => MetaDto(
@@ -22,24 +19,20 @@ MetaDto _$MetaDtoFromJson(Map json) => MetaDto(
: PaginationDto.fromJson(json['pagination'] as Map),
);
-PaginationDto _$PaginationDtoFromJson(Map json) =>
- PaginationDto(
+PaginationDto _$PaginationDtoFromJson(Map json) => PaginationDto(
current: (json['current'] as num?)?.toInt(),
last: (json['last'] as num?)?.toInt(),
next: (json['next'] as num?)?.toInt(),
);
-CharacterDataDto _$CharacterDataDtoFromJson(Map json) =>
- CharacterDataDto(
+CharacterDataDto _$CharacterDataDtoFromJson(Map json) => CharacterDataDto(
json['id'] as String?,
json['attributes'] == null
? null
- : CharacterAttributesDataDto.fromJson(
- json['attributes'] as Map),
+ : CharacterAttributesDataDto.fromJson(json['attributes'] as Map),
);
-CharacterAttributesDataDto _$CharacterAttributesDataDtoFromJson(
- Map json) =>
+CharacterAttributesDataDto _$CharacterAttributesDataDtoFromJson(Map json) =>
CharacterAttributesDataDto(
json['name'] as String?,
json['image'] as String?,
diff --git a/lib/data/mappers/characters_mapper.dart b/lib/data/mappers/characters_mapper.dart
index 7c94908..0ca1411 100644
--- a/lib/data/mappers/characters_mapper.dart
+++ b/lib/data/mappers/characters_mapper.dart
@@ -4,6 +4,7 @@ import 'package:lab/domain/models/home_data.dart';
extension CharacterDataDtoToModel on CharacterDataDto {
CardData toDomain() => CardData(
+ id: id,
name: attributes?.name ?? "UNKNOWN",
image: attributes?.image ??
"https://upload.wikimedia.org/wikipedia/commons/a/a2/Person_Image_Placeholder.png",
@@ -11,7 +12,6 @@ extension CharacterDataDtoToModel on CharacterDataDto {
}
extension ChatactersDtoToModel on CharactersDto {
- HomeData toDomain() => HomeData(
- data: data?.map((e) => e.toDomain()).toList(),
- nextPage: meta?.pagination?.next);
+ HomeData toDomain() =>
+ HomeData(data: data?.map((e) => e.toDomain()).toList(), nextPage: meta?.pagination?.next);
}
diff --git a/lib/data/repositories/mock_repository.dart b/lib/data/repositories/mock_repository.dart
index 3e3814a..49a1713 100644
--- a/lib/data/repositories/mock_repository.dart
+++ b/lib/data/repositories/mock_repository.dart
@@ -7,11 +7,13 @@ class MockRepository extends ApiInterface {
Future loadData({OnErrorCallback? onError}) async {
return HomeData(data: [
const CardData(
+ id: '0',
name: "test 0",
species: "Species 0",
image:
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTtAT11wKgHrJBUYzIBFogucXg0a9fE0fQXDQ&s"),
const CardData(
+ id: "1",
name: "test 1",
species: "Species 1",
image:
diff --git a/lib/data/repositories/potter_repository.dart b/lib/data/repositories/potter_repository.dart
index 1456872..393d22b 100644
--- a/lib/data/repositories/potter_repository.dart
+++ b/lib/data/repositories/potter_repository.dart
@@ -7,33 +7,23 @@ import 'package:lab/domain/models/home_data.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class PotterRepository extends ApiInterface {
- static final Dio _dio = Dio(
- BaseOptions(connectTimeout: const Duration(seconds: 10)))
- ..interceptors.add(
- PrettyDioLogger(request: true, requestHeader: true, requestBody: true));
+ static final Dio _dio = Dio(BaseOptions(connectTimeout: const Duration(seconds: 10)))
+ ..interceptors.add(PrettyDioLogger(request: true, requestHeader: true, requestBody: true));
static const String _baseUrl = "https://api.potterdb.com/v1";
@override
Future loadData(
- {OnErrorCallback? onError,
- String? q,
- int page = 1,
- int pageSize = 10}) async {
+ {OnErrorCallback? onError, String? q, int page = 1, int pageSize = 10}) async {
try {
const String url = '$_baseUrl/characters';
final Response response = await _dio.get