Compare commits

..

16 Commits
master ... lab7

Author SHA1 Message Date
7be5b8a4c5 иконка 2024-11-14 16:52:16 +03:00
f5ef0d96fd lab7 2024-11-14 16:45:44 +03:00
5b36c7e279 lab7 2024-11-14 16:44:54 +03:00
f420330f68 lab7 2024-11-14 16:44:44 +03:00
5db40e6364 lab7, но пагинация слегла 2024-11-13 21:31:22 +03:00
856f35c159 lab7, но пагинация слегла 2024-11-13 21:28:56 +03:00
afee5d8948 lab7, но пагинация слегла 2024-11-13 21:23:59 +03:00
a247669194 lab7, но пагинация слегла 2024-11-13 21:23:25 +03:00
0d2a639c9d lab_5 2024-10-28 16:08:51 +03:00
e72bed02f8 lab_5 2024-10-28 16:07:37 +03:00
fad9c2a19f lab_5 2024-10-28 16:07:23 +03:00
c2328a5ada теперь в lab4 анекдоты и мопсы 2024-10-14 16:24:22 +03:00
c71fd1ddf2 lab4 2024-10-14 12:01:33 +03:00
fc033c77e9 добавление изображения в lab3 2024-10-14 11:45:20 +03:00
5cfe4a6ad9 lab2 2024-09-19 15:13:14 +03:00
84e1f6325f lab2 2024-09-19 15:11:28 +03:00
60 changed files with 2082 additions and 132 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 36 KiB

BIN
assets/icon1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

19
assets/svg/ru.svg Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -4 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_503_2726)">
<rect x="0.25" y="0.25" width="27.5" height="19.5" rx="1.75" fill="white" stroke="#F5F5F5" stroke-width="0.5"/>
<mask id="mask0_503_2726" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="28" height="20">
<rect x="0.25" y="0.25" width="27.5" height="19.5" rx="1.75" fill="white" stroke="white" stroke-width="0.5"/>
</mask>
<g mask="url(#mask0_503_2726)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 13.3333H28V6.66667H0V13.3333Z" fill="#0C47B7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 20H28V13.3333H0V20Z" fill="#E53B35"/>
</g>
</g>
<defs>
<clipPath id="clip0_503_2726">
<rect width="28" height="20" rx="2" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 968 B

23
assets/svg/uk.svg Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<path style="fill:#41479B;" d="M473.655,88.276H38.345C17.167,88.276,0,105.443,0,126.621V385.38
c0,21.177,17.167,38.345,38.345,38.345h435.31c21.177,0,38.345-17.167,38.345-38.345V126.621
C512,105.443,494.833,88.276,473.655,88.276z"/>
<path style="fill:#F5F5F5;" d="M511.469,120.282c-3.022-18.159-18.797-32.007-37.814-32.007h-9.977l-163.54,107.147V88.276h-88.276
v107.147L48.322,88.276h-9.977c-19.017,0-34.792,13.847-37.814,32.007l139.778,91.58H0v88.276h140.309L0.531,391.717
c3.022,18.159,18.797,32.007,37.814,32.007h9.977l163.54-107.147v107.147h88.276V316.577l163.54,107.147h9.977
c19.017,0,34.792-13.847,37.814-32.007l-139.778-91.58H512v-88.276H371.691L511.469,120.282z"/>
<g>
<polygon style="fill:#FF4B55;" points="282.483,88.276 229.517,88.276 229.517,229.517 0,229.517 0,282.483 229.517,282.483
229.517,423.724 282.483,423.724 282.483,282.483 512,282.483 512,229.517 282.483,229.517 "/>
<path style="fill:#FF4B55;" d="M24.793,421.252l186.583-121.114h-32.428L9.224,410.31
C13.377,415.157,18.714,418.955,24.793,421.252z"/>
<path style="fill:#FF4B55;" d="M346.388,300.138H313.96l180.716,117.305c5.057-3.321,9.277-7.807,12.287-13.075L346.388,300.138z"
/>
<path style="fill:#FF4B55;" d="M4.049,109.475l157.73,102.387h32.428L15.475,95.842C10.676,99.414,6.749,104.084,4.049,109.475z"/>
<path style="fill:#FF4B55;" d="M332.566,211.862l170.035-110.375c-4.199-4.831-9.578-8.607-15.699-10.86L300.138,211.862H332.566z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

