7 lab check 2

This commit is contained in:
sardq 2024-10-17 10:44:09 +04:00
parent 2654fd0dff
commit 5add92307a
18 changed files with 469 additions and 109 deletions

View File

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

View File

@ -106,14 +106,8 @@ abstract class AppLocale {
/// No description provided for @disliked. /// No description provided for @disliked.
/// ///
/// In ru, this message translates to: /// In ru, this message translates to:
/// **'разонравился :('** /// **'разонравился'**
String get disliked; String get unliked;
/// No description provided for @arbEnding.
///
/// In ru, this message translates to:
/// **'Чтобы не забыть про отсутствие запятой :)'**
String get arbEnding;
} }
class _AppLocaleDelegate extends LocalizationsDelegate<AppLocale> { class _AppLocaleDelegate extends LocalizationsDelegate<AppLocale> {

View File

@ -11,8 +11,6 @@ class AppLocaleEn extends AppLocale {
String get liked => 'liked!'; String get liked => 'liked!';
@override @override
String get disliked => 'disliked :('; String get unliked => 'disliked';
@override
String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)';
} }

View File

@ -11,8 +11,5 @@ class AppLocaleRu extends AppLocale {
String get liked => 'понравился!'; String get liked => 'понравился!';
@override @override
String get disliked => 'разонравился :('; String get unliked => 'разонравился';
@override
String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)';
} }

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

@ -3,8 +3,10 @@ class CardData {
final String text; final String text;
final String descriptionText; final String descriptionText;
final String? imageUrl; final String? imageUrl;
final String? id;
CardData(this.text, CardData(this.text,
{required this.descriptionText, {required this.descriptionText,
this.imageUrl}); this.imageUrl,
this.id});
} }

View File

@ -0,0 +1,34 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_app/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

@ -1,103 +1,130 @@
part of 'home_page.dart'; part of 'home_page.dart';
class _CardState extends State<_Card> { typedef OnLikeCallback = void Function(String? id, String title, bool isLiked)?;
bool isLiked = false;
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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: widget.onTap, onTap: onTap,
child: Container( child: Container(
margin: const EdgeInsets.only(top: 16), margin: const EdgeInsets.all(16),
constraints: const BoxConstraints(minHeight: 140), constraints: const BoxConstraints(minHeight: 160),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.amber, color: Colors.white70,
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
border: Border.all( boxShadow: [
color: Colors.grey, BoxShadow(
width: 2, color: Colors.grey.withOpacity(.5),
), spreadRadius: 4,
offset: const Offset(0, 5),
blurRadius: 8,
),
],
), ),
child: IntrinsicHeight( child: IntrinsicHeight(
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(20), borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(20),
topLeft: Radius.circular(20),
),
child: SizedBox( child: SizedBox(
width: 140, height: double.infinity,
height: 100, width: 120,
child: Image.network( child: Stack(
widget.imageUrl ?? '', children: [
fit: BoxFit.cover, Positioned.fill(
errorBuilder: (_, __, ___) => const Placeholder(), child: Image.network(
imageUrl ?? '',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const Placeholder(),
),
),
],
), ),
), ),
), ),
Flexible( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 16.0), padding: const EdgeInsets.only(left: 16.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(widget.text, Text(
style: Theme.of(context).textTheme.headlineLarge), text,
Text(widget.descriptionText, style: Theme.of(context).textTheme.headlineSmall,
style: Theme.of(context).textTheme.bodyLarge), ),
], Text(
descriptionText,
style: Theme.of(context).textTheme.bodyLarge,
)
],
),
), ),
)), ),
Padding( Align(
padding: alignment: Alignment.bottomRight,
const EdgeInsets.only(left: 8.0, right: 16, bottom: 16), child: Padding(
padding: const EdgeInsets.only(left: 8, right: 16, bottom: 16),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () => onLike?.call(id, text, isLiked),
setState(() {
isLiked = !isLiked;
});
widget.onLike?.call(widget.text, isLiked);
},
child: AnimatedSwitcher( child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 200),
child: isLiked child: isLiked
? const Icon( ? const Icon(
Icons.favorite, Icons.favorite,
color: Colors.redAccent, color: Colors.redAccent,
key: ValueKey<int>(0), key: ValueKey<int>(0),
) )
: const Icon(Icons.favorite_border), : const Icon(
key: ValueKey<int>(1), Icons.favorite_border,
key: ValueKey<int>(1),
),
), ),
)) ),
),
),
], ],
), ),
), ),
), ),
); );
} }
} }
typedef onLikeCallback = void Function(String title, bool isliked)?;
class _Card extends StatefulWidget {
final String text;
final String descriptionText;
final String? imageUrl;
final onLikeCallback onLike;
final VoidCallback? onTap;
const _Card(this.text,
{required this.descriptionText, this.imageUrl, this.onLike, this.onTap});
factory _Card.fromData(CardData data, {onLikeCallback onLike, VoidCallback? onTap}) =>
_Card(data.text,
descriptionText: data.descriptionText,
imageUrl: data.imageUrl,
onLike: onLike,
onTap: onTap);
@override
State<_Card> createState() => _CardState();
}

