This commit is contained in:
Ivan 2024-12-17 17:27:25 +04:00
parent 71b375ff79
commit d10601b204
43 changed files with 1243 additions and 163 deletions

15
Makefile.mk Normal file
View File

@ -0,0 +1,15 @@
gen:
flutter pub run build_runner build --delete-conflicting-outputs
icon:
flutter pub run flutter_launcher_icons:main
init_res:
dart pub global activate flutter_asset_generator
flutter pub global activate fgen
format:
dart format . --line-length 100
res:
fgen --output lib/components/resources.g.dart --no-watch --no-preview; \
make format
loc:
flutter gen-l10n; \
make format

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

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

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9 6" width="900" height="600"><rect fill="#fff" width="9" height="3"/><rect fill="#d52b1e" y="3" width="9" height="3"/><rect fill="#0039a6" y="2" width="9" height="2"/></svg>

After

Width:  |  Height:  |  Size: 265 B

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

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-us" viewBox="0 0 640 480">
<path fill="#bd3d44" d="M0 0h640v480H0"/>
<path stroke="#fff" stroke-width="37" d="M0 55.3h640M0 129h640M0 203h640M0 277h640M0 351h640M0 425h640"/>
<path fill="#192f5d" d="M0 0h364.8v258.5H0"/>
<marker id="us-a" markerHeight="30" markerWidth="30">
<path fill="#fff" d="m14 0 9 27L0 10h28L5 27z"/>
</marker>
<path fill="none" marker-mid="url(#us-a)" d="m0 0 16 11h61 61 61 61 60L47 37h61 61 60 61L16 63h61 61 61 61 60L47 89h61 61 60 61L16 115h61 61 61 61 60L47 141h61 61 60 61L16 166h61 61 61 61 60L47 192h61 61 60 61L16 218h61 61 61 61 60z"/>
</svg>

After

Width:  |  Height:  |  Size: 648 B

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

9
l10n/app_en.arb Normal file
View File

@ -0,0 +1,9 @@
{
"@@locale": "en",
"search": "Search",
"liked": "liked!",
"disliked": "disliked :(",
"arbEnding": "Чтобы не забыть про отсутствие запятой :)"
}

9
l10n/app_ru.arb Normal file
View File

@ -0,0 +1,9 @@
{
"@@locale": "ru",
"search": "Поиск",
"liked": "понравился!",
"disliked": "разонравился :(",
"arbEnding": "Чтобы не забыть про отсутствие запятой :)"
}

View File

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

View File

@ -0,0 +1,153 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;
import 'app_locale_en.dart';
import 'app_locale_ru.dart';
// ignore_for_file: type=lint
/// Callers can lookup localized strings with an instance of AppLocale
/// returned by `AppLocale.of(context)`.
///
/// Applications need to include `AppLocale.delegate()` in their app's
/// `localizationDelegates` list, and the locales they support in the app's
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/app_locale.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AppLocale.localizationsDelegates,
/// supportedLocales: AppLocale.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```yaml
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # Rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, 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;
/// No description provided for @arbEnding.
///
/// In ru, this message translates to:
/// **'Чтобы не забыть про отсутствие запятой :)'**
String get arbEnding;
}
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,20 @@
import 'app_locale.dart';
// ignore_for_file: type=lint
/// The translations for English (`en`).
class AppLocaleEn extends AppLocale {
AppLocaleEn([String locale = 'en']) : super(locale);
@override
String get search => 'Search';
@override
String get liked => 'liked!';
@override
String get disliked => 'disliked :(';
@override
String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)';
}

View File

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

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,20 @@
import 'dart:async';
import 'dart:ui';
class Debounce {
factory Debounce() => _instance;
Debounce._();
static final Debounce _instance = Debounce._();
static Timer? _timer;
static void run(
VoidCallback action, {
Duration delay = const Duration(milliseconds: 700),
}) {
_timer?.cancel();
_timer = Timer(delay, action);
}
}

View File