34
assets/svg/us.svg Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -4 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_503_3486)">
<rect width="28" height="20" rx="2" fill="white"/>
<mask id="mask0_503_3486" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="28" height="20">
<rect width="28" height="20" rx="2" fill="white"/>
</mask>
<g mask="url(#mask0_503_3486)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M28 0H0V1.33333H28V0ZM28 2.66667H0V4H28V2.66667ZM0 5.33333H28V6.66667H0V5.33333ZM28 8H0V9.33333H28V8ZM0 10.6667H28V12H0V10.6667ZM28 13.3333H0V14.6667H28V13.3333ZM0 16H28V17.3333H0V16ZM28 18.6667H0V20H28V18.6667Z" fill="#D02F44"/>
<rect width="12" height="9.33333" fill="#46467F"/>
<g filter="url(#filter0_d_503_3486)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.66665 1.99999C2.66665 2.36818 2.36817 2.66666 1.99998 2.66666C1.63179 2.66666 1.33331 2.36818 1.33331 1.99999C1.33331 1.63181 1.63179 1.33333 1.99998 1.33333C2.36817 1.33333 2.66665 1.63181 2.66665 1.99999ZM5.33331 1.99999C5.33331 2.36818 5.03484 2.66666 4.66665 2.66666C4.29846 2.66666 3.99998 2.36818 3.99998 1.99999C3.99998 1.63181 4.29846 1.33333 4.66665 1.33333C5.03484 1.33333 5.33331 1.63181 5.33331 1.99999ZM7.33331 2.66666C7.7015 2.66666 7.99998 2.36818 7.99998 1.99999C7.99998 1.63181 7.7015 1.33333 7.33331 1.33333C6.96512 1.33333 6.66665 1.63181 6.66665 1.99999C6.66665 2.36818 6.96512 2.66666 7.33331 2.66666ZM10.6666 1.99999C10.6666 2.36818 10.3682 2.66666 9.99998 2.66666C9.63179 2.66666 9.33331 2.36818 9.33331 1.99999C9.33331 1.63181 9.63179 1.33333 9.99998 1.33333C10.3682 1.33333 10.6666 1.63181 10.6666 1.99999ZM3.33331 3.99999C3.7015 3.99999 3.99998 3.70152 3.99998 3.33333C3.99998 2.96514 3.7015 2.66666 3.33331 2.66666C2.96512 2.66666 2.66665 2.96514 2.66665 3.33333C2.66665 3.70152 2.96512 3.99999 3.33331 3.99999ZM6.66665 3.33333C6.66665 3.70152 6.36817 3.99999 5.99998 3.99999C5.63179 3.99999 5.33331 3.70152 5.33331 3.33333C5.33331 2.96514 5.63179 2.66666 5.99998 2.66666C6.36817 2.66666 6.66665 2.96514 6.66665 3.33333ZM8.66665 3.99999C9.03484 3.99999 9.33331 3.70152 9.33331 3.33333C9.33331 2.96514 9.03484 2.66666 8.66665 2.66666C8.29846 2.66666 7.99998 2.96514 7.99998 3.33333C7.99998 3.70152 8.29846 3.99999 8.66665 3.99999ZM10.6666 4.66666C10.6666 5.03485 10.3682 5.33333 9.99998 5.33333C9.63179 5.33333 9.33331 5.03485 9.33331 4.66666C9.33331 4.29847 9.63179 3.99999 9.99998 3.99999C10.3682 3.99999 10.6666 4.29847 10.6666 4.66666ZM7.33331 5.33333C7.7015 5.33333 7.99998 5.03485 7.99998 4.66666C7.99998 4.29847 7.7015 3.99999 7.33331 3.99999C6.96512 3.99999 6.66665 4.29847 6.66665 4.66666C6.66665 5.03485 6.96512 5.33333 7.33331 5.33333ZM5.33331 4.66666C5.33331 5.03485 5.03484 5.33333 4.66665 5.33333C4.29846 5.33333 3.99998 5.03485 3.99998 4.66666C3.99998 4.29847 4.29846 3.99999 4.66665 3.99999C5.03484 3.99999 5.33331 4.29847 5.33331 4.66666ZM1.99998 5.33333C2.36817 5.33333 2.66665 5.03485 2.66665 4.66666C2.66665 4.29847 2.36817 3.99999 1.99998 3.99999C1.63179 3.99999 1.33331 4.29847 1.33331 4.66666C1.33331 5.03485 1.63179 5.33333 1.99998 5.33333ZM3.99998 5.99999C3.99998 6.36819 3.7015 6.66666 3.33331 6.66666C2.96512 6.66666 2.66665 6.36819 2.66665 5.99999C2.66665 5.6318 2.96512 5.33333 3.33331 5.33333C3.7015 5.33333 3.99998 5.6318 3.99998 5.99999ZM5.99998 6.66666C6.36817 6.66666 6.66665 6.36819 6.66665 5.99999C6.66665 5.6318 6.36817 5.33333 5.99998 5.33333C5.63179 5.33333 5.33331 5.6318 5.33331 5.99999C5.33331 6.36819 5.63179 6.66666 5.99998 6.66666ZM9.33331 5.99999C9.33331 6.36819 9.03484 6.66666 8.66665 6.66666C8.29846 6.66666 7.99998 6.36819 7.99998 5.99999C7.99998 5.6318 8.29846 5.33333 8.66665 5.33333C9.03484 5.33333 9.33331 5.6318 9.33331 5.99999ZM9.99998 8C10.3682 8 10.6666 7.70152 10.6666 7.33333C10.6666 6.96514 10.3682 6.66666 9.99998 6.66666C9.63179 6.66666 9.33331 6.96514 9.33331 7.33333C9.33331 7.70152 9.63179 8 9.99998 8ZM7.99998 7.33333C7.99998 7.70152 7.7015 8 7.33331 8C6.96512 8 6.66665 7.70152 6.66665 7.33333C6.66665 6.96514 6.96512 6.66666 7.33331 6.66666C7.7015 6.66666 7.99998 6.96514 7.99998 7.33333ZM4.66665 8C5.03484 8 5.33331 7.70152 5.33331 7.33333C5.33331 6.96514 5.03484 6.66666 4.66665 6.66666C4.29846 6.66666 3.99998 6.96514 3.99998 7.33333C3.99998 7.70152 4.29846 8 4.66665 8ZM2.66665 7.33333C2.66665 7.70152 2.36817 8 1.99998 8C1.63179 8 1.33331 7.70152 1.33331 7.33333C1.33331 6.96514 1.63179 6.66666 1.99998 6.66666C2.36817 6.66666 2.66665 6.96514 2.66665 7.33333Z" fill="url(#paint0_linear_503_3486)"/>
</g>
</g>
</g>
<defs>
<filter id="filter0_d_503_3486" x="1.33331" y="1.33333" width="9.33331" height="7.66667" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.06 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_503_3486"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_503_3486" result="shape"/>
</filter>
<linearGradient id="paint0_linear_503_3486" x1="1.33331" y1="1.33333" x2="1.33331" y2="7.99999" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#F0F0F0"/>
</linearGradient>
<clipPath id="clip0_503_3486">
<rect width="28" height="20" rx="2" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -427,7 +427,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@ -484,7 +484,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 28 KiB