View File

@ -0,0 +1,35 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_app/presentation/like_bloc/like_event.dart';
import 'package:flutter_app/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:flutter_app/components/locale/l10n/app_locale.dart';
import 'package:flutter_app/presentation/locale_bloc/locale_events.dart';
import 'package:flutter_app/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,56 @@
// 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);
}

View File

@ -22,6 +22,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.7.0" version: "6.7.0"
archive:
dependency: transitive
description:
name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
url: "https://pub.dev"
source: hosted
version: "3.6.1"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -134,6 +142,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.3" version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -166,6 +182,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" 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: crypto:
dependency: transitive dependency: transitive
description: description:
@ -259,14 +291,27 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.6" 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: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "2.0.3"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_svg: flutter_svg:
dependency: "direct main" dependency: "direct main"
description: description:
@ -333,6 +378,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" 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: io:
dependency: transitive dependency: transitive
description: description:
@ -393,10 +454,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "2.1.1"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -542,7 +603,7 @@ packages:
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
provider: provider:
dependency: "direct main" dependency: transitive
description: description:
name: provider name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
@ -566,13 +627,13 @@ packages:
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
shared_preferences: shared_preferences:
dependency: "direct dev" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.2.3"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
@ -827,5 +888,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.5.2 <4.0.0" dart: ">=3.5.0 <4.0.0"
flutter: ">=3.24.0" flutter: ">=3.24.0"

View File

@ -1,41 +1,59 @@
name: flutter_app name: flutter_app
description: "Лабораторные по дисциплине ПМУ" description: "Лабораторные по дисциплине ПМУ"
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none'
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ^3.5.2 sdk: '>=3.1.0 <4.0.0'
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
provider: ^6.0.0 # Виджеты
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.2
flutter_svg: 2.0.7 flutter_svg: 2.0.7
# Сетевое взаимодействие
json_annotation: ^4.8.1 json_annotation: ^4.8.1
dio: ^5.4.2+1 dio: ^5.4.2+1
pretty_dio_logger: ^1.3.1 pretty_dio_logger: ^1.3.1
# BLoC
equatable: ^2.0.5 equatable: ^2.0.5
flutter_bloc: ^8.1.5 flutter_bloc: ^8.1.5
copy_with_extension_gen: ^5.0.4
# Localization
flutter_localizations:
sdk: flutter
intl: 0.19.0
shared_preferences: 2.2.3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.0
# Иконки
flutter_launcher_icons: 0.13.1
# Сетевое взаимодействие
build_runner: ^2.4.9 build_runner: ^2.4.9
json_serializable: ^6.7.1 json_serializable: ^6.7.1
shared_preferences: ^2.3.0
flutter_lints: ^4.0.0
flutter:
uses-material-design: true
flutter_icons: flutter_icons:
android: "ic_launcher" android: "ic_launcher"
ios: true ios: true
image_path: "assets/icon.jpg" image_path: "assets/launcher.jpeg"
min_sdk_android: 21 min_sdk_android: 21
flutter:
generate: true
uses-material-design: true
assets:
- assets/svg/