@ -19,7 +19,7 @@ class PlaceDataDto {
final String? description;
final String? imageData;
const PlaceDataDto({this.id, this.name, this.yearBuilt, this.description, this.imageData});
const PlaceDataDto({this.id, this.name, this.yearBuilt, this.description, this.imageData});
factory PlaceDataDto.fromJson(Map<String, dynamic> json) => _$PlaceDataDtoFromJson(json);
}
}

View File

@ -5,15 +5,16 @@ import '../dtos/PlaceDTO.dart';
extension PlaceDataDtoToModel on PlaceDataDto {
CardData toDomain() => CardData(
name ?? 'Неизвестное имя',
//imageUrl: attributes?.image,
descriptionText: getText(),
//bytes: (imagedata != null) ? base64.decode(imagedata!) : null,
bytes: imageData?? null,
);
String getText(){
name ?? 'Неизвестное имя',
//imageUrl: attributes?.image,
descriptionText: getText(),
//bytes: (imagedata != null) ? base64.decode(imagedata!) : null,
bytes: imageData ?? null,
id: id.toString()
);
String getText() {
final String str1 = description ?? "Описания нет";
final String str2 = yearBuilt?.toString() ?? "Даты построики нет";
return "$str1\n$str2 год";
return "$str1\n--------------\n$str2 год";
}
}
}

View File

@ -4,33 +4,29 @@ import 'package:pmu_new/data/repositories/api_Interface.dart';
import '../../models/CardData.dart';
class MockRepository extends ApiInterface{
class MockRepository extends ApiInterface {
@override
Future<List<CardData>?> loadData({OnErrorCallback? onError}) async{
Future<List<CardData>?> loadData({OnErrorCallback? onError}) async {
final data = [
CardData(
'Фрукты',
descriptionText:
'Какие же они полезные и сладкие!!!! Generate Lorem Ipsum placeholder text for use in your graphic, print and web layouts, and discover plugins for your favorite writing, design and blogging tools',
'Какие же они полезные и сладкие!!!! Generate Lorem Ipsum placeholder text for use in your graphic, print and web layouts, and discover plugins for your favorite writing, design and blogging tools',
imageUrl:
'https://media.gettyimages.com/id/182810893/photo/fruit-mix.jpg?s=612x612&w=0&k=20&c=v9jopDXbS5LCXY1d8uSwEldLJVVkOpYtYtyHD8azWDU=',
'https://media.gettyimages.com/id/182810893/photo/fruit-mix.jpg?s=612x612&w=0&k=20&c=v9jopDXbS5LCXY1d8uSwEldLJVVkOpYtYtyHD8azWDU=',
),
CardData(
'Киви',
descriptionText:
'сладкий и спелый, можно купить по акции прямо сейчас звоните не пожалеете',
Backgroundcolor: Color.fromARGB(10, 210, 30, 40),
imageUrl:
'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-09b.jpg',
'сладкий и спелый, можно купить по акции прямо сейчас звоните не пожалеете',
imageUrl: 'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-09b.jpg',
),
CardData(
'Банан',
descriptionText: 'Ого, че с ним произошло',
Backgroundcolor: Color.fromARGB(30, 30, 210, 15),
imageUrl:
'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-01.jpg',
imageUrl: 'https://www.diyphotography.net/files/images/3/pictures-of-sliced-fruits-01.jpg',
),
];
return data;
}
}
}

View File

@ -15,24 +15,29 @@ class PlacesRepository extends ApiInterface {
requestHeader: true,
requestBody: true,
));
static const String _baseUrl = 'https://localhost:5001';
static const String _baseUrl = 'http://192.168.184.215:5001';
//static const String _baseUrl = 'https://localhost:5001';
@override
Future<List<CardData>?> loadData({int? q, OnErrorCallback? onError}) async {
try {
const String url = '$_baseUrl/api/Main/GetManufactureList';
Future<List<CardData>?> loadData({int? q, OnErrorCallback? onError}) async {
try {
const String url = '$_baseUrl/api/Main/GetManufactureList';
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
url,
queryParameters: q != null ? {'year': q} : null,
);
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
url,
queryParameters: q != null ? {'year': q} : null,
);
final PlacesDto dto = PlacesDto.fromJson(response.data as Map<String, dynamic>);
final List<CardData>? data = dto.data?.map((e) => e.toDomain()).toList();
//debugPrint("length: ${data?.length}");
return data;
} on DioException catch (e) {
onError?.call(e.response?.statusMessage);
return null;
}
final PlacesDto dto = PlacesDto.fromJson(response.data as Map<String, dynamic>);
final List<CardData>? data = dto.data?.map((e) => e.toDomain()).toList();
//debugPrint("length: ${data?.length}");
return data;
} on DioException catch (e) {
if (e.response != null) {
print(e.response!.data);
print(e.response!.headers);
print(e.response!.requestOptions);}
onError?.call(e.error?.toString());
return null;
}
}
}
}