6
l10n.yaml Normal file
View File

@ -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

8
l10n/app_en.arb Normal file
View File

@ -0,0 +1,8 @@
{
"@@locale": "en",
"search": "Search",
"liked": "liked!",
"disliked": "unliked"
}

7
l10n/app_ru.arb Normal file
View File

@ -0,0 +1,7 @@
{
"@@locale": "ru",
"search": "Поиск",
"liked": "понравился!",
"disliked": "разонравился"
}

View File

@ -0,0 +1,6 @@
import 'package:flutter/widgets.dart';
import 'package:pmd_labs/components/locale/l10n/app_locale.dart';
extension LocalContextX on BuildContext {
AppLocale get locale => AppLocale.of(this)!;
}

View File

@ -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, youll need to edit this
/// file.
///
/// First, open your projects ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// projects 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<AppLocale>(context, AppLocale);
}
static const LocalizationsDelegate<AppLocale> 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<LocalizationsDelegate<dynamic>> localizationsDelegates = <LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[
Locale('en'),
Locale('ru')
];
/// No description provided for @search.
///
/// In ru, this message translates to:
/// **'Поиск'**
String get search;
/// No description provided for @liked.
///
/// In ru, this message translates to:
/// **'понравился!'**
String get liked;
/// No description provided for @disliked.
///
/// In ru, this message translates to:
/// **'разонравился'**
String get disliked;
}
class _AppLocaleDelegate extends LocalizationsDelegate<AppLocale> {
const _AppLocaleDelegate();
@override
Future<AppLocale> load(Locale locale) {
return SynchronousFuture<AppLocale>(lookupAppLocale(locale));
}
@override
bool isSupported(Locale locale) => <String>['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.'
);
}

View File

@ -0,0 +1,17 @@
import 'app_locale.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocaleEn extends AppLocale {
AppLocaleEn([String locale = 'en']) : super(locale);
@override
String get search => 'Search';
@override
String get liked => 'liked!';
@override
String get disliked => 'unliked';
}

View File

@ -0,0 +1,17 @@
import 'app_locale.dart';
// ignore_for_file: type=lint
/// The translations for Russian (`ru`).
class AppLocaleRu extends AppLocale {
AppLocaleRu([String locale = 'ru']) : super(locale);
@override
String get search => 'Поиск';
@override
String get liked => 'понравился!';
@override
String get disliked => 'разонравился';
}

View File

@ -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';
}

View File

@ -0,0 +1,22 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
class Debounce {
factory Debounce() => _instance;
Debounce._();
static final Debounce _instance = Debounce._();
static Timer? _timer;
static void run(
VoidCallback action, {
Duration delay = const Duration(milliseconds: 500),
}) {
_timer?.cancel();
_timer = Timer(delay, action);
}
}

View File

@ -0,0 +1 @@
typedef OnErrorCallback = void Function(String? error)?;

View File

