diff --git a/lib/components/extension/context_x.dart b/lib/components/extension/context_x.dart new file mode 100644 index 0000000..7bdd724 --- /dev/null +++ b/lib/components/extension/context_x.dart @@ -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)!; +} diff --git a/lib/components/locale/l10n/app_locale.dart b/lib/components/locale/l10n/app_locale.dart index 73fb18a..5f646e6 100644 --- a/lib/components/locale/l10n/app_locale.dart +++ b/lib/components/locale/l10n/app_locale.dart @@ -106,14 +106,8 @@ abstract class AppLocale { /// No description provided for @disliked. /// /// In ru, this message translates to: - /// **'разонравился :('** - String get disliked; - - /// No description provided for @arbEnding. - /// - /// In ru, this message translates to: - /// **'Чтобы не забыть про отсутствие запятой :)'** - String get arbEnding; + /// **'разонравился'** + String get unliked; } class _AppLocaleDelegate extends LocalizationsDelegate { diff --git a/lib/components/locale/l10n/app_locale_en.dart b/lib/components/locale/l10n/app_locale_en.dart index 6dd52c6..90b81dd 100644 --- a/lib/components/locale/l10n/app_locale_en.dart +++ b/lib/components/locale/l10n/app_locale_en.dart @@ -11,8 +11,6 @@ class AppLocaleEn extends AppLocale { String get liked => 'liked!'; @override - String get disliked => 'disliked :('; + String get unliked => 'disliked'; - @override - String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)'; } diff --git a/lib/components/locale/l10n/app_locale_ru.dart b/lib/components/locale/l10n/app_locale_ru.dart index d4e3938..4d78b7f 100644 --- a/lib/components/locale/l10n/app_locale_ru.dart +++ b/lib/components/locale/l10n/app_locale_ru.dart @@ -11,8 +11,5 @@ class AppLocaleRu extends AppLocale { String get liked => 'понравился!'; @override - String get disliked => 'разонравился :('; - - @override - String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)'; + String get unliked => 'разонравился'; } diff --git a/lib/components/resources.g.dart b/lib/components/resources.g.dart new file mode 100644 index 0000000..915a8d9 --- /dev/null +++ b/lib/components/resources.g.dart @@ -0,0 +1,10 @@ +/// Generate by [asset_generator](https://github.com/fluttercandies/flutter_asset_generator) library. +/// PLEASE DO NOT EDIT MANUALLY. +// ignore_for_file: constant_identifier_names +class R { + const R._(); + + static const String ASSETS_SVG_RU_SVG = 'assets/svg/ru.svg'; + + static const String ASSETS_SVG_US_SVG = 'assets/svg/us.svg'; +} diff --git a/lib/domain/models/carddata.dart b/lib/domain/models/carddata.dart index e1201e5..c6d0595 100644 --- a/lib/domain/models/carddata.dart +++ b/lib/domain/models/carddata.dart @@ -3,8 +3,10 @@ class CardData { final String text; final String descriptionText; final String? imageUrl; + final String? id; CardData(this.text, {required this.descriptionText, - this.imageUrl}); + this.imageUrl, + this.id}); } \ No newline at end of file diff --git a/lib/presentation/common/svg_objects.dart b/lib/presentation/common/svg_objects.dart new file mode 100644 index 0000000..3af15c2 --- /dev/null +++ b/lib/presentation/common/svg_objects.dart @@ -0,0 +1,34 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_app/components/resources.g.dart'; + +abstract class SvgObjects { + static void init() { + final pics = [ + R.ASSETS_SVG_RU_SVG, + R.ASSETS_SVG_US_SVG, + ]; + for (final String p in pics) { + final loader = SvgAssetLoader(p); + svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null)); + } + } +} + +class SvgRu extends StatelessWidget { + const SvgRu({super.key}); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset(R.ASSETS_SVG_RU_SVG); + } +} + +class SvgUk extends StatelessWidget { + const SvgUk({super.key}); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset(R.ASSETS_SVG_US_SVG); + } +} diff --git a/lib/presentation/home_page/card.dart b/lib/presentation/home_page/card.dart index d911741..c1407f2 100644 --- a/lib/presentation/home_page/card.dart +++ b/lib/presentation/home_page/card.dart @@ -1,103 +1,130 @@ part of 'home_page.dart'; -class _CardState extends State<_Card> { - bool isLiked = false; +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: widget.onTap, + onTap: onTap, child: Container( - margin: const EdgeInsets.only(top: 16), - constraints: const BoxConstraints(minHeight: 140), - padding: const EdgeInsets.all(16), + margin: const EdgeInsets.all(16), + constraints: const BoxConstraints(minHeight: 160), decoration: BoxDecoration( - color: Colors.amber, + color: Colors.white70, borderRadius: BorderRadius.circular(20), - border: Border.all( - color: Colors.grey, - width: 2, - ), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(.5), + spreadRadius: 4, + offset: const Offset(0, 5), + blurRadius: 8, + ), + ], ), child: IntrinsicHeight( child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( - borderRadius: BorderRadius.circular(20), + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(20), + topLeft: Radius.circular(20), + ), child: SizedBox( - width: 140, - height: 100, - child: Image.network( - widget.imageUrl ?? '', - fit: BoxFit.cover, - errorBuilder: (_, __, ___) => const Placeholder(), + height: double.infinity, + width: 120, + child: Stack( + children: [ + Positioned.fill( + child: Image.network( + imageUrl ?? '', + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => const Placeholder(), + ), + ), + ], ), ), ), - Flexible( - child: Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(widget.text, - style: Theme.of(context).textTheme.headlineLarge), - Text(widget.descriptionText, - style: Theme.of(context).textTheme.bodyLarge), - ], + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + text, + style: Theme.of(context).textTheme.headlineSmall, + ), + Text( + descriptionText, + style: Theme.of(context).textTheme.bodyLarge, + ) + ], + ), ), - )), - Padding( - padding: - const EdgeInsets.only(left: 8.0, right: 16, bottom: 16), + ), + Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: const EdgeInsets.only(left: 8, right: 16, bottom: 16), child: GestureDetector( - onTap: () { - setState(() { - isLiked = !isLiked; - }); - widget.onLike?.call(widget.text, isLiked); - }, + onTap: () => onLike?.call(id, text, isLiked), child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), + duration: const Duration(milliseconds: 200), child: isLiked ? const Icon( - Icons.favorite, - color: Colors.redAccent, - key: ValueKey(0), - ) - : const Icon(Icons.favorite_border), - key: ValueKey(1), + Icons.favorite, + color: Colors.redAccent, + key: ValueKey(0), + ) + : const Icon( + Icons.favorite_border, + key: ValueKey(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(); -} +} \ No newline at end of file diff --git a/lib/presentation/like_bloc/like_bloc.dart b/lib/presentation/like_bloc/like_bloc.dart new file mode 100644 index 0000000..b117829 --- /dev/null +++ b/lib/presentation/like_bloc/like_bloc.dart @@ -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 { + LikeBloc() : super(const LikeState(likedIds: [])) { + on(_onChangeLike); + on(_onLoadLikes); + } + + Future _onLoadLikes(LoadLikesEvent event, Emitter emit) async { + final prefs = await SharedPreferences.getInstance(); + final data = prefs.getStringList(_likedPrefsKey); + + emit(state.copyWith(likedIds: data)); + } + + Future _onChangeLike(ChangeLikeEvent event, Emitter emit) async { + final updatedList = List.from(state.likedIds ?? []); + + if (updatedList.contains(event.id)) { + updatedList.remove(event.id); + } else { + updatedList.add(event.id); + } + + final prefs = await SharedPreferences.getInstance(); + prefs.setStringList(_likedPrefsKey, updatedList); + + emit(state.copyWith(likedIds: updatedList)); + } +} diff --git a/lib/presentation/like_bloc/like_event.dart b/lib/presentation/like_bloc/like_event.dart new file mode 100644 index 0000000..d0326d8 --- /dev/null +++ b/lib/presentation/like_bloc/like_event.dart @@ -0,0 +1,13 @@ +abstract class LikeEvent { + const LikeEvent(); +} + +class LoadLikesEvent extends LikeEvent { + const LoadLikesEvent(); +} + +class ChangeLikeEvent extends LikeEvent { + final String id; + + const ChangeLikeEvent(this.id); +} diff --git a/lib/presentation/like_bloc/like_state.dart b/lib/presentation/like_bloc/like_state.dart new file mode 100644 index 0000000..5f0959f --- /dev/null +++ b/lib/presentation/like_bloc/like_state.dart @@ -0,0 +1,14 @@ +import 'package:copy_with_extension/copy_with_extension.dart'; +import 'package:equatable/equatable.dart'; + +part 'like_state.g.dart'; + +@CopyWith() +class LikeState extends Equatable { + final List? likedIds; + + const LikeState({required this.likedIds}); + + @override + List get props => [likedIds]; +} diff --git a/lib/presentation/like_bloc/like_state.g.dart b/lib/presentation/like_bloc/like_state.g.dart new file mode 100644 index 0000000..0888cf2 --- /dev/null +++ b/lib/presentation/like_bloc/like_state.g.dart @@ -0,0 +1,56 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'like_state.dart'; + +// ************************************************************************** +// CopyWithGenerator +// ************************************************************************** + +abstract class _$LikeStateCWProxy { + LikeState likedIds(List? likedIds); + + /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LikeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. + /// + /// Usage + /// ```dart + /// LikeState(...).copyWith(id: 12, name: "My name") + /// ```` + LikeState call({ + List? likedIds, + }); +} + +/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfLikeState.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfLikeState.copyWith.fieldName(...)` +class _$LikeStateCWProxyImpl implements _$LikeStateCWProxy { + const _$LikeStateCWProxyImpl(this._value); + + final LikeState _value; + + @override + LikeState likedIds(List? likedIds) => this(likedIds: likedIds); + + @override + + /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `LikeState(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. + /// + /// Usage + /// ```dart + /// LikeState(...).copyWith(id: 12, name: "My name") + /// ```` + LikeState call({ + Object? likedIds = const $CopyWithPlaceholder(), + }) { + return LikeState( + likedIds: likedIds == const $CopyWithPlaceholder() + ? _value.likedIds + // ignore: cast_nullable_to_non_nullable + : likedIds as List?, + ); + } +} + +extension $LikeStateCopyWith on LikeState { + /// Returns a callable class that can be used as follows: `instanceOfLikeState.copyWith(...)` or like so:`instanceOfLikeState.copyWith.fieldName(...)`. + // ignore: library_private_types_in_public_api + _$LikeStateCWProxy get copyWith => _$LikeStateCWProxyImpl(this); +} diff --git a/lib/presentation/locale_bloc/locale_bloc.dart b/lib/presentation/locale_bloc/locale_bloc.dart new file mode 100644 index 0000000..6c4d672 --- /dev/null +++ b/lib/presentation/locale_bloc/locale_bloc.dart @@ -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 { + LocaleBloc(Locale defaultLocale) : super(LocaleState(currentLocale: defaultLocale)) { + on(_onChangeLocale); + } + + Future _onChangeLocale(ChangeLocaleEvent event, Emitter emit) async { + final toChange = AppLocale.supportedLocales + .firstWhere((e) => e.languageCode != state.currentLocale.languageCode); + emit(state.copyWith(currentLocale: toChange)); + } +} diff --git a/lib/presentation/locale_bloc/locale_events.dart b/lib/presentation/locale_bloc/locale_events.dart new file mode 100644 index 0000000..c08cd1b --- /dev/null +++ b/lib/presentation/locale_bloc/locale_events.dart @@ -0,0 +1,7 @@ +abstract class LocaleEvent { + const LocaleEvent(); +} + +class ChangeLocaleEvent extends LocaleEvent { + const ChangeLocaleEvent(); +} diff --git a/lib/presentation/locale_bloc/locale_state.dart b/lib/presentation/locale_bloc/locale_state.dart new file mode 100644 index 0000000..b9221ca --- /dev/null +++ b/lib/presentation/locale_bloc/locale_state.dart @@ -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 get props => [currentLocale]; +} diff --git a/lib/presentation/locale_bloc/locale_state.g.dart b/lib/presentation/locale_bloc/locale_state.g.dart new file mode 100644 index 0000000..23ee063 --- /dev/null +++ b/lib/presentation/locale_bloc/locale_state.g.dart @@ -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); +} diff --git a/pubspec.lock b/pubspec.lock index 5e949c1..9947209 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -22,6 +22,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.0" + archive: + dependency: transitive + description: + name: archive + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + url: "https://pub.dev" + source: hosted + version: "3.6.1" args: dependency: transitive description: @@ -134,6 +142,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -166,6 +182,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + copy_with_extension: + dependency: transitive + description: + name: copy_with_extension + sha256: fbcf890b0c34aedf0894f91a11a579994b61b4e04080204656b582708b5b1125 + url: "https://pub.dev" + source: hosted + version: "5.0.4" + copy_with_extension_gen: + dependency: "direct main" + description: + name: copy_with_extension_gen + sha256: "51cd11094096d40824c8da629ca7f16f3b7cea5fc44132b679617483d43346b0" + url: "https://pub.dev" + source: hosted + version: "5.0.4" crypto: dependency: transitive description: @@ -259,14 +291,27 @@ packages: 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: name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" 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: dependency: "direct main" description: @@ -333,6 +378,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image: + dependency: transitive + description: + name: image + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d + url: "https://pub.dev" + source: hosted + version: "4.3.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" io: dependency: transitive description: @@ -393,10 +454,10 @@ packages: dependency: transitive description: name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "2.1.1" logging: dependency: transitive description: @@ -542,7 +603,7 @@ packages: source: hosted version: "1.4.0" provider: - dependency: "direct main" + dependency: transitive description: name: provider sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c @@ -566,13 +627,13 @@ packages: source: hosted version: "1.3.0" shared_preferences: - dependency: "direct dev" + dependency: "direct main" description: name: shared_preferences - sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.2.3" shared_preferences_android: dependency: transitive description: @@ -827,5 +888,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.2 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3b61e4c..90d469f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,41 +1,59 @@ name: flutter_app description: "Лабораторные по дисциплине ПМУ" -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - +publish_to: 'none' version: 1.0.0+1 environment: - sdk: ^3.5.2 + sdk: '>=3.1.0 <4.0.0' dependencies: flutter: sdk: flutter - provider: ^6.0.0 - cupertino_icons: ^1.0.8 + # Виджеты + cupertino_icons: ^1.0.2 flutter_svg: 2.0.7 + # Сетевое взаимодействие json_annotation: ^4.8.1 dio: ^5.4.2+1 pretty_dio_logger: ^1.3.1 + # BLoC equatable: ^2.0.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: flutter_test: sdk: flutter + flutter_lints: ^2.0.0 + + # Иконки + flutter_launcher_icons: 0.13.1 + + # Сетевое взаимодействие build_runner: ^2.4.9 json_serializable: ^6.7.1 - shared_preferences: ^2.3.0 - flutter_lints: ^4.0.0 - -flutter: - uses-material-design: true flutter_icons: android: "ic_launcher" ios: true - image_path: "assets/icon.jpg" - min_sdk_android: 21 \ No newline at end of file + image_path: "assets/launcher.jpeg" + min_sdk_android: 21 + +flutter: + generate: true + + uses-material-design: true + + assets: + - assets/svg/ \ No newline at end of file