View File

@ -4,4 +4,4 @@ typedef OnErrorCallback = void Function(String? error);
abstract class ApiInterface {
Future<List<CardData>?> loadData({OnErrorCallback? onError});
}
}

View File

@ -1,5 +1,14 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:pmu_new/presentation/home_page/bloc/bloc.dart';
import 'package:pmu_new/presentation/home_page/like_bloc/like_bloc.dart';
import 'package:pmu_new/presentation/locale_bloc/locale_bloc.dart';
import 'package:pmu_new/presentation/locale_bloc/locale_state.dart';
import 'components/locale/l10n/app_locale.dart';
import 'data/repositories/PlacesRepository.dart';
import 'presentation/home_page/home_page.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
enum FoodType { fruit, vegetable, meat, fish, sweet }
@ -54,16 +63,33 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Старостин Иван Константинович'),
);
return BlocProvider<LocaleBloc>(
lazy: false,
create: (context) => LocaleBloc(Locale(Platform.localeName)),
child: BlocBuilder<LocaleBloc, LocaleState>(builder: (context, state) {
return MaterialApp(
localizationsDelegates: AppLocale.localizationsDelegates,
locale: state.currentLocale,
supportedLocales: AppLocale.supportedLocales,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: RepositoryProvider<PlacesRepository>(
lazy: true,
create: (_) => PlacesRepository(),
child: BlocProvider<LikeBloc>(
lazy: false,
create: (context) => LikeBloc(),
child: BlocProvider<HomeBloc>(
lazy: false,
create: (context) => HomeBloc(context.read<PlacesRepository>()),
child: const MyHomePage(title: "Старостин Иван Константинович"),
),
),
),
);
}));
}
}

View File

@ -5,15 +5,15 @@ import 'package:flutter/material.dart';
class CardData {
final String text;
final String descriptionText;
final Color Backgroundcolor;
final String? imageUrl;
final String? bytes;
final String? id;
CardData(
this.text, {
required this.descriptionText,
this.Backgroundcolor = const Color.fromARGB(70, 173, 216, 230),
this.imageUrl = 'https://simbircity.ru/wp-content/uploads/2020/10/1IMG_36222.jpg',
this.bytes = null,
});
}
this.text, {
required this.descriptionText,
this.imageUrl = 'https://simbircity.ru/wp-content/uploads/2020/10/1IMG_36222.jpg',
this.bytes = null,
this.id
});
}

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:pmu_new/models/CardData.dart';
@ -15,9 +17,11 @@ class DetailsPage extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Image.network(
data.imageUrl ?? '',
),
child: data.bytes != null
? Image.memory(base64Decode(data.bytes!))
: Image.network(
data.imageUrl ?? '',
),
),
Padding(
padding: const EdgeInsets.only(bottom: 4.0),
@ -34,4 +38,4 @@ class DetailsPage extends StatelessWidget {
),
);
}
}
}

View File