@ -0,0 +1,86 @@
import 'package:json_annotation/json_annotation.dart';
part 'movies_dto.g.dart';
@JsonSerializable(createToJson: false)
class MoviesDto {
final List<MovieDataDto>? docs;
final MoviePaginationDto? pagination;
const MoviesDto({this.docs, this.pagination});
factory MoviesDto.fromJson(Map<String, dynamic> json) =>
_$MoviesDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
class MoviePaginationDto {
@JsonKey(name: 'current_page')
final int? currentPage;
@JsonKey(name: 'has_next_page')
final bool? hasNextPage;
@JsonKey(name: 'last_visible_page')
final int? lastVisiblePage;
const MoviePaginationDto({this.currentPage, this.hasNextPage, this.lastVisiblePage});
factory MoviePaginationDto.fromJson(Map<String, dynamic> json) =>
_$MoviePaginationDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
class MovieDataDto {
@JsonKey(name: 'id')
final int? id;
final String? name;
final String? description;
final PosterDto? poster;
@JsonKey(name: 'year', defaultValue: 0)
final int year;
final List<GenreDto>? genres;
final List<CountryDto>? countries;
const MovieDataDto(
this.name,
this.description,
this.poster, {
this.id,
this.year = 0,
this.genres,
this.countries,
});
factory MovieDataDto.fromJson(Map<String, dynamic> json) =>
_$MovieDataDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
class GenreDto {
final String? name;
const GenreDto({this.name});
factory GenreDto.fromJson(Map<String, dynamic> json) =>
_$GenreDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
class CountryDto {
final String? name;
const CountryDto({this.name});
factory CountryDto.fromJson(Map<String, dynamic> json) =>
_$CountryDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
class PosterDto {
final String? url;
final String? previewUrl;
const PosterDto({this.url, this.previewUrl});
factory PosterDto.fromJson(Map<String, dynamic> json) =>
_$PosterDtoFromJson(json);
}

View File

@ -0,0 +1,53 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'movies_dto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
MoviesDto _$MoviesDtoFromJson(Map<String, dynamic> json) => MoviesDto(
docs: (json['docs'] as List<dynamic>?)
?.map((e) => MovieDataDto.fromJson(e as Map<String, dynamic>))
.toList(),
pagination: json['pagination'] == null
? null
: MoviePaginationDto.fromJson(
json['pagination'] as Map<String, dynamic>),
);
MoviePaginationDto _$MoviePaginationDtoFromJson(Map<String, dynamic> json) =>
MoviePaginationDto(
currentPage: (json['current_page'] as num?)?.toInt(),
hasNextPage: json['has_next_page'] as bool?,
lastVisiblePage: (json['last_visible_page'] as num?)?.toInt(),
);
MovieDataDto _$MovieDataDtoFromJson(Map<String, dynamic> json) => MovieDataDto(
json['name'] as String?,
json['description'] as String?,
json['poster'] == null
? null
: PosterDto.fromJson(json['poster'] as Map<String, dynamic>),
id: (json['id'] as num?)?.toInt(),
year: (json['year'] as num?)?.toInt() ?? 0,
genres: (json['genres'] as List<dynamic>?)
?.map((e) => GenreDto.fromJson(e as Map<String, dynamic>))
.toList(),
countries: (json['countries'] as List<dynamic>?)
?.map((e) => CountryDto.fromJson(e as Map<String, dynamic>))
.toList(),
);
GenreDto _$GenreDtoFromJson(Map<String, dynamic> json) => GenreDto(
name: json['name'] as String?,
);
CountryDto _$CountryDtoFromJson(Map<String, dynamic> json) => CountryDto(
name: json['name'] as String?,
);
PosterDto _$PosterDtoFromJson(Map<String, dynamic> json) => PosterDto(
url: json['url'] as String?,
previewUrl: json['previewUrl'] as String?,
);

View File

@ -0,0 +1,27 @@
import 'package:pmd_labs/data/dtos/movies_dto.dart';
import 'package:pmd_labs/domain/models/carddata.dart';
import 'package:pmd_labs/presentation/home_page/home_page.dart';
const _imagePlaceholder =
'https://upload.wikimedia.org/wikipedia/en/archive/b/b1/20210811082420%21Portrait_placeholder.png';
extension MovieDataDtoMapper on MovieDataDto {
CardData toDomain() => CardData(
name ?? 'UNKNOWN',
imageUrl: poster?.url ?? _imagePlaceholder,
id: id?.toString() ?? '0',
descriptionText: description ?? 'Нет описания',
year: year,
genres: genres?.map((genre) => genre.name ?? 'UNKNOWN').toList() ?? [],
countries: countries?.map((country) => country.name ?? 'UNKNOWN').toList() ?? [],
);
}
extension MoviesDtoToModel on MoviesDto {
HomeData toDomain() => HomeData(
data: docs?.map((e) => e.toDomain()).toList(),
nextPage: (pagination?.hasNextPage ?? false)
? ((pagination?.currentPage ?? 0) + 1)
: null
);
}

View File

@ -0,0 +1,6 @@
import 'package:pmd_labs/components/utils/error_callback.dart';
import 'package:pmd_labs/presentation/home_page/home_page.dart';
abstract class ApiInterface {
Future<HomeData?> loadData({OnErrorCallback? onError});
}

View File

@ -0,0 +1,53 @@
import 'package:pmd_labs/components/utils/error_callback.dart';
import 'package:pmd_labs/data/dtos/movies_dto.dart';
import 'package:pmd_labs/data/mappers/movies_mapper.dart';
import 'package:pmd_labs/data/repositories/api_interface.dart';
import 'package:dio/dio.dart';
import 'package:pmd_labs/presentation/home_page/home_page.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class MovieRepository extends ApiInterface {
static final Dio _dio = Dio()
..interceptors.add(PrettyDioLogger(
requestHeader: true,
requestBody: true,
));
static const String _baseUrl = 'https://api.kinopoisk.dev';
static const String _apiKey = 'HQTFY5N-8D34FT0-HXQQQ1S-KPREHDX';
@override
Future<HomeData?> loadData({
OnErrorCallback? onError,
String? q,
int page = 1,
int pageSize = 10,
}) async {
try {
const String url = '$_baseUrl/v1.4/movie/search';
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
url,
queryParameters: {
'page': page,
'pageSize': pageSize,
'query': q,
},
options: Options(
headers: {
'X-API-KEY': _apiKey,
},
),
);
// Теперь используйте `FilmsDto.fromJson` с response.data
final MoviesDto dto = MoviesDto.fromJson(
response.data as Map<String, dynamic>);
final HomeData data = dto.toDomain();
return data;
} on DioException catch (e) {
onError?.call(e.error?.toString());
return null;
}
}
}

View File

@ -0,0 +1,17 @@
class CardData {
final String text;
final String descriptionText;
final String? imageUrl;
final String? id;
final int? year;
final List<String>? genres;
final List<String>? countries;
CardData(this.text, {
required this.descriptionText,
this.imageUrl,
this.id,
this.year,
this.genres,
this.countries,
});
}

View File

@ -1,4 +1,15 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:pmd_labs/data/repositories/movie_repository.dart';
import 'package:pmd_labs/presentation/home_page/bloc/bloc.dart';
import 'package:pmd_labs/presentation/home_page/home_page.dart';
import 'package:pmd_labs/presentation/like_bloc/like_bloc.dart';
import 'package:pmd_labs/presentation/locale_bloc/locale_bloc.dart';
import 'package:pmd_labs/presentation/locale_bloc/locale_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'components/locale/l10n/app_locale.dart';
void main() {
runApp(const MyApp());
@ -6,66 +17,40 @@ void main() {
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return BlocProvider<LocaleBloc>( //
lazy: false,
create: (context) => LocaleBloc(Locale(Platform.localeName)),
child: BlocBuilder<LocaleBloc, LocaleState>( //
builder: (context, state) {
return MaterialApp(
title: 'Flutter Demo',
locale: state.currentLocale, // передаем текущую локаль
localizationsDelegates: AppLocale.localizationsDelegates, // делегат (подключение локали)
supportedLocales: AppLocale.supportedLocales, // список доступных локалей (подключение локали)
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
colorScheme:
ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Жирнова Алена Евгениевна'),
);
home: RepositoryProvider<MovieRepository>(
lazy: true,
create: (_) => MovieRepository(),
child: BlocProvider<LikeBloc>( // добавили BlocProvider
lazy: false,
create: (context) => LikeBloc(),
child: BlocProvider<HomeBloc>(
lazy: false,
create: (context) =>
HomeBloc(context.read<MovieRepository>()),
child: const HomePage(),
),
)));
},
));
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:pmd_labs/components/resources.g.dart';
abstract class SvgObjects {
static void init() {
final pics = <String>[
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);
}
}

View File

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:pmd_labs/domain/models/carddata.dart';
class DetailsPage extends StatelessWidget {
final CardData data;
const DetailsPage(this.data, {super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Детали"),
backgroundColor: Colors.purpleAccent, // Фиолетовый цвет для AppBar
),
body: Container(
color: Colors.purple[100], // Светло-сиреневый фон для всей страницы
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Изображение слева
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(20)),
child: Image.network(
data.imageUrl ?? '',
height: 600, // Задайте фиксированную высоту для изображения
width: 600, // Задайте фиксированную ширину для изображения
fit: BoxFit.cover, // Обеспечьте хороший аспект изображения
),
),
SizedBox(width: 16), // Промежуток между изображением и текстом
// Текст справа
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Text(
data.text,
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
color: Colors.purple, // Простой сиреневый цвет текста заголовка
fontWeight: FontWeight.bold, // Жирный шрифт
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Text(
'Год: ${data.year}', // Отображение года
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Colors.purple[700], // Темный сиреневый цвет текста года
fontStyle: FontStyle.italic, // Курсив для выделения года
),
),
),
Text(
data.descriptionText ?? '', // Обработаем случай, если описания нет
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Colors.purple[600], // Темный сиреневый цвет текста описания
),
),
// Отображение жанров
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'Жанры: ${data.genres?.join(', ') ?? 'Нет жанров'}', // Проверка на null
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Colors.purple[600], // Темный сиреневый цвет текста жанров
fontStyle: FontStyle.italic, // Курсив для выделения жанров
),
),
),
// Отображение стран
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'Страны: ${data.countries?.join(', ') ?? 'Нет стран'}', // Проверка на null
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Colors.purple[600], // Темный сиреневый цвет текста стран
fontStyle: FontStyle.italic, // Курсив для выделения стран
),
),
),
],
),
),
],
),
),
),
),
);
}
}

