diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bafc7ff
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+gen:
+ flutter pub run build_runner build --delete-conflicting-outputs
+
+icon:
+ flutter pub run flutter_launcher_icons:main
+
+hello:
+ echo "Hi!"; \
+ echo "I`m makefile"; \
+ echo "^_^"
+
+init_res:
+ dart pub global activate flutter_asset_generator
+
+format:
+ dart format . --line-length 100
+
+activate_fgen:
+ flutter pub global activate fgen
+
+res:
+ fgen --output lib/components/resources.g.dart --no-watch --no-preview; \
+ make format
+
+loc:
+ flutter gen-l10n && \
+ make format
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..2e647e6 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..c877e86 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..8755825 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..698cf09 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..2c297f4 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/svg/ru.svg b/assets/svg/ru.svg
new file mode 100644
index 0000000..b8a353e
--- /dev/null
+++ b/assets/svg/ru.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/assets/svg/uk.svg b/assets/svg/uk.svg
new file mode 100644
index 0000000..f87fdb0
--- /dev/null
+++ b/assets/svg/uk.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/assets/wp.png b/assets/wp.png
new file mode 100644
index 0000000..693ac14
Binary files /dev/null and b/assets/wp.png differ
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..b26d33d
--- /dev/null
+++ b/l10n/app_en.arb
@@ -0,0 +1,7 @@
+{
+ "@@locale": "en",
+ "search": "Search",
+ "liked": "liked!",
+ "disliked": "disliked :(",
+ "arbEnding": "Чтобы не забыть про отсутствие запятой :)"
+}
\ No newline at end of file
diff --git a/l10n/app_ru.arb b/l10n/app_ru.arb
new file mode 100644
index 0000000..b119483
--- /dev/null
+++ b/l10n/app_ru.arb
@@ -0,0 +1,7 @@
+{
+ "@@locale": "ru",
+ "search": "Поиск",
+ "liked": "понравился!",
+ "disliked": "разонравился :(",
+ "arbEnding": "Чтобы не забыть про отсутствие запятой :)"
+}
\ No newline at end of file
diff --git a/lib/components/extensions/context_x.dart b/lib/components/extensions/context_x.dart
new file mode 100644
index 0000000..3c0d586
--- /dev/null
+++ b/lib/components/extensions/context_x.dart
@@ -0,0 +1,6 @@
+import 'package:flutter/widgets.dart';
+import 'package:pmd_lab/components/locale/l10n/app_locale.dart';
+
+extension LocalContextX on BuildContext {
+ AppLocale get locale => AppLocale.of(this)!;
+}
\ No newline at end of file
diff --git a/lib/components/locale/l10n/app_locale.dart b/lib/components/locale/l10n/app_locale.dart
new file mode 100644
index 0000000..0922eb1
--- /dev/null
+++ b/lib/components/locale/l10n/app_locale.dart
@@ -0,0 +1,153 @@
+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;
+
+ /// No description provided for @arbEnding.
+ ///
+ /// In ru, this message translates to:
+ /// **'Чтобы не забыть про отсутствие запятой :)'**
+ String get arbEnding;
+}
+
+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..599548c
--- /dev/null
+++ b/lib/components/locale/l10n/app_locale_en.dart
@@ -0,0 +1,20 @@
+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 => 'disliked :(';
+
+ @override
+ String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)';
+}
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..b8d5444
--- /dev/null
+++ b/lib/components/locale/l10n/app_locale_ru.dart
@@ -0,0 +1,20 @@
+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 => 'разонравился :(';
+
+ @override
+ String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)';
+}
diff --git a/lib/components/resources.g.dart b/lib/components/resources.g.dart
new file mode 100644
index 0000000..02eed01
--- /dev/null
+++ b/lib/components/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._();
+
+ /// ![preview](file://C:\Users\Natalia\Desktop\5semestr\PMD\pmd_lab\assets\svg\ru.svg)
+ static const String ASSETS_SVG_RU_SVG = 'assets/svg/ru.svg';
+
+ /// ![preview](file://C:\Users\Natalia\Desktop\5semestr\PMD\pmd_lab\assets\svg\uk.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 a28b3cc..aec7c08 100644
--- a/lib/components/utils/debounce.dart
+++ b/lib/components/utils/debounce.dart
@@ -9,9 +9,9 @@ class Debounce {
static Timer? _timer;
static void run(
- VoidCallback action, {
- Duration delay = const Duration(milliseconds: 500),
- }) {
+ 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 adb13d4..cf888e9 100644
--- a/lib/data/dtos/characters_dto.dart
+++ b/lib/data/dtos/characters_dto.dart
@@ -2,9 +2,8 @@ import 'package:json_annotation/json_annotation.dart';
part 'characters_dto.g.dart';
-
@JsonSerializable(createToJson: false)
-class CharactersDto{
+class CharactersDto {
final List? data;
final MetaDto? meta;
const CharactersDto({this.data, this.meta});
@@ -12,23 +11,25 @@ class CharactersDto{
}
@JsonSerializable(createToJson: false)
-class CharactersDataDto{
+class CharactersDataDto {
final String? id;
final String? type;
final CharasterAttributesDataDto? attributes;
const CharactersDataDto({this.id, this.type, this.attributes});
- factory CharactersDataDto.fromJson(Map json) => _$CharactersDataDtoFromJson(json);
+ factory CharactersDataDto.fromJson(Map json) =>
+ _$CharactersDataDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
-class CharasterAttributesDataDto{
+class CharasterAttributesDataDto {
final String? name;
final String? born;
final String? died;
final String? image;
const CharasterAttributesDataDto({this.name, this.born, this.died, this.image});
- factory CharasterAttributesDataDto.fromJson(Map json) => _$CharasterAttributesDataDtoFromJson(json);
+ factory CharasterAttributesDataDto.fromJson(Map json) =>
+ _$CharasterAttributesDataDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
@@ -49,4 +50,4 @@ class PaginationDto {
const PaginationDto({this.current, this.next, this.last});
factory PaginationDto.fromJson(Map json) => _$PaginationDtoFromJson(json);
-}
\ No newline at end of file
+}
diff --git a/lib/data/mappes/characters_mapper.dart b/lib/data/mappes/characters_mapper.dart
index 8f6ecb6..e132916 100644
--- a/lib/data/mappes/characters_mapper.dart
+++ b/lib/data/mappes/characters_mapper.dart
@@ -2,30 +2,29 @@ import 'package:pmd_lab/data/dtos/characters_dto.dart';
import 'package:pmd_lab/domain/models/card.dart';
import 'package:pmd_lab/domain/models/home.dart';
-
const _imagePlaceholder = 'https://cdn-icons-png.flaticon.com/512/4054/4054617.png';
-
extension CharacterDataDtoToModel on CharactersDataDto {
CardData toDomain() => CardData(
- attributes?.name ?? 'UNKNOWN',
- imgUrl: attributes?.image ?? _imagePlaceholder,
- description: _makeDescription(attributes?.born, attributes?.died),
- );
- String _makeDescription(String? born, String? died){
+ attributes?.name ?? 'UNKNOWN',
+ imgUrl: attributes?.image ?? _imagePlaceholder,
+ description: _makeDescription(attributes?.born, attributes?.died),
+ id: id,
+ );
+ String _makeDescription(String? born, String? died) {
return born != null && died != null
? '$born - $died'
: born != null
- ? 'born: $born'
- : died != null
- ? 'died: $died'
- : '';
+ ? 'born: $born'
+ : died != null
+ ? 'died: $died'
+ : '';
}
}
extension CharactersDtoToModel on CharactersDto {
HomeData toDomain() => HomeData(
- data: data?.map((e) => e.toDomain()).toList(),
- nextPage: meta?.pagination?.next,
- );
+ data: data?.map((e) => e.toDomain()).toList(),
+ nextPage: meta?.pagination?.next,
+ );
}
diff --git a/lib/domain/models/card.dart b/lib/domain/models/card.dart
index d449320..7b55800 100644
--- a/lib/domain/models/card.dart
+++ b/lib/domain/models/card.dart
@@ -2,10 +2,12 @@ class CardData {
final String text;
final String description;
final String? imgUrl;
+ final String? id;
CardData(
- this.text, {
- required this.description,
- this.imgUrl,
- });
-}
\ No newline at end of file
+ this.text, {
+ required this.description,
+ this.imgUrl,
+ this.id,
+ });
+}
diff --git a/lib/main.dart b/lib/main.dart
index dafc92e..6824431 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,7 +1,14 @@
+import 'dart:io';
+
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:pmd_lab/components/locale/l10n/app_locale.dart';
import 'package:pmd_lab/presentation/home_page/bloc/bloc.dart';
+
import 'package:pmd_lab/presentation/home_page/home_page.dart';
+import 'package:pmd_lab/presentation/like_bloc/like_bloc.dart';
+import 'package:pmd_lab/presentation/locale_bloc/locale_bloc.dart';
+import 'package:pmd_lab/presentation/locale_bloc/locale_state.dart';
import 'package:pmd_lab/repositories/potter_repository.dart';
void main() {
@@ -13,21 +20,37 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return MaterialApp(
- title: 'Flutter Demo',
- theme: ThemeData(
- // colorScheme: ColorScheme.fromSeed(seedColor: Colors.black), colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
- useMaterial3: true,
+ return BlocProvider(
+ lazy: false,
+ create: (context) => LocaleBloc(Locale(Platform.localeName)),
+ child: BlocBuilder(
+ builder: (context, state) {
+ return MaterialApp(
+ title: 'Potter App',
+ locale: state.currentLocale,
+ localizationsDelegates: AppLocale.localizationsDelegates,
+ supportedLocales: AppLocale.supportedLocales,
+ theme: ThemeData(
+ primarySwatch: Colors.orange,
+ scaffoldBackgroundColor: Colors.white,
+ useMaterial3: true,
+ ),
+ home: RepositoryProvider(
+ lazy: true,
+ create: (_) => PotterRepository(),
+ child: BlocProvider(
+ lazy: false,
+ create: (context) => LikeBloc(),
+ child: BlocProvider(
+ lazy: false,
+ create: (context) => HomeBloc(context.read()),
+ child: const MyHomePage(),
+ ),
+ ),
+ ),
+ );
+ },
),
- home: RepositoryProvider(
- lazy: true,
- create: (_) => PotterRepository(),
- child: BlocProvider(
- lazy: true,
- create: (context) => HomeBloc(context.read()),
- child: const MyHomePage(title: 'Potter API'),
- ),
- )
);
}
}
diff --git a/lib/presentation/common/svg_objects.dart b/lib/presentation/common/svg_objects.dart
new file mode 100644
index 0000000..0398b8f
--- /dev/null
+++ b/lib/presentation/common/svg_objects.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/widgets.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:pmd_lab/components/resources.g.dart';
+
+abstract class SvgObjects {
+ static void init() {
+ final pics = [
+ R.ASSETS_SVG_RU_SVG,
+ R.ASSETS_SVG_UK_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 SvgUk extends StatelessWidget {
+ const SvgUk({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SvgPicture.asset(R.ASSETS_SVG_UK_SVG);
+ }
+}
diff --git a/lib/presentation/details_page/details_page.dart b/lib/presentation/details_page/details_page.dart
index 61fc791..ac6d191 100644
--- a/lib/presentation/details_page/details_page.dart
+++ b/lib/presentation/details_page/details_page.dart
@@ -9,27 +9,28 @@ class DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
- appBar: AppBar(),
- body:Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Padding(
- padding: const EdgeInsets.only(bottom: 16),
- child: Image.network(data.imgUrl ?? '',),
- ),
- Padding(
- padding: const EdgeInsets.only(bottom: 4),
- child: Text(
+ appBar: AppBar(),
+ body: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 16),
+ child: Image.network(
+ data.imgUrl ?? '',
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(bottom: 4),
+ child: Text(
data.text,
style: Theme.of(context).textTheme.headlineLarge,
+ ),
),
- ),
- Text(
+ Text(
data.description,
style: Theme.of(context).textTheme.bodyLarge,
- )
- ],
- )
- );
+ )
+ ],
+ ));
}
}
diff --git a/lib/presentation/home_page/bloc/bloc.dart b/lib/presentation/home_page/bloc/bloc.dart
index 5024834..9243871 100644
--- a/lib/presentation/home_page/bloc/bloc.dart
+++ b/lib/presentation/home_page/bloc/bloc.dart
@@ -36,6 +36,4 @@ class HomeBloc extends Bloc {
error: error,
));
}
-
}
-
diff --git a/lib/presentation/home_page/bloc/events.dart b/lib/presentation/home_page/bloc/events.dart
index 26a31be..b1cff65 100644
--- a/lib/presentation/home_page/bloc/events.dart
+++ b/lib/presentation/home_page/bloc/events.dart
@@ -1,11 +1,9 @@
-
class HomeEvent {
const HomeEvent();
}
-class HomeLoadDataEvent extends HomeEvent{
+class HomeLoadDataEvent extends HomeEvent {
final String? search;
final int? nextPage;
const HomeLoadDataEvent({this.search, this.nextPage});
-
-}
\ No newline at end of file
+}
diff --git a/lib/presentation/home_page/bloc/state.dart b/lib/presentation/home_page/bloc/state.dart
index 10269be..8b812ef 100644
--- a/lib/presentation/home_page/bloc/state.dart
+++ b/lib/presentation/home_page/bloc/state.dart
@@ -1,6 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:copy_with_extension/copy_with_extension.dart';
-import 'package:pmd_lab/domain/models/card.dart';
import 'package:pmd_lab/domain/models/home.dart';
part 'state.g.dart';
@@ -34,11 +33,9 @@ class HomeState extends Equatable {
@override
List