@ -4,23 +4,21 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:pmu_new/models/CardData.dart';
class MyCard extends StatefulWidget {
class MyCard extends StatelessWidget {
final CardData cardData;
final VoidCallback onTap;
final void Function(String title, bool isliked)? onlike;
final Color backgroundColor;
//final String? id;
final bool isliked;
final void Function(String? id, String title, bool isliked)? onlike;
const MyCard({
MyCard({
required this.cardData,
required this.onTap,
this.onlike
this.onlike,
this.backgroundColor = const Color.fromARGB(70, 173, 216, 230),
this.isliked=false,
});
@override
State<MyCard> createState() => _CardState();
}
class _CardState extends State<MyCard> {
bool isliked = false;
double turns = 0.0;
@override
@ -29,7 +27,7 @@ class _CardState extends State<MyCard> {
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: this.widget.cardData.Backgroundcolor,
color: this.backgroundColor,
borderRadius: BorderRadius.circular(9),
border: Border.all(color: Colors.grey),
boxShadow: [
@ -52,22 +50,21 @@ class _CardState extends State<MyCard> {
Column(
children: [
GestureDetector(
onTap: widget.onTap,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: SizedBox(
height: 240,
width: 300,
child: this.widget.cardData.bytes != null?
Image.memory(base64Decode(this.widget.cardData.bytes!)) :
Image.network(
this.widget.cardData.imageUrl ?? '',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const Placeholder(),
),
),
)
)
onTap: onTap,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: SizedBox(
height: 240,
width: 300,
child: this.cardData.bytes != null
? Image.memory(base64Decode(this.cardData.bytes!))
: Image.network(
this.cardData.imageUrl ?? '',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const Placeholder(),
),
),
))
],
),
]),
@ -75,16 +72,12 @@ class _CardState extends State<MyCard> {
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 16.0, top: 12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(this.widget.cardData.text,
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: 30)),
]),
)),
padding: const EdgeInsets.only(left: 16.0, top: 12.0),
child:
Text(this.cardData.text,
style: TextStyle(decoration: TextDecoration.underline, fontSize: 30)),
),
),
],
),
Row(children: [
@ -92,7 +85,7 @@ class _CardState extends State<MyCard> {
child: Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Text(
this.widget.cardData.descriptionText,
this.cardData.descriptionText,
style: Theme.of(context).textTheme.bodyLarge,
)),
),
@ -109,19 +102,24 @@ class _CardState extends State<MyCard> {
duration: const Duration(milliseconds: 500),
turns: turns,
child: GestureDetector(
onTap: () {
setState(() {
isliked = !isliked;
onTap: () {
turns += 1.0;
});
widget.onlike?.call(widget.cardData.text, isliked);
onlike?.call(this.cardData.id, cardData.text, isliked);
},
child: Icon(
isliked ? Icons.favorite : Icons.favorite_border,
color: Colors.red,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: isliked
? const Icon(
Icons.favorite,
color: Colors.redAccent,
key: ValueKey<int>(0),
)
: const Icon(
Icons.favorite_border,
key: ValueKey<int>(1),
)),
))
])
)])
],
),
));

View File

@ -0,0 +1,26 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pmu_new/presentation/home_page/bloc/state.dart';
import '../../../data/repositories/PlacesRepository.dart';
import 'events.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> {
final PlacesRepository repo;
String? error;
HomeBloc(this.repo) : super(const HomeState()) {
on<HomeLoadDataEvent>(_onLoadData);
}
Future<void> _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
emit(state.copyWith(isLoading: true));
final data = await repo.loadData(q: event.search, onError: (e) => error = e,);
emit(state.copyWith(
isLoading: false,
data: data,
error: error,
));
}
}

View File

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

View File

@ -0,0 +1,25 @@
import 'package:equatable/equatable.dart';
import 'package:copy_with_extension/copy_with_extension.dart';
import '../../../models/CardData.dart';
part 'state.g.dart';
@CopyWith()
class HomeState extends Equatable {
final List<CardData>? data;
final bool isLoading;
final String? error;
const HomeState({
this.data,
this.isLoading = false,
this.error,
});
@override
List<Object?> get props => [
data,
isLoading,
error,
];
}

View File

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

View File