View File

@ -0,0 +1,27 @@
import 'package:pmd_labs/data/repositories/movie_repository.dart';
import 'package:pmd_labs/presentation/home_page/bloc/events.dart';
import 'package:pmd_labs/presentation/home_page/bloc/state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState>{
final MovieRepository repo;
HomeBloc(this.repo) : super (const HomeState()) {
on<HomeLoadDataEvent>(_onLoadData);
}
Future<void> _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
if( event.nextPage == null) {
emit(state.copyWith(isLoading: true));
} else {
emit(state.copyWith(isPaginationLoading: true));
}
final data = await repo.loadData(q: event.search, page: event.nextPage ?? 1);
if (event.nextPage != null) {
data?.data?.insertAll(0, state.data?.data ?? []);
}
emit(state.copyWith(data: data, isLoading: false, isPaginationLoading: false));
}
}

View File

@ -0,0 +1,9 @@
abstract class HomeEvent {
const HomeEvent();
}
class HomeLoadDataEvent extends HomeEvent {
final String? search;
final int? nextPage;
const HomeLoadDataEvent({this.search, this.nextPage});
}

View File

@ -0,0 +1,15 @@
import 'package:equatable/equatable.dart';
import 'package:pmd_labs/presentation/home_page/home_page.dart';
class HomeState extends Equatable {
final HomeData? data;
final bool isLoading;
final bool isPaginationLoading;
const HomeState({this.data, this.isLoading = false, this.isPaginationLoading = false});
HomeState copyWith({HomeData? data, bool? isLoading, bool? isPaginationLoading}) => HomeState(
data: data ?? this.data, isLoading: isLoading ?? this.isLoading, isPaginationLoading: isPaginationLoading ?? this.isPaginationLoading);
@override
List<Object?> get props => [data, isLoading, isPaginationLoading];
}

