From d10601b204d4c371b78a4a96d0275bf935d4d3b6 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 17 Dec 2024 17:27:25 +0400 Subject: [PATCH] changes --- Makefile.mk | 15 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 5580 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 3429 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 7304 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 10235 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 13560 bytes assets/icon.png | Bin 0 -> 13678 bytes assets/svg/ru.svg | 1 + assets/svg/us.svg | 9 + l10n.yaml | 6 + l10n/app_en.arb | 9 + l10n/app_ru.arb | 9 + lib/components/extensions/context_x.dart | 6 + lib/components/locale/l10n/app_locale.dart | 153 ++++++++++ lib/components/locale/l10n/app_locale_en.dart | 20 ++ lib/components/locale/l10n/app_locale_ru.dart | 20 ++ lib/components/resources.g.dart | 10 + lib/components/utils/debounce.dart | 20 ++ lib/data/dtos/PlaceDTO.dart | 4 +- lib/data/mappers/PlaceDataDtoToModel.dart | 19 +- lib/data/repositories/MockRepository.dart | 20 +- lib/data/repositories/PlacesRepository.dart | 39 +-- lib/data/repositories/api_Interface.dart | 2 +- lib/main.dart | 48 ++- lib/models/CardData.dart | 16 +- .../details_page/details_page.dart | 12 +- lib/presentation/home_page/MyCard.dart | 94 +++--- lib/presentation/home_page/bloc/bloc.dart | 26 ++ lib/presentation/home_page/bloc/events.dart | 9 + lib/presentation/home_page/bloc/state.dart | 25 ++ lib/presentation/home_page/bloc/state.g.dart | 78 +++++ lib/presentation/home_page/home_page.dart | 179 ++++++++--- .../home_page/like_bloc/like_bloc.dart | 36 +++ .../home_page/like_bloc/like_event.dart | 13 + .../home_page/like_bloc/like_state.dart | 14 + .../home_page/like_bloc/like_state.g.dart | 56 ++++ lib/presentation/locale_bloc/locale_bloc.dart | 18 ++ .../locale_bloc/locale_event.dart | 7 + .../locale_bloc/locale_state.dart | 14 + .../locale_bloc/locale_state.g.dart | 58 ++++ lib/presentation/svg_object.dart | 34 +++ pubspec.lock | 284 +++++++++++++++++- pubspec.yaml | 23 +- 43 files changed, 1243 insertions(+), 163 deletions(-) create mode 100644 Makefile.mk create mode 100644 assets/icon.png create mode 100644 assets/svg/ru.svg create mode 100644 assets/svg/us.svg create mode 100644 l10n.yaml create mode 100644 l10n/app_en.arb create mode 100644 l10n/app_ru.arb create mode 100644 lib/components/extensions/context_x.dart create mode 100644 lib/components/locale/l10n/app_locale.dart create mode 100644 lib/components/locale/l10n/app_locale_en.dart create mode 100644 lib/components/locale/l10n/app_locale_ru.dart create mode 100644 lib/components/resources.g.dart create mode 100644 lib/components/utils/debounce.dart create mode 100644 lib/presentation/home_page/bloc/bloc.dart create mode 100644 lib/presentation/home_page/bloc/events.dart create mode 100644 lib/presentation/home_page/bloc/state.dart create mode 100644 lib/presentation/home_page/bloc/state.g.dart create mode 100644 lib/presentation/home_page/like_bloc/like_bloc.dart create mode 100644 lib/presentation/home_page/like_bloc/like_event.dart create mode 100644 lib/presentation/home_page/like_bloc/like_state.dart create mode 100644 lib/presentation/home_page/like_bloc/like_state.g.dart create mode 100644 lib/presentation/locale_bloc/locale_bloc.dart create mode 100644 lib/presentation/locale_bloc/locale_event.dart create mode 100644 lib/presentation/locale_bloc/locale_state.dart create mode 100644 lib/presentation/locale_bloc/locale_state.g.dart create mode 100644 lib/presentation/svg_object.dart diff --git a/Makefile.mk b/Makefile.mk new file mode 100644 index 0000000..85df52d --- /dev/null +++ b/Makefile.mk @@ -0,0 +1,15 @@ +gen: + flutter pub run build_runner build --delete-conflicting-outputs +icon: + flutter pub run flutter_launcher_icons:main +init_res: + dart pub global activate flutter_asset_generator + flutter pub global activate fgen +format: + dart format . --line-length 100 +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 db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..8cf393bdbfad619c1bf820e3a7b465db26aafc85 100644 GIT binary patch literal 5580 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 amHrEiA2aAI>R)aE0000o literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ 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 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..73676ed1b79bdb76667451abf46d1418cb0295f0 100644 GIT binary patch literal 3429 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 Hu0mjfJg}H= literal 442 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 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 d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..2b2f0d292d59e25e0b585c852bbd5c3c76832135 100644 GIT binary patch literal 10235 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 BzR&;w literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof 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 4d6372eebdb28e45604e46eeda8dd24651419bc0..883ce22ccfcf6c1b3a71f94bbbabb3cd651b3454 100644 GIT binary patch literal 13560 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 yn^7E%NKR=n7@c_ literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bb96b6588e2725b3e6cd495b5c0aa969254caadf GIT binary patch literal 13678 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 literal 0 HcmV?d00001 diff --git a/assets/svg/ru.svg b/assets/svg/ru.svg new file mode 100644 index 0000000..855b805 --- /dev/null +++ b/assets/svg/ru.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/svg/us.svg b/assets/svg/us.svg new file mode 100644 index 0000000..9cfd0c9 --- /dev/null +++ b/assets/svg/us.svg @@ -0,0 +1,9 @@ + + + + + + + + + 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..a01051c --- /dev/null +++ b/l10n/app_en.arb @@ -0,0 +1,9 @@ +{ + "@@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..7b30d41 --- /dev/null +++ b/l10n/app_ru.arb @@ -0,0 +1,9 @@ +{ + "@@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..a715b51 --- /dev/null +++ b/lib/components/extensions/context_x.dart @@ -0,0 +1,6 @@ +import 'package:flutter/widgets.dart'; +import 'package:pmu_new/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..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..32d0457 --- /dev/null +++ b/lib/components/utils/debounce.dart @@ -0,0 +1,20 @@ +import 'dart:async'; +import 'dart:ui'; + +class Debounce { + factory Debounce() => _instance; + + Debounce._(); + + static final Debounce _instance = Debounce._(); + + static Timer? _timer; + + static void run( + VoidCallback action, { + Duration delay = const Duration(milliseconds: 700), + }) { + _timer?.cancel(); + _timer = Timer(delay, action); + } +} diff --git a/lib/data/dtos/PlaceDTO.dart b/lib/data/dtos/PlaceDTO.dart index 35b9f8c..bc07748 100644 --- a/lib/data/dtos/PlaceDTO.dart +++ b/lib/data/dtos/PlaceDTO.dart @@ -19,7 +19,7 @@ class PlaceDataDto { final String? description; final String? imageData; - const PlaceDataDto({this.id, this.name, this.yearBuilt, this.description, this.imageData}); + const PlaceDataDto({this.id, this.name, this.yearBuilt, this.description, this.imageData}); factory PlaceDataDto.fromJson(Map json) => _$PlaceDataDtoFromJson(json); -} \ No newline at end of file +} diff --git a/lib/data/mappers/PlaceDataDtoToModel.dart b/lib/data/mappers/PlaceDataDtoToModel.dart index 8e8da8e..95f2930 100644 --- a/lib/data/mappers/PlaceDataDtoToModel.dart +++ b/lib/data/mappers/PlaceDataDtoToModel.dart @@ -5,15 +5,16 @@ import '../dtos/PlaceDTO.dart'; extension PlaceDataDtoToModel on PlaceDataDto { CardData toDomain() => CardData( - name ?? 'Неизвестное имя', - //imageUrl: attributes?.image, - descriptionText: getText(), - //bytes: (imagedata != null) ? base64.decode(imagedata!) : null, - bytes: imageData?? null, - ); - String getText(){ + name ?? 'Неизвестное имя', + //imageUrl: attributes?.image, + descriptionText: getText(), + //bytes: (imagedata != null) ? base64.decode(imagedata!) : null, + bytes: imageData ?? null, + id: id.toString() + ); + String getText() { final String str1 = description ?? "Описания нет"; final String str2 = yearBuilt?.toString() ?? "Даты построики нет"; - return "$str1\n$str2 год"; + return "$str1\n--------------\n$str2 год"; } -} \ No newline at end of file +} diff --git a/lib/data/repositories/MockRepository.dart b/lib/data/repositories/MockRepository.dart index 0f61e14..355bd19 100644 --- a/lib/data/repositories/MockRepository.dart +++ b/lib/data/repositories/MockRepository.dart @@ -4,33 +4,29 @@ import 'package:pmu_new/data/repositories/api_Interface.dart'; import '../../models/CardData.dart'; -class MockRepository extends ApiInterface{ +class MockRepository extends ApiInterface { @override - Future?> loadData({OnErrorCallback? onError}) async{ + Future?> loadData({OnErrorCallback? onError}) async { final data = [ CardData( 'Фрукты', descriptionText: - 'Какие же они полезные и сладкие!!!! Generate Lorem Ipsum placeholder text for use in your graphic, print and web layouts, and discover plugins for your favorite writing, design and blogging tools', + 'Какие же они полезные и сладкие!!!! Generate Lorem Ipsum placeholder text for use in your graphic, print and web layouts, and discover plugins for your favorite writing, design and blogging tools', imageUrl: - 'https://media.gettyimages.com/id/182810893/photo/fruit-mix.jpg?s=612x612&w=0&k=20&c=v9jopDXbS5LCXY1d8uSwEldLJVVkOpYtYtyHD8azWDU=', + 'https://media.gettyimages.com/id/182810893/photo/fruit-mix.jpg?s=612x612&w=0&k=20&c=v9jopDXbS5LCXY1d8uSwEldLJVVkOpYtYtyHD8azWDU=', ), CardData( 'Киви', descriptionText: - 'сладкий и спелый, можно купить по акции прямо сейчас звоните не пожалеете', - Backgroundcolor: Color.fromARGB(10, 210, 30, 40), - imageUrl: - 'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-09b.jpg', + 'сладкий и спелый, можно купить по акции прямо сейчас звоните не пожалеете', + imageUrl: 'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-09b.jpg', ), CardData( 'Банан', descriptionText: 'Ого, че с ним произошло', - Backgroundcolor: Color.fromARGB(30, 30, 210, 15), - imageUrl: - 'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-01.jpg', + imageUrl: 'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-01.jpg', ), ]; return data; } -} \ No newline at end of file +} diff --git a/lib/data/repositories/PlacesRepository.dart b/lib/data/repositories/PlacesRepository.dart index 8c12e78..47ffbf0 100644 --- a/lib/data/repositories/PlacesRepository.dart +++ b/lib/data/repositories/PlacesRepository.dart @@ -15,24 +15,29 @@ class PlacesRepository extends ApiInterface { requestHeader: true, requestBody: true, )); - static const String _baseUrl = 'https://localhost:5001'; + static const String _baseUrl = 'http://192.168.184.215:5001'; + //static const String _baseUrl = 'https://localhost:5001'; @override - Future?> loadData({int? q, OnErrorCallback? onError}) async { - try { - const String url = '$_baseUrl/api/Main/GetManufactureList'; + Future?> loadData({int? q, OnErrorCallback? onError}) async { + try { + const String url = '$_baseUrl/api/Main/GetManufactureList'; - final Response response = await _dio.get>( - url, - queryParameters: q != null ? {'year': q} : null, - ); + final Response response = await _dio.get>( + url, + queryParameters: q != null ? {'year': q} : null, + ); - final PlacesDto dto = PlacesDto.fromJson(response.data as Map); - final List? data = dto.data?.map((e) => e.toDomain()).toList(); - //debugPrint("length: ${data?.length}"); - return data; - } on DioException catch (e) { - onError?.call(e.response?.statusMessage); - return null; - } + final PlacesDto dto = PlacesDto.fromJson(response.data as Map); + final List? data = dto.data?.map((e) => e.toDomain()).toList(); + //debugPrint("length: ${data?.length}"); + return data; + } on DioException catch (e) { + if (e.response != null) { + print(e.response!.data); + print(e.response!.headers); + print(e.response!.requestOptions);} + onError?.call(e.error?.toString()); + return null; } - } \ No newline at end of file + } +} diff --git a/lib/data/repositories/api_Interface.dart b/lib/data/repositories/api_Interface.dart index 225c404..bb61a46 100644 --- a/lib/data/repositories/api_Interface.dart +++ b/lib/data/repositories/api_Interface.dart @@ -4,4 +4,4 @@ typedef OnErrorCallback = void Function(String? error); abstract class ApiInterface { Future?> loadData({OnErrorCallback? onError}); -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index 4c26939..2f8ec7f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,14 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:pmu_new/presentation/home_page/bloc/bloc.dart'; +import 'package:pmu_new/presentation/home_page/like_bloc/like_bloc.dart'; +import 'package:pmu_new/presentation/locale_bloc/locale_bloc.dart'; +import 'package:pmu_new/presentation/locale_bloc/locale_state.dart'; +import 'components/locale/l10n/app_locale.dart'; +import 'data/repositories/PlacesRepository.dart'; import 'presentation/home_page/home_page.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; enum FoodType { fruit, vegetable, meat, fish, sweet } @@ -54,16 +63,33 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Старостин Иван Константинович'), - ); + return BlocProvider( + lazy: false, + create: (context) => LocaleBloc(Locale(Platform.localeName)), + child: BlocBuilder(builder: (context, state) { + return MaterialApp( + localizationsDelegates: AppLocale.localizationsDelegates, + locale: state.currentLocale, + supportedLocales: AppLocale.supportedLocales, + title: 'Flutter Demo', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: RepositoryProvider( + lazy: true, + create: (_) => PlacesRepository(), + child: BlocProvider( + lazy: false, + create: (context) => LikeBloc(), + child: BlocProvider( + lazy: false, + create: (context) => HomeBloc(context.read()), + child: const MyHomePage(title: "Старостин Иван Константинович"), + ), + ), + ), + ); + })); } } - - - diff --git a/lib/models/CardData.dart b/lib/models/CardData.dart index f6c809d..3f97768 100644 --- a/lib/models/CardData.dart +++ b/lib/models/CardData.dart @@ -5,15 +5,15 @@ import 'package:flutter/material.dart'; class CardData { final String text; final String descriptionText; - final Color Backgroundcolor; final String? imageUrl; final String? bytes; + final String? id; CardData( - this.text, { - required this.descriptionText, - this.Backgroundcolor = const Color.fromARGB(70, 173, 216, 230), - this.imageUrl = 'https://simbircity.ru/wp-content/uploads/2020/10/1IMG_36222.jpg', - this.bytes = null, - }); -} \ No newline at end of file + this.text, { + required this.descriptionText, + this.imageUrl = 'https://simbircity.ru/wp-content/uploads/2020/10/1IMG_36222.jpg', + this.bytes = null, + this.id + }); +} diff --git a/lib/presentation/details_page/details_page.dart b/lib/presentation/details_page/details_page.dart index 0446efc..7948505 100644 --- a/lib/presentation/details_page/details_page.dart +++ b/lib/presentation/details_page/details_page.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:pmu_new/models/CardData.dart'; @@ -15,9 +17,11 @@ class DetailsPage extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(bottom: 16.0), - child: Image.network( - data.imageUrl ?? '', - ), + child: data.bytes != null + ? Image.memory(base64Decode(data.bytes!)) + : Image.network( + data.imageUrl ?? '', + ), ), Padding( padding: const EdgeInsets.only(bottom: 4.0), @@ -34,4 +38,4 @@ class DetailsPage extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/presentation/home_page/MyCard.dart b/lib/presentation/home_page/MyCard.dart index c577a10..80b47a2 100644 --- a/lib/presentation/home_page/MyCard.dart +++ b/lib/presentation/home_page/MyCard.dart @@ -4,23 +4,21 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:pmu_new/models/CardData.dart'; -class MyCard extends StatefulWidget { +class MyCard extends StatelessWidget { final CardData cardData; final VoidCallback onTap; - final void Function(String title, bool isliked)? onlike; + final Color backgroundColor; + //final String? id; + final bool isliked; + final void Function(String? id, String title, bool isliked)? onlike; - const MyCard({ + MyCard({ required this.cardData, required this.onTap, - this.onlike + this.onlike, + this.backgroundColor = const Color.fromARGB(70, 173, 216, 230), + this.isliked=false, }); - - @override - State createState() => _CardState(); -} - -class _CardState extends State { - bool isliked = false; double turns = 0.0; @override @@ -29,7 +27,7 @@ class _CardState extends State { margin: const EdgeInsets.all(16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: this.widget.cardData.Backgroundcolor, + color: this.backgroundColor, borderRadius: BorderRadius.circular(9), border: Border.all(color: Colors.grey), boxShadow: [ @@ -52,22 +50,21 @@ class _CardState extends State { Column( children: [ GestureDetector( - onTap: widget.onTap, - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: SizedBox( - height: 240, - width: 300, - child: this.widget.cardData.bytes != null? - Image.memory(base64Decode(this.widget.cardData.bytes!)) : - Image.network( - this.widget.cardData.imageUrl ?? '', - fit: BoxFit.cover, - errorBuilder: (_, __, ___) => const Placeholder(), - ), - ), - ) - ) + onTap: onTap, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: SizedBox( + height: 240, + width: 300, + child: this.cardData.bytes != null + ? Image.memory(base64Decode(this.cardData.bytes!)) + : Image.network( + this.cardData.imageUrl ?? '', + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => const Placeholder(), + ), + ), + )) ], ), ]), @@ -75,16 +72,12 @@ class _CardState extends State { children: [ Expanded( child: Padding( - padding: const EdgeInsets.only(left: 16.0, top: 12.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(this.widget.cardData.text, - style: TextStyle( - decoration: TextDecoration.underline, - fontSize: 30)), - ]), - )), + padding: const EdgeInsets.only(left: 16.0, top: 12.0), + child: + Text(this.cardData.text, + style: TextStyle(decoration: TextDecoration.underline, fontSize: 30)), + ), + ), ], ), Row(children: [ @@ -92,7 +85,7 @@ class _CardState extends State { child: Padding( padding: const EdgeInsets.only(left: 16.0), child: Text( - this.widget.cardData.descriptionText, + this.cardData.descriptionText, style: Theme.of(context).textTheme.bodyLarge, )), ), @@ -109,19 +102,24 @@ class _CardState extends State { duration: const Duration(milliseconds: 500), turns: turns, child: GestureDetector( - onTap: () { - setState(() { - isliked = !isliked; + onTap: () { turns += 1.0; - }); - widget.onlike?.call(widget.cardData.text, isliked); + onlike?.call(this.cardData.id, cardData.text, isliked); }, - child: Icon( - isliked ? Icons.favorite : Icons.favorite_border, - color: Colors.red, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isliked + ? const Icon( + Icons.favorite, + color: Colors.redAccent, + key: ValueKey(0), + ) + : const Icon( + Icons.favorite_border, + key: ValueKey(1), )), )) - ]) + )]) ], ), )); diff --git a/lib/presentation/home_page/bloc/bloc.dart b/lib/presentation/home_page/bloc/bloc.dart new file mode 100644 index 0000000..cb2220c --- /dev/null +++ b/lib/presentation/home_page/bloc/bloc.dart @@ -0,0 +1,26 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:pmu_new/presentation/home_page/bloc/state.dart'; +import '../../../data/repositories/PlacesRepository.dart'; +import 'events.dart'; + +class HomeBloc extends Bloc { + final PlacesRepository repo; + String? error; + + HomeBloc(this.repo) : super(const HomeState()) { + on(_onLoadData); + } + + Future _onLoadData(HomeLoadDataEvent event, Emitter emit) async { + emit(state.copyWith(isLoading: true)); + + final data = await repo.loadData(q: event.search, onError: (e) => error = e,); + + + emit(state.copyWith( + isLoading: false, + data: data, + error: error, + )); + } +} diff --git a/lib/presentation/home_page/bloc/events.dart b/lib/presentation/home_page/bloc/events.dart new file mode 100644 index 0000000..b6e5234 --- /dev/null +++ b/lib/presentation/home_page/bloc/events.dart @@ -0,0 +1,9 @@ +abstract class HomeEvent { + const HomeEvent(); +} + +class HomeLoadDataEvent extends HomeEvent { + final int? search; + + const HomeLoadDataEvent({this.search}); +} diff --git a/lib/presentation/home_page/bloc/state.dart b/lib/presentation/home_page/bloc/state.dart new file mode 100644 index 0000000..b641c77 --- /dev/null +++ b/lib/presentation/home_page/bloc/state.dart @@ -0,0 +1,25 @@ +import 'package:equatable/equatable.dart'; +import 'package:copy_with_extension/copy_with_extension.dart'; +import '../../../models/CardData.dart'; +part 'state.g.dart'; + +@CopyWith() +class HomeState extends Equatable { + final List? data; + final bool isLoading; + final String? error; + + const HomeState({ + this.data, + this.isLoading = false, + this.error, + }); + + + @override + List get props => [ + data, + isLoading, + error, + ]; +} diff --git a/lib/presentation/home_page/bloc/state.g.dart b/lib/presentation/home_page/bloc/state.g.dart new file mode 100644 index 0000000..c7f5b24 --- /dev/null +++ b/lib/presentation/home_page/bloc/state.g.dart @@ -0,0 +1,78 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'state.dart'; + +// ************************************************************************** +// CopyWithGenerator +// ************************************************************************** + +abstract class _$HomeStateCWProxy { + HomeState data(List? data); + + HomeState isLoading(bool isLoading); + + 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({ + List? data, + bool? isLoading, + 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(List? data) => this(data: data); + + @override + HomeState isLoading(bool isLoading) => this(isLoading: isLoading); + + @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? error = const $CopyWithPlaceholder(), + }) { + return HomeState( + data: data == const $CopyWithPlaceholder() + ? _value.data + // ignore: cast_nullable_to_non_nullable + : data as List?, + isLoading: isLoading == const $CopyWithPlaceholder() || isLoading == null + ? _value.isLoading + // ignore: cast_nullable_to_non_nullable + : isLoading 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); +} diff --git a/lib/presentation/home_page/home_page.dart b/lib/presentation/home_page/home_page.dart index a1584f4..80cd605 100644 --- a/lib/presentation/home_page/home_page.dart +++ b/lib/presentation/home_page/home_page.dart @@ -1,11 +1,23 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:pmu_new/data/repositories/PlacesRepository.dart'; +import 'package:pmu_new/components/extensions/context_x.dart'; import 'package:pmu_new/presentation/home_page/MyCard.dart'; import 'package:pmu_new/models/CardData.dart'; - -import '../../data/repositories/MockRepository.dart'; +import 'package:pmu_new/presentation/svg_object.dart'; import '../details_page/details_page.dart'; +import '../locale_bloc/locale_bloc.dart'; +import '../locale_bloc/locale_event.dart'; +import '../locale_bloc/locale_state.dart'; +import 'bloc/bloc.dart'; +import 'bloc/events.dart'; +import 'bloc/state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../components/utils/debounce.dart'; +import 'dart:math' as math; + +import 'like_bloc/like_bloc.dart'; +import 'like_bloc/like_event.dart'; +import 'like_bloc/like_state.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); @@ -17,35 +29,49 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - - @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - backgroundColor: Theme - .of(context) - .colorScheme - .inversePrimary, + backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: const CardWidget()); } } - -class CardWidget extends StatelessWidget { +class CardWidget extends StatefulWidget { const CardWidget({super.key}); + @override + State createState() => _CardWidgetState(); +} + +class _CardWidgetState extends State { + final searchController = TextEditingController(); + + @override + void initState() { + SvgObjects.init(); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().add(const HomeLoadDataEvent()); + context.read().add(const LoadLikesEvent()); + }); + super.initState(); + } + + @override + void dispose() { + searchController.dispose(); + super.dispose(); + } + void _showSnackBar(BuildContext context, String title, bool isLiked) { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( - '$title ${isLiked ? 'Понравилось!' : 'Больше не нравится'}', - style: Theme - .of(context) - .textTheme - .bodyLarge, + '$title ${isLiked ? context.locale.liked : context.locale.disliked}', + style: Theme.of(context).textTheme.bodyLarge, ), backgroundColor: Colors.greenAccent, duration: const Duration(seconds: 1), @@ -53,34 +79,97 @@ class CardWidget extends StatelessWidget { }); } - @override - Widget build(BuildContext context) { - final data = PlacesRepository().loadData(q: 1780); - return Center( - child: FutureBuilder?>( - future: data, - builder: (context, snapshot) => - SingleChildScrollView( - child: snapshot.hasData ? - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: snapshot.data?.map((e) { - return MyCard( - cardData: e, - onlike: (title, state) => _showSnackBar(context, title, state), - onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => DetailsPage(e)), - ); - } - ); - }).toList() ?? [], - ) - : const CircularProgressIndicator() - ) - ), + void _navToDetails(BuildContext context, CardData data) { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => DetailsPage(data)), ); } -} \ No newline at end of file + + Future _onRefresh() { + context + .read() + .add(HomeLoadDataEvent(search: int.tryParse(searchController.text))); + return Future.value(null); + } + void _onLike(String? id, String title, bool isLiked) { + if (id != null) { + context.read().add(ChangeLikeEvent(id)); + _showSnackBar(context, title, !isLiked); + } + } + + @override + Widget build(BuildContext context) { + return Center( + child: Padding( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: Column( + children: [ + Row( + children: [ + Expanded( + flex: 4, + child: Padding( + padding: const EdgeInsets.all(12), + child: CupertinoSearchTextField( + controller: searchController, + placeholder: context.locale.search, + onChanged: (search) { + Debounce.run(() => context.read().add( + HomeLoadDataEvent(search: int.tryParse(search)))); + }, + ), + ), + ), + GestureDetector( + onTap: () => + context.read().add(const ChangeLocaleEvent()), + child: SizedBox.square( + dimension: 50, + child: Padding( + padding: const EdgeInsets.only(right: 12), + child: BlocBuilder( + builder: (context, state) { + return state.currentLocale.languageCode == 'ru' + ? const SvgRu() + : const SvgUk(); + }, + ), + ), + ), + ), + ], + ), + BlocBuilder( + builder: (context, state) => state.isLoading + ? const CircularProgressIndicator() + : BlocBuilder( + builder: (context, likeState) { + return Expanded( + child: RefreshIndicator( + onRefresh: _onRefresh, + child: ListView.builder( + padding: EdgeInsets.zero, + itemCount: state.data?.length ?? 0, + itemBuilder: (context, index) { + final data = state.data?[index]; + return data != null + ? MyCard( + cardData: data, + onlike: _onLike, + //id: data.id, + isliked: likeState.likedIds?.contains(data.id) == true, + onTap: () => _navToDetails(context, data), + ) + : const SizedBox.shrink(); + }, + ), + ), + ); + },)) + ], + ), + )); + } +} diff --git a/lib/presentation/home_page/like_bloc/like_bloc.dart b/lib/presentation/home_page/like_bloc/like_bloc.dart new file mode 100644 index 0000000..5ba512c --- /dev/null +++ b/lib/presentation/home_page/like_bloc/like_bloc.dart @@ -0,0 +1,36 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'like_event.dart'; +import 'like_state.dart'; + +const String _likedPrefsKey = 'liked'; + +class LikeBloc extends Bloc { + LikeBloc() : super(const LikeState(likedIds: [])) { + on(_onChangeLike); + on(_onLoadLikes); + } + + Future _onLoadLikes(LoadLikesEvent event, Emitter emit) async { + final prefs = await SharedPreferences.getInstance(); + final data = prefs.getStringList(_likedPrefsKey); + + emit(state.copyWith(likedIds: data)); + } + + Future _onChangeLike(ChangeLikeEvent event, Emitter emit) async { + final updatedList = List.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)); + } +} \ No newline at end of file diff --git a/lib/presentation/home_page/like_bloc/like_event.dart b/lib/presentation/home_page/like_bloc/like_event.dart new file mode 100644 index 0000000..43032db --- /dev/null +++ b/lib/presentation/home_page/like_bloc/like_event.dart @@ -0,0 +1,13 @@ +abstract class LikeEvent { + const LikeEvent(); +} + +class LoadLikesEvent extends LikeEvent { + const LoadLikesEvent(); +} + +class ChangeLikeEvent extends LikeEvent { + final String id; + + const ChangeLikeEvent(this.id); +} \ No newline at end of file diff --git a/lib/presentation/home_page/like_bloc/like_state.dart b/lib/presentation/home_page/like_bloc/like_state.dart new file mode 100644 index 0000000..0099c7c --- /dev/null +++ b/lib/presentation/home_page/like_bloc/like_state.dart @@ -0,0 +1,14 @@ +import 'package:copy_with_extension/copy_with_extension.dart'; +import 'package:equatable/equatable.dart'; + +part 'like_state.g.dart'; + +@CopyWith() +class LikeState extends Equatable { + final List? likedIds; + + const LikeState({required this.likedIds}); + + @override + List get props => [likedIds]; +} \ No newline at end of file diff --git a/lib/presentation/home_page/like_bloc/like_state.g.dart b/lib/presentation/home_page/like_bloc/like_state.g.dart new file mode 100644 index 0000000..0888cf2 --- /dev/null +++ b/lib/presentation/home_page/like_bloc/like_state.g.dart @@ -0,0 +1,56 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'like_state.dart'; + +// ************************************************************************** +// CopyWithGenerator +// ************************************************************************** + +abstract class _$LikeStateCWProxy { + LikeState likedIds(List? 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? 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? 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?, + ); + } +} + +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); +} diff --git a/lib/presentation/locale_bloc/locale_bloc.dart b/lib/presentation/locale_bloc/locale_bloc.dart new file mode 100644 index 0000000..05ea5ba --- /dev/null +++ b/lib/presentation/locale_bloc/locale_bloc.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:pmu_new/components/locale/l10n/app_locale.dart'; +import 'locale_event.dart'; +import 'locale_state.dart'; + + +class LocaleBloc extends Bloc { + LocaleBloc(Locale defaultLocale) : super(LocaleState(currentLocale: defaultLocale)) { + on(_onChangeLocale); + } + + Future _onChangeLocale(ChangeLocaleEvent event, Emitter emit) async { + final toChange = AppLocale.supportedLocales + .firstWhere((e) => e.languageCode != state.currentLocale.languageCode); + emit(state.copyWith(currentLocale: toChange)); + } +} \ No newline at end of file diff --git a/lib/presentation/locale_bloc/locale_event.dart b/lib/presentation/locale_bloc/locale_event.dart new file mode 100644 index 0000000..f345ef9 --- /dev/null +++ b/lib/presentation/locale_bloc/locale_event.dart @@ -0,0 +1,7 @@ +abstract class LocaleEvent { + const LocaleEvent(); +} + +class ChangeLocaleEvent extends LocaleEvent { + const ChangeLocaleEvent(); +} \ No newline at end of file diff --git a/lib/presentation/locale_bloc/locale_state.dart b/lib/presentation/locale_bloc/locale_state.dart new file mode 100644 index 0000000..8b7a64d --- /dev/null +++ b/lib/presentation/locale_bloc/locale_state.dart @@ -0,0 +1,14 @@ +import 'package:copy_with_extension/copy_with_extension.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +part 'locale_state.g.dart'; + +@CopyWith() +class LocaleState extends Equatable { + final Locale currentLocale; + + const LocaleState({required this.currentLocale}); + + @override + List get props => [currentLocale]; +} \ No newline at end of file diff --git a/lib/presentation/locale_bloc/locale_state.g.dart b/lib/presentation/locale_bloc/locale_state.g.dart new file mode 100644 index 0000000..e374db6 --- /dev/null +++ b/lib/presentation/locale_bloc/locale_state.g.dart @@ -0,0 +1,58 @@ +// 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); +} diff --git a/lib/presentation/svg_object.dart b/lib/presentation/svg_object.dart new file mode 100644 index 0000000..4e6ad97 --- /dev/null +++ b/lib/presentation/svg_object.dart @@ -0,0 +1,34 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:pmu_new/components/resources.g.dart'; + +abstract class SvgObjects { + static void init() { + final pics = [ + R.ASSETS_SVG_RU_SVG, + R.ASSETS_SVG_US_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_US_SVG); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 06ef412..2226eff 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,6 +22,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.0" + archive: + dependency: transitive + description: + name: archive + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + url: "https://pub.dev" + source: hosted + version: "3.6.1" args: dependency: transitive description: @@ -38,6 +46,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" + url: "https://pub.dev" + source: hosted + version: "8.1.4" boolean_selector: dependency: transitive description: @@ -126,6 +142,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" clock: dependency: transitive description: @@ -158,6 +182,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + copy_with_extension: + dependency: transitive + description: + name: copy_with_extension + sha256: fbcf890b0c34aedf0894f91a11a579994b61b4e04080204656b582708b5b1125 + url: "https://pub.dev" + source: hosted + version: "5.0.4" + copy_with_extension_gen: + dependency: "direct dev" + description: + name: copy_with_extension_gen + sha256: "51cd11094096d40824c8da629ca7f16f3b7cea5fc44132b679617483d43346b0" + url: "https://pub.dev" + source: hosted + version: "5.0.4" crypto: dependency: transitive description: @@ -198,6 +238,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + equatable: + dependency: "direct dev" + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" fake_async: dependency: transitive description: @@ -206,6 +254,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" file: dependency: transitive description: @@ -227,6 +283,22 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct dev" + description: + name: flutter_bloc + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a + url: "https://pub.dev" + source: hosted + version: "8.1.6" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" + source: hosted + version: "0.13.1" flutter_lints: dependency: "direct dev" description: @@ -235,11 +307,29 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + flutter_localizations: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_svg: + dependency: "direct dev" + description: + name: flutter_svg + sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123" + url: "https://pub.dev" + source: hosted + version: "2.0.16" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" frontend_server_client: dependency: transitive description: @@ -264,6 +354,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + http: + dependency: transitive + description: + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" http_multi_server: dependency: transitive description: @@ -280,6 +378,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image: + dependency: transitive + description: + name: image + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d + url: "https://pub.dev" + source: hosted + version: "4.3.0" + intl: + dependency: "direct dev" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" io: dependency: transitive description: @@ -392,6 +506,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_config: dependency: transitive description: @@ -408,6 +530,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" pool: dependency: transitive description: @@ -424,6 +602,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + provider: + dependency: transitive + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" pub_semver: dependency: transitive description: @@ -440,6 +626,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + shared_preferences: + dependency: "direct dev" + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" shelf: dependency: transitive description: @@ -549,6 +791,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7" + url: "https://pub.dev" + source: hosted + version: "1.1.15" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb" + url: "https://pub.dev" + source: hosted + version: "1.1.12" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" + url: "https://pub.dev" + source: hosted + version: "1.1.16" vector_math: dependency: transitive description: @@ -597,6 +863,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" yaml: dependency: transitive description: @@ -607,4 +889,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.5.4 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8c87614..37c81b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,33 +40,46 @@ dependencies: json_annotation: ^4.8.1 dio: ^5.4.2+1 pretty_dio_logger: ^1.3.1 + dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.4.9 json_serializable: ^6.7.1 - + equatable: ^2.0.5 + flutter_bloc: ^8.1.5 + flutter_launcher_icons: 0.13.1 + flutter_svg: ^2.0.7 + copy_with_extension_gen: ^5.0.4 + shared_preferences: 2.2.3 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^4.0.0 + flutter_localizations: + sdk: flutter + intl: ^0.19.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec - +flutter_icons: + android: "ic_launcher" + ios: false + image_path: "assets/icon.png" + min_sdk_android: 17 # The following section is specific to Flutter packages. flutter: - + generate: true # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg + assets: + - assets/svg/ # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see