@ -1,11 +1,23 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:pmu_new/data/repositories/PlacesRepository.dart';
import 'package:pmu_new/components/extensions/context_x.dart';
import 'package:pmu_new/presentation/home_page/MyCard.dart';
import 'package:pmu_new/models/CardData.dart';
import '../../data/repositories/MockRepository.dart';
import 'package:pmu_new/presentation/svg_object.dart';
import '../details_page/details_page.dart';
import '../locale_bloc/locale_bloc.dart';
import '../locale_bloc/locale_event.dart';
import '../locale_bloc/locale_state.dart';
import 'bloc/bloc.dart';
import 'bloc/events.dart';
import 'bloc/state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../components/utils/debounce.dart';
import 'dart:math' as math;
import 'like_bloc/like_bloc.dart';
import 'like_bloc/like_event.dart';
import 'like_bloc/like_state.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
@ -17,35 +29,49 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme
.of(context)
.colorScheme
.inversePrimary,
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: const CardWidget());
}
}
class CardWidget extends StatelessWidget {
class CardWidget extends StatefulWidget {
const CardWidget({super.key});
@override
State<CardWidget> createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
final searchController = TextEditingController();
@override
void initState() {
SvgObjects.init();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<HomeBloc>().add(const HomeLoadDataEvent());
context.read<LikeBloc>().add(const LoadLikesEvent());
});
super.initState();
}
@override
void dispose() {
searchController.dispose();
super.dispose();
}
void _showSnackBar(BuildContext context, String title, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'$title ${isLiked ? 'Понравилось!' : 'Больше не нравится'}',
style: Theme
.of(context)
.textTheme
.bodyLarge,
'$title ${isLiked ? context.locale.liked : context.locale.disliked}',
style: Theme.of(context).textTheme.bodyLarge,
),
backgroundColor: Colors.greenAccent,
duration: const Duration(seconds: 1),
@ -53,34 +79,97 @@ class CardWidget extends StatelessWidget {
});
}
@override
Widget build(BuildContext context) {
final data = PlacesRepository().loadData(q: 1780);
return Center(
child: FutureBuilder<List<CardData>?>(
future: data,
builder: (context, snapshot) =>
SingleChildScrollView(
child: snapshot.hasData ?
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: snapshot.data?.map((e) {
return MyCard(
cardData: e,
onlike: (title, state) => _showSnackBar(context, title, state),
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => DetailsPage(e)),
);
}
);
}).toList() ?? [],
)
: const CircularProgressIndicator()
)
),
void _navToDetails(BuildContext context, CardData data) {
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => DetailsPage(data)),
);
}
}
Future<void> _onRefresh() {
context
.read<HomeBloc>()
.add(HomeLoadDataEvent(search: int.tryParse(searchController.text)));
return Future.value(null);
}
void _onLike(String? id, String title, bool isLiked) {
if (id != null) {
context.read<LikeBloc>().add(ChangeLikeEvent(id));
_showSnackBar(context, title, !isLiked);
}
}
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
children: [
Row(
children: [
Expanded(
flex: 4,
child: Padding(
padding: const EdgeInsets.all(12),
child: CupertinoSearchTextField(
controller: searchController,
placeholder: context.locale.search,
onChanged: (search) {
Debounce.run(() => context.read<HomeBloc>().add(
HomeLoadDataEvent(search: int.tryParse(search))));
},
),
),
),
GestureDetector(
onTap: () =>
context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
child: SizedBox.square(
dimension: 50,
child: Padding(
padding: const EdgeInsets.only(right: 12),
child: BlocBuilder<LocaleBloc, LocaleState>(
builder: (context, state) {
return state.currentLocale.languageCode == 'ru'
? const SvgRu()
: const SvgUk();
},
),
),
),
),
],
),
BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.isLoading
? const CircularProgressIndicator()
: BlocBuilder<LikeBloc, LikeState>(
builder: (context, likeState) {
return Expanded(
child: RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: state.data?.length ?? 0,
itemBuilder: (context, index) {
final data = state.data?[index];
return data != null
? MyCard(
cardData: data,
onlike: _onLike,
//id: data.id,
isliked: likeState.likedIds?.contains(data.id) == true,
onTap: () => _navToDetails(context, data),
)
: const SizedBox.shrink();
},
),
),
);
},))
],
),
));
}
}

View File

@ -0,0 +1,36 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'like_event.dart';
import 'like_state.dart';
const String _likedPrefsKey = 'liked';
class LikeBloc extends Bloc<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,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pmu_new/components/locale/l10n/app_locale.dart';
import 'locale_event.dart';
import 'locale_state.dart';
class LocaleBloc extends Bloc<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,14 @@
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
part 'locale_state.g.dart';
@CopyWith()
class LocaleState extends Equatable {
final Locale currentLocale;
const LocaleState({required this.currentLocale});
@override
List<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);
}