View File

@ -0,0 +1,143 @@
part of 'home_page.dart';
typedef OnLikeCallback = void Function(String? id, String title, bool isLiked)?;
class _Card extends StatelessWidget { // состояние карточки регулируется извне; виджеты с точкой убираем
final String text;
final String descriptionText;
final String? imageUrl;
final OnLikeCallback onLike;
final VoidCallback? onTap;
final String? id;
final bool isLiked;
const _Card(this.text, {
required this.descriptionText,
this.imageUrl,
this.onLike,
this.onTap,
this.id,
this.isLiked = false,
});
factory _Card.fromData(CardData data, {
OnLikeCallback onLike,
VoidCallback? onTap,
bool isLiked = false,
}) =>
_Card(
data.text,
descriptionText: data.descriptionText,
imageUrl: data.imageUrl,
onLike: onLike,
onTap: onTap,
isLiked: isLiked,
id: data.id,
);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
margin: const EdgeInsets.all(16),
constraints: const BoxConstraints(minHeight: 130),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.purpleAccent, width: 2),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 4,
offset: const Offset(0, 5),
blurRadius: 8,
),
],
),
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(20),
topLeft: Radius.circular(20),
),
child: SizedBox(
height: double.infinity,
width: 120,
child: Image.network(
imageUrl ?? '',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const Placeholder(),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Text(
text,
style: Theme
.of(context)
.textTheme
.headlineSmall
?.apply(color: Colors.purple),
textAlign: TextAlign.center,
),
),
SizedBox(height: 4),
],
),
),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(
left: 8, right: 16, bottom: 16),
child: GestureDetector(
onTap: () => onLike?.call(id, text, isLiked),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: isLiked ? Colors.transparent : Colors.purple,
width: 2,
),
borderRadius: BorderRadius.circular(20),
),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.all(8),
child: isLiked
? const Icon(
Icons.favorite,
color: Colors.purple,
key: ValueKey<int>(0),
)
: const Icon(
Icons.favorite_border,
color: Colors.purple,
key: ValueKey<int>(1),
),
),
),
),
),
),
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,198 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:pmd_labs/components/extension/context_x.dart';
import 'package:pmd_labs/components/utils/debounce.dart';
import 'package:pmd_labs/data/repositories/movie_repository.dart';
import 'package:pmd_labs/presentation/details_page/details_page.dart';
import 'package:pmd_labs/presentation/home_page/bloc/bloc.dart';
import 'package:pmd_labs/presentation/home_page/bloc/events.dart';
import 'package:pmd_labs/presentation/home_page/bloc/state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../domain/models/carddata.dart';
import '../common/svg_objects.dart';
import '../like_bloc/like_bloc.dart';
import '../like_bloc/like_event.dart';
import '../like_bloc/like_state.dart';
import '../locale_bloc/locale_bloc.dart';
import '../locale_bloc/locale_events.dart';
import '../locale_bloc/locale_state.dart';
part 'card.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purpleAccent,
),
body: const Body(),
);
}
}
class Body extends StatefulWidget {
const Body({super.key});
@override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
final scrollController = ScrollController();
final searchController = TextEditingController();
@override
void initState() {
SvgObjects.init();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<HomeBloc>().add(const HomeLoadDataEvent());
context.read<LikeBloc>().add(const LoadLikesEvent()); // событие на изменение лайка
});
scrollController.addListener(_onNextPageListener);
super.initState();
}
void _onNextPageListener() {
if (scrollController.offset >= scrollController.position.maxScrollExtent) {
final bloc = context.read<HomeBloc>();
if (!bloc.state.isPaginationLoading) {
bloc.add(HomeLoadDataEvent(
search: searchController.text,
nextPage: bloc.state.data?.nextPage,
));
}
}
}
@override
void dispose() {
searchController.dispose();
scrollController.dispose();
super.dispose();
}
final MovieRepository repo = MovieRepository();
var data = MovieRepository().loadData();
void _onLike(String? id, String title, bool isLiked) { // обработчик лайков
print("$id $title, $isLiked");
if (id != null) {
context.read<LikeBloc>().add(ChangeLikeEvent(id));
_showSnackBar(context, title, !isLiked);
}
}
void _showSnackBar(BuildContext context, String title, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
' ${isLiked ? context.locale.liked : context.locale.disliked} $title', //переписали константные строки под локаль
style: Theme.of(context).textTheme.bodyLarge,
),
backgroundColor: Colors.orangeAccent,
duration: const Duration(seconds: 1),
));
});
}
void _navToDetails(BuildContext context, CardData data) {
Navigator.push(
context, CupertinoPageRoute(builder: (context) => DetailsPage(data)));
}
Future<void> _onRefresh() {
context
.read<HomeBloc>()
.add(HomeLoadDataEvent(search: searchController.text));
return Future.value(null);
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Expanded(
child: CupertinoSearchTextField(
controller: searchController,
onChanged: (search) {
Debounce.run(() => context
.read<HomeBloc>()
.add(HomeLoadDataEvent(search: search)));
},
),
),
const SizedBox(width: 12), // Отступ между полем поиска и иконкой
GestureDetector(
onTap: () =>
context.read<LocaleBloc>().add(const ChangeLocaleEvent()), // смена иконки локализации
child: SizedBox.square(
dimension: 50,
child: BlocBuilder<LocaleBloc, LocaleState>(
builder: (context, state) {
return state.currentLocale.languageCode == 'ru'
? const SvgRu()
: const SvgUk();
},
),
),
),
],
),
),
BlocBuilder<HomeBloc, HomeState>( // обертка списка с карточками
builder: (context, state) => state.isLoading
? CircularProgressIndicator()
: BlocBuilder<LikeBloc, LikeState>(
builder: (context, likeState) => Expanded(
child: RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
controller: scrollController,
padding: EdgeInsets.zero,
itemCount: state.data?.data?.length ?? 0,
itemBuilder: (context, index) {
final data = state.data?.data?[index];
return data != null
? _Card.fromData(
data,
isLiked: likeState.likedIds
?.contains(data.id) ==
true,
onLike: _onLike,
onTap: () =>
_navToDetails(context, data),
)
: const SizedBox.shrink();
}),
),
))),
BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.isPaginationLoading
? const CircularProgressIndicator()
: const SizedBox.shrink())
],
));
}
}
class HomeData {
final List<CardData>? data;
final int? nextPage;
HomeData({this.data, this.nextPage});
}

View File

@ -0,0 +1,35 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pmd_labs/presentation/like_bloc/like_event.dart';
import 'package:pmd_labs/presentation/like_bloc/like_state.dart';
import 'package:shared_preferences/shared_preferences.dart';
const String _likedPrefsKey = 'liked';
class LikeBloc extends Bloc<LikeEvent, LikeState> {
LikeBloc() : super(const LikeState(likedIds: [])) {
on<ChangeLikeEvent>(_onChangeLike);
on<LoadLikesEvent>(_onLoadLikes);
}
Future<void> _onLoadLikes(LoadLikesEvent event, Emitter<LikeState> emit) async {
final prefs = await SharedPreferences.getInstance();
final data = prefs.getStringList(_likedPrefsKey);
emit(state.copyWith(likedIds: data));
}
Future<void> _onChangeLike(ChangeLikeEvent event, Emitter<LikeState> emit) async {
final updatedList = List<String>.from(state.likedIds ?? []);
if (updatedList.contains(event.id)) {
updatedList.remove(event.id);
} else {
updatedList.add(event.id);
}
final prefs = await SharedPreferences.getInstance();
prefs.setStringList(_likedPrefsKey, updatedList);
emit(state.copyWith(likedIds: updatedList));
}
}

View File

@ -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);
}

View File

@ -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<String>? likedIds;
const LikeState({required this.likedIds});
@override
List<Object?> get props => [likedIds];
}

View File

@ -0,0 +1,56 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'like_state.dart';
// **************************************************************************
// CopyWithGenerator
// **************************************************************************
abstract class _$LikeStateCWProxy {
LikeState likedIds(List<String>? likedIds);
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LikeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// LikeState(...).copyWith(id: 12, name: "My name")
/// ````
LikeState call({
List<String>? likedIds,
});
}
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfLikeState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfLikeState.copyWith.fieldName(...)`
class _$LikeStateCWProxyImpl implements _$LikeStateCWProxy {
const _$LikeStateCWProxyImpl(this._value);
final LikeState _value;
@override
LikeState likedIds(List<String>? likedIds) => this(likedIds: likedIds);
@override
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LikeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// LikeState(...).copyWith(id: 12, name: "My name")
/// ````
LikeState call({
Object? likedIds = const $CopyWithPlaceholder(),
}) {
return LikeState(
likedIds: likedIds == const $CopyWithPlaceholder()
? _value.likedIds
// ignore: cast_nullable_to_non_nullable
: likedIds as List<String>?,
);
}
}
extension $LikeStateCopyWith on LikeState {
/// Returns a callable class that can be used as follows: `instanceOfLikeState.copyWith(...)` or like so:`instanceOfLikeState.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$LikeStateCWProxy get copyWith => _$LikeStateCWProxyImpl(this);
}