View File

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

@ -22,6 +22,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.7.0"
archive:
dependency: transitive
description:
name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
url: "https://pub.dev"
source: hosted
version: "3.6.1"
args:
dependency: transitive
description:
@ -38,6 +46,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
bloc:
dependency: transitive
description:
name: bloc
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
url: "https://pub.dev"
source: hosted
version: "8.1.4"
boolean_selector:
dependency: transitive
description:
@ -126,6 +142,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
url: "https://pub.dev"
source: hosted
version: "0.4.2"
clock:
dependency: transitive
description:
@ -158,6 +182,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
copy_with_extension:
dependency: transitive
description:
name: copy_with_extension
sha256: fbcf890b0c34aedf0894f91a11a579994b61b4e04080204656b582708b5b1125
url: "https://pub.dev"
source: hosted
version: "5.0.4"
copy_with_extension_gen:
dependency: "direct dev"
description:
name: copy_with_extension_gen
sha256: "51cd11094096d40824c8da629ca7f16f3b7cea5fc44132b679617483d43346b0"
url: "https://pub.dev"
source: hosted
version: "5.0.4"
crypto:
dependency: transitive
description:
@ -198,6 +238,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
equatable:
dependency: "direct dev"
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async:
dependency: transitive
description:
@ -206,6 +254,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
file:
dependency: transitive
description:
@ -227,6 +283,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_bloc:
dependency: "direct dev"
description:
name: flutter_bloc
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
url: "https://pub.dev"
source: hosted
version: "8.1.6"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
@ -235,11 +307,29 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
flutter_localizations:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_svg:
dependency: "direct dev"
description:
name: flutter_svg
sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123"
url: "https://pub.dev"
source: hosted
version: "2.0.16"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
@ -264,6 +354,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
http:
dependency: transitive
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.2"
http_multi_server:
dependency: transitive
description:
@ -280,6 +378,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
url: "https://pub.dev"
source: hosted
version: "4.3.0"
intl:
dependency: "direct dev"
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
io:
dependency: transitive
description:
@ -392,6 +506,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
package_config:
dependency: transitive
description:
@ -408,6 +530,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.0"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.2"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pool:
dependency: transitive
description:
@ -424,6 +602,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
provider:
dependency: transitive
description:
name: provider
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
url: "https://pub.dev"
source: hosted
version: "6.1.2"
pub_semver:
dependency: transitive
description:
@ -440,6 +626,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
shared_preferences:
dependency: "direct dev"
description:
name: shared_preferences
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
url: "https://pub.dev"
source: hosted
version: "2.2.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549"
url: "https://pub.dev"
source: hosted
version: "2.3.4"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shelf:
dependency: transitive
description:
@ -549,6 +791,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7"
url: "https://pub.dev"
source: hosted
version: "1.1.15"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb"
url: "https://pub.dev"
source: hosted
version: "1.1.12"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad"
url: "https://pub.dev"
source: hosted
version: "1.1.16"
vector_math:
dependency: transitive
description:
@ -597,6 +863,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.5.0"
yaml:
dependency: transitive
description:
@ -607,4 +889,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.5.4 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
flutter: ">=3.24.0"

View File

@ -40,33 +40,46 @@ dependencies:
json_annotation: ^4.8.1
dio: ^5.4.2+1
pretty_dio_logger: ^1.3.1
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.9
json_serializable: ^6.7.1
equatable: ^2.0.5
flutter_bloc: ^8.1.5
flutter_launcher_icons: 0.13.1
flutter_svg: ^2.0.7
copy_with_extension_gen: ^5.0.4
shared_preferences: 2.2.3
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^4.0.0
flutter_localizations:
sdk: flutter
intl: ^0.19.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
flutter_icons:
android: "ic_launcher"
ios: false
image_path: "assets/icon.png"
min_sdk_android: 17
# The following section is specific to Flutter packages.
flutter:
generate: true
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
assets:
- assets/svg/
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see