View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pmd_labs/components/locale/l10n/app_locale.dart';
import 'package:pmd_labs/presentation/locale_bloc/locale_events.dart';
import 'package:pmd_labs/presentation/locale_bloc/locale_state.dart';
class LocaleBloc extends Bloc<LocaleEvent, LocaleState> {
LocaleBloc(Locale defaultLocale) : super(LocaleState(currentLocale: defaultLocale)) {
on<ChangeLocaleEvent>(_onChangeLocale);
}
Future<void> _onChangeLocale(ChangeLocaleEvent event, Emitter<LocaleState> emit) async {
final toChange = AppLocale.supportedLocales
.firstWhere((e) => e.languageCode != state.currentLocale.languageCode);
emit(state.copyWith(currentLocale: toChange));
}
}

View File

@ -0,0 +1,7 @@
abstract class LocaleEvent {
const LocaleEvent();
}
class ChangeLocaleEvent extends LocaleEvent {
const ChangeLocaleEvent();
}

View File

@ -0,0 +1,15 @@
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<Object?> get props => [currentLocale];
}

View File

@ -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);
}

23
makefile Normal file
View File

@ -0,0 +1,23 @@
gen:
flutter pub run build_runner build --delete-conflicting-outputs
hello:
echo "Hi!"; \
echo "I'm makefile"; \
echo "^_^"
icon:
flutter pub run flutter_launcher_icons:main
init_res:
dart pub global activate flutter_asset_generator
format:
dart format . --line-length 100
res:
fgen --output lib/components/resources.g.dart --no-watch --no-preview; \
make format
loc:
flutter gen-l10n

View File

@ -1,6 +1,43 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
url: "https://pub.dev"
source: hosted
version: "72.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.2"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
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:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.6.0"
async:
dependency: transitive
description:
@ -9,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:
@ -17,6 +62,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
build:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.dev"
source: hosted
version: "7.3.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
url: "https://pub.dev"
source: hosted
version: "8.9.2"
characters:
dependency: transitive
description:
@ -25,6 +134,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
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:
@ -33,6 +158,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
url: "https://pub.dev"
source: hosted
version: "4.10.0"
collection:
dependency: transitive
description:
@ -41,6 +174,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
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 main"
description:
name: copy_with_extension_gen
sha256: "51cd11094096d40824c8da629ca7f16f3b7cea5fc44132b679617483d43346b0"
url: "https://pub.dev"
source: hosted
version: "5.0.4"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
cupertino_icons:
dependency: "direct main"
description:
@ -49,6 +214,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
dio:
dependency: "direct main"
description:
name: dio
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
url: "https://pub.dev"
source: hosted
version: "5.7.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
equatable:
dependency: "direct main"
description:
name: equatable
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
url: "https://pub.dev"
source: hosted
version: "2.0.5"
fake_async:
dependency: transitive
description:
@ -57,11 +254,51 @@ 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:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_bloc:
dependency: "direct main"
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:
@ -70,11 +307,125 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
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:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
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:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
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 main"
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
io:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
js:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.7.1"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b
url: "https://pub.dev"
source: hosted
version: "6.8.0"
leak_tracker:
dependency: transitive
description:
@ -107,6 +458,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
url: "https://pub.dev"
source: hosted
version: "0.1.2-main.4"
matcher:
dependency: transitive
description:
@ -131,6 +498,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.15.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
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:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
@ -139,11 +530,195 @@ 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:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
pretty_dio_logger:
dependency: "direct main"
description:
name: pretty_dio_logger
sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407"
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:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
url: "https://pub.dev"
source: hosted
version: "1.3.0"
shared_preferences:
dependency: "direct main"
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: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.3"
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:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.1"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
url: "https://pub.dev"
source: hosted
version: "1.3.4"
source_span:
dependency: transitive
description:
@ -168,6 +743,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
@ -192,6 +775,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.2"
timing:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "773c9522d66d523e1c7b25dfb95cc91c26a1e17b107039cfe147285e92de7878"
url: "https://pub.dev"
source: hosted
version: "1.1.14"
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: ab9ff38fc771e9ee1139320adbe3d18a60327370c218c60752068ebee4b49ab1
url: "https://pub.dev"
source: hosted
version: "1.1.15"
vector_math:
dependency: transitive
description:
@ -208,6 +831,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.2.5"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
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:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.5.2 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
dart: ">=3.5.3 <4.0.0"
flutter: ">=3.24.0"

View File

@ -1,90 +1,54 @@
name: pmd_labs
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ^3.5.2
sdk: ^3.5.3
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
# Виджеты
cupertino_icons: ^1.0.8
flutter_svg: 2.0.7
json_annotation: ^4.8.1
dio: ^5.4.2+1
pretty_dio_logger: ^1.3.1
equatable: ^2.0.5
flutter_bloc: ^8.1.5
copy_with_extension_gen: ^5.0.4
flutter_localizations:
sdk: flutter
intl: ^0.19.0
shared_preferences: 2.2.3
dev_dependencies:
flutter_test:
sdk: flutter
# 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.
build_runner: ^2.4.9
json_serializable: ^6.7.1
flutter_lints: ^4.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
flutter_launcher_icons: 0.13.1
flutter_icons:
android: "ic_launcher"
ios: true
image_path: "assets/icon1.jpg"
min_sdk_android: 21
# The following section is specific to Flutter packages.
flutter:
# 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
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
generate: true
assets:
- assets/svg/