Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c9ef6476c | |||
| 2d49ce9f8c | |||
| f492991a36 | |||
| 78350b99e4 | |||
| 1c46c1c0fb | |||
| 76dcbb954b | |||
| 0e8ce38c33 | |||
| 7cede4272f | |||
| 465d7dce9c | |||
| c244105079 |
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 8.7 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
Normal file
|
After Width: | Height: | Size: 857 B |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 4.4 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
Normal file
|
After Width: | Height: | Size: 463 B |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 14 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 92 KiB |
BIN
assets/launcher.png
Normal file
|
After Width: | Height: | Size: 202 KiB |
5
assets/svg/ru.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ru" viewBox="0 0 640 480">
|
||||
<path fill="#fff" d="M0 0h640v160H0z"/>
|
||||
<path fill="#0039a6" d="M0 160h640v160H0z"/>
|
||||
<path fill="#d52b1e" d="M0 320h640v160H0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 225 B |
9
assets/svg/us.svg
Normal 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
@@ -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
|
||||
13
l10n/app_en.arb
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
|
||||
"title": "Quotes",
|
||||
"search_word": "Search by word",
|
||||
"search_author": "Search by author",
|
||||
"liked": "Like :)",
|
||||
"disliked": "Dislike :(",
|
||||
"error": "Error :C",
|
||||
"details": "Details of quote",
|
||||
|
||||
"arbEnding": ""
|
||||
}
|
||||
13
l10n/app_ru.arb
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"@@locale": "ru",
|
||||
|
||||
"title": "Цитаты",
|
||||
"search_word": "Поиск по слову",
|
||||
"search_author": "Поиск по автору",
|
||||
"liked": "Лайк :)",
|
||||
"disliked": "Дизлайк :(",
|
||||
"error": "Ошибка :C",
|
||||
"details": "Детали цитаты",
|
||||
|
||||
"arbEnding": ""
|
||||
}
|
||||
6
lib/components/extensions/context_x.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import '../locale/l10n/app_locale.dart';
|
||||
|
||||
extension LocalContextX on BuildContext {
|
||||
AppLocale get locale => AppLocale.of(this)!;
|
||||
}
|
||||
177
lib/components/locale/l10n/app_locale.dart
Normal file
@@ -0,0 +1,177 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
import 'app_locale_en.dart';
|
||||
import 'app_locale_ru.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// Callers can lookup localized strings with an instance of AppLocale
|
||||
/// returned by `AppLocale.of(context)`.
|
||||
///
|
||||
/// Applications need to include `AppLocale.delegate()` in their app's
|
||||
/// `localizationDelegates` list, and the locales they support in the app's
|
||||
/// `supportedLocales` list. For example:
|
||||
///
|
||||
/// ```dart
|
||||
/// import 'l10n/app_locale.dart';
|
||||
///
|
||||
/// return MaterialApp(
|
||||
/// localizationsDelegates: AppLocale.localizationsDelegates,
|
||||
/// supportedLocales: AppLocale.supportedLocales,
|
||||
/// home: MyApplicationHome(),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ## Update pubspec.yaml
|
||||
///
|
||||
/// Please make sure to update your pubspec.yaml to include the following
|
||||
/// packages:
|
||||
///
|
||||
/// ```yaml
|
||||
/// dependencies:
|
||||
/// # Internationalization support.
|
||||
/// flutter_localizations:
|
||||
/// sdk: flutter
|
||||
/// intl: any # Use the pinned version from flutter_localizations
|
||||
///
|
||||
/// # Rest of dependencies
|
||||
/// ```
|
||||
///
|
||||
/// ## iOS Applications
|
||||
///
|
||||
/// iOS applications define key application metadata, including supported
|
||||
/// locales, in an Info.plist file that is built into the application bundle.
|
||||
/// To configure the locales supported by your app, you’ll need to edit this
|
||||
/// file.
|
||||
///
|
||||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
||||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
||||
/// project’s Runner folder.
|
||||
///
|
||||
/// Next, select the Information Property List item, select Add Item from the
|
||||
/// Editor menu, then select Localizations from the pop-up menu.
|
||||
///
|
||||
/// Select and expand the newly-created Localizations item then, for each
|
||||
/// locale your application supports, add a new item and select the locale
|
||||
/// you wish to add from the pop-up menu in the Value field. This list should
|
||||
/// be consistent with the languages listed in the AppLocale.supportedLocales
|
||||
/// property.
|
||||
abstract class AppLocale {
|
||||
AppLocale(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||||
|
||||
final String localeName;
|
||||
|
||||
static AppLocale? of(BuildContext context) {
|
||||
return Localizations.of<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 @title.
|
||||
///
|
||||
/// In ru, this message translates to:
|
||||
/// **'Цитаты'**
|
||||
String get title;
|
||||
|
||||
/// No description provided for @search_word.
|
||||
///
|
||||
/// In ru, this message translates to:
|
||||
/// **'Поиск по слову'**
|
||||
String get search_word;
|
||||
|
||||
/// No description provided for @search_author.
|
||||
///
|
||||
/// In ru, this message translates to:
|
||||
/// **'Поиск по автору'**
|
||||
String get search_author;
|
||||
|
||||
/// 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 @error.
|
||||
///
|
||||
/// In ru, this message translates to:
|
||||
/// **'Ошибка :C'**
|
||||
String get error;
|
||||
|
||||
/// No description provided for @details.
|
||||
///
|
||||
/// In ru, this message translates to:
|
||||
/// **'Детали цитаты'**
|
||||
String get details;
|
||||
|
||||
/// 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.'
|
||||
);
|
||||
}
|
||||
32
lib/components/locale/l10n/app_locale_en.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
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 title => 'Quotes';
|
||||
|
||||
@override
|
||||
String get search_word => 'Search by word';
|
||||
|
||||
@override
|
||||
String get search_author => 'Search by author';
|
||||
|
||||
@override
|
||||
String get liked => 'Like :)';
|
||||
|
||||
@override
|
||||
String get disliked => 'Dislike :(';
|
||||
|
||||
@override
|
||||
String get error => 'Error :C';
|
||||
|
||||
@override
|
||||
String get details => 'Details of quote';
|
||||
|
||||
@override
|
||||
String get arbEnding => '';
|
||||
}
|
||||
32
lib/components/locale/l10n/app_locale_ru.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
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 title => 'Цитаты';
|
||||
|
||||
@override
|
||||
String get search_word => 'Поиск по слову';
|
||||
|
||||
@override
|
||||
String get search_author => 'Поиск по автору';
|
||||
|
||||
@override
|
||||
String get liked => 'Лайк :)';
|
||||
|
||||
@override
|
||||
String get disliked => 'Дизлайк :(';
|
||||
|
||||
@override
|
||||
String get error => 'Ошибка :C';
|
||||
|
||||
@override
|
||||
String get details => 'Детали цитаты';
|
||||
|
||||
@override
|
||||
String get arbEnding => '';
|
||||
}
|
||||
10
lib/components/resources.g.dart
Normal 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';
|
||||
}
|
||||
20
lib/components/utils/debounce.dart
Normal 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: 1000),
|
||||
}) {
|
||||
_timer?.cancel();
|
||||
_timer = Timer(delay, action);
|
||||
}
|
||||
}
|
||||
25
lib/data/dtos/quotes_dto.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'quotes_dto.g.dart';
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class QuotesDto {
|
||||
final List<QuoteDataDto>? quotes;
|
||||
|
||||
const QuotesDto({this.quotes});
|
||||
|
||||
factory QuotesDto.fromJson(Map<String, dynamic> json) => _$QuotesDtoFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class QuoteDataDto {
|
||||
final String? body;
|
||||
final String? author;
|
||||
final String? imageUrl;
|
||||
final dynamic id;
|
||||
final bool? isLiked;
|
||||
|
||||
const QuoteDataDto({this.body, this.author, this.imageUrl, this.id, this.isLiked});
|
||||
|
||||
factory QuoteDataDto.fromJson(Map<String, dynamic> json) => _$QuoteDataDtoFromJson(json);
|
||||
}
|
||||
21
lib/data/dtos/quotes_dto.g.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'quotes_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
QuotesDto _$QuotesDtoFromJson(Map<String, dynamic> json) => QuotesDto(
|
||||
quotes: (json['quotes'] as List<dynamic>?)
|
||||
?.map((e) => QuoteDataDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
QuoteDataDto _$QuoteDataDtoFromJson(Map<String, dynamic> json) => QuoteDataDto(
|
||||
body: json['body'] as String?,
|
||||
author: json['author'] as String?,
|
||||
imageUrl: json['imageUrl'] as String?,
|
||||
id: json['id'],
|
||||
isLiked: json['isLiked'] as bool?,
|
||||
);
|
||||
15
lib/data/mappers/quotes_mapper.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import '../../domain/quote.dart';
|
||||
import '/data/dtos/quotes_dto.dart';
|
||||
import '/presentation/home_page/home_page.dart';
|
||||
|
||||
const _imagePlaceholder = 'https://cdn-icons-png.flaticon.com/128/17818/17818874.png';
|
||||
|
||||
extension QuoteDtoToModel on QuoteDataDto {
|
||||
Quote toDomain() => Quote(
|
||||
body ?? 'Без текста',
|
||||
author ?? 'Неизвестный автор',
|
||||
imageUrl ?? _imagePlaceholder,
|
||||
id?.toString() ?? "",
|
||||
isLiked ?? false,
|
||||
);
|
||||
}
|
||||
8
lib/data/repositories/api_interface.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
import '../../domain/quote.dart';
|
||||
import '/presentation/home_page/home_page.dart';
|
||||
|
||||
typedef OnErrorCallback = void Function(String? error);
|
||||
|
||||
abstract class ApiInterface {
|
||||
Future<List<Quote>?> loadData({OnErrorCallback? onError});
|
||||
}
|
||||
51
lib/data/repositories/quotes_repository.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import '../../domain/quote.dart';
|
||||
import '/data/dtos/quotes_dto.dart';
|
||||
import '/data/mappers/quotes_mapper.dart';
|
||||
import '/data/repositories/api_interface.dart';
|
||||
import '/presentation/home_page/home_page.dart';
|
||||
import '/presentation/dialogs/show_dialog.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
|
||||
class QuotesRepository extends ApiInterface {
|
||||
static final Dio _dio = Dio()
|
||||
..interceptors.add(PrettyDioLogger(
|
||||
requestHeader: true,
|
||||
requestBody: true,
|
||||
))
|
||||
..options.headers = {
|
||||
'Authorization': 'Bearer 82ccaa45c8344de4ac008b2487d33361',
|
||||
};
|
||||
|
||||
static const String _baseUrl = 'https://favqs.com/api';
|
||||
|
||||
@override
|
||||
Future<List<Quote>?> loadData({String? q, String? author, OnErrorCallback? onError}) async {
|
||||
try {
|
||||
final Map<String, dynamic> queryParams = {};
|
||||
|
||||
if (q != null && q.isNotEmpty) {
|
||||
queryParams['filter'] = q;
|
||||
}
|
||||
|
||||
if (author != null && author.isNotEmpty) {
|
||||
queryParams['type'] = 'author';
|
||||
queryParams['filter'] = author;
|
||||
}
|
||||
|
||||
const String url = '$_baseUrl/quotes';
|
||||
|
||||
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
|
||||
url,
|
||||
queryParameters: queryParams.isNotEmpty ? queryParams : null,
|
||||
);
|
||||
|
||||
final QuotesDto dto = QuotesDto.fromJson(response.data as Map<String, dynamic>);
|
||||
final List<Quote>? data = dto.quotes?.map((e) => e.toDomain()).toList();
|
||||
return data;
|
||||
} on DioException catch (e) {
|
||||
onError?.call(e.response?.statusMessage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
lib/domain/quote.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
class Quote {
|
||||
final String text;
|
||||
final String author;
|
||||
final String imagePath;
|
||||
final String id;
|
||||
final bool isLiked;
|
||||
Quote(this.text, this.author, this.imagePath, this.id, this.isLiked);
|
||||
}
|
||||
233
lib/main.dart
@@ -1,6 +1,15 @@
|
||||
// ignore_for_file: unnecessary_string_escapes
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:pmu_flutter_labs/presentation/home_page/bloc/events.dart';
|
||||
import 'package:pmu_flutter_labs/presentation/like_bloc/like_bloc.dart';
|
||||
import 'package:pmu_flutter_labs/presentation/locale_bloc/locale_bloc.dart';
|
||||
import 'package:pmu_flutter_labs/presentation/locale_bloc/locale_state.dart';
|
||||
import 'components/locale/l10n/app_locale.dart';
|
||||
import 'data/repositories/quotes_repository.dart';
|
||||
import '/presentation/home_page/bloc/bloc.dart';
|
||||
import '/presentation/home_page/home_page.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
@@ -11,202 +20,34 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Цитаты',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const MyHomePage(title: 'Цитаты'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Enum для управления состоянием сообщений
|
||||
enum MessageType { noQuotes, newQuoteAdded }
|
||||
|
||||
// Модель цитаты
|
||||
class Quote {
|
||||
final String text;
|
||||
final String author;
|
||||
Quote(this.text, this.author);
|
||||
}
|
||||
|
||||
// Главный экран
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
final List<Quote> _quotes = [];
|
||||
MessageType _messageType = MessageType.noQuotes;
|
||||
|
||||
// Метод добавления цитаты
|
||||
Future<void> _addQuote() async {
|
||||
final result = await showDialog<Map<String, String>?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => const AddQuoteDialog(),
|
||||
);
|
||||
|
||||
if (result != null && result['quote'] != '' && result['author'] != '') {
|
||||
setState(() {
|
||||
_quotes.add(
|
||||
Quote(result['quote']!.addQuotesIfMissing(), result['author']!.capitalize()),
|
||||
return BlocProvider<LocaleBloc>(
|
||||
lazy: false,
|
||||
create: (context) => LocaleBloc(Locale(Platform.localeName)),
|
||||
child: BlocBuilder<LocaleBloc, LocaleState>(builder: (context, state) {
|
||||
return MaterialApp(
|
||||
title: 'Цитаты',
|
||||
locale: state.currentLocale,
|
||||
localizationsDelegates: AppLocale.localizationsDelegates,
|
||||
supportedLocales: AppLocale.supportedLocales,
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: RepositoryProvider<QuotesRepository>(
|
||||
lazy: true,
|
||||
create: (_) => QuotesRepository(),
|
||||
child: BlocProvider<LikeBloc>(
|
||||
lazy: false,
|
||||
create: (context) => LikeBloc(), // Add LikeBloc here
|
||||
child: BlocProvider<HomeBloc>(
|
||||
lazy: false,
|
||||
create: (context) => HomeBloc(context.read<QuotesRepository>()),
|
||||
child: const MyHomePage(title: '',),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
_messageType = MessageType.newQuoteAdded;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: _messageType == MessageType.noQuotes
|
||||
? const NoQuotesMessage()
|
||||
: QuoteList(quotes: _quotes),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: QuoteCounter(count: _quotes.length),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _addQuote,
|
||||
tooltip: 'Добавить цитату',
|
||||
child: const Icon(Icons.format_quote),
|
||||
),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Виджет списка цитат
|
||||
class QuoteList extends StatelessWidget {
|
||||
final List<Quote> quotes;
|
||||
const QuoteList({super.key, required this.quotes});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
itemCount: quotes.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
title: Text(
|
||||
quotes[index].text,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text('- ${quotes[index].author}'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Виджет сообщения "нет цитат"
|
||||
class NoQuotesMessage extends StatelessWidget {
|
||||
const NoQuotesMessage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Text(
|
||||
'Нет добавленных цитат. Добавь первую!',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 18, color: Colors.grey),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Виджет счетчика цитат
|
||||
class QuoteCounter extends StatelessWidget {
|
||||
final int count;
|
||||
const QuoteCounter({super.key, required this.count});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(30.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Количество цитат: $count',
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Диалоговое окно добавления цитаты
|
||||
class AddQuoteDialog extends StatelessWidget {
|
||||
const AddQuoteDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TextEditingController quoteController = TextEditingController();
|
||||
final TextEditingController authorController = TextEditingController();
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text('Добавить цитату'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: quoteController,
|
||||
decoration: const InputDecoration(labelText: 'Цитата'),
|
||||
),
|
||||
TextField(
|
||||
controller: authorController,
|
||||
decoration: const InputDecoration(labelText: 'Автор'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop({
|
||||
'quote': quoteController.text,
|
||||
'author': authorController.text,
|
||||
}),
|
||||
child: const Text('Добавить'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Расширение для форматирования текста
|
||||
extension StringExtension on String {
|
||||
String capitalize() {
|
||||
return split(' ').map((word) {
|
||||
if (word.isNotEmpty) {
|
||||
return '${word[0].toUpperCase()}${word.substring(1).toLowerCase()}';
|
||||
}
|
||||
return word;
|
||||
}).join(' ');
|
||||
}
|
||||
|
||||
String addQuotesIfMissing() {
|
||||
if (startsWith('\"') && endsWith('\"')) return this;
|
||||
if (startsWith('\"') && !endsWith('\"')) return '$this\"';
|
||||
if (endsWith('\"') && !startsWith('\"')) return '\"$this';
|
||||
return '\"$this\"';
|
||||
}
|
||||
}
|
||||
|
||||
34
lib/presentation/common/svg_objects.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import '/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);
|
||||
}
|
||||
}
|
||||
33
lib/presentation/dialogs/error_dialog.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ErrorDialog extends StatelessWidget {
|
||||
final String? error;
|
||||
|
||||
const ErrorDialog(this.error, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(36),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.grey, borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.error, color: Colors.white),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
error ?? 'UNKNOWN',
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
12
lib/presentation/dialogs/show_dialog.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'error_dialog.dart';
|
||||
|
||||
void showErrorDialog(
|
||||
BuildContext context, {
|
||||
required String error,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => ErrorDialog(error),
|
||||
);
|
||||
}
|
||||
48
lib/presentation/home_page/bloc/bloc.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../data/repositories/quotes_repository.dart';
|
||||
import '/presentation/home_page/bloc/events.dart';
|
||||
import '/presentation/home_page/bloc/state.dart';
|
||||
|
||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
final QuotesRepository repo;
|
||||
|
||||
HomeBloc(this.repo) : super(const HomeState()) {
|
||||
on<HomeLoadDataEvent>(_onLoadData);
|
||||
on<HomeRefreshEvent>(_onRefreshData);
|
||||
}
|
||||
|
||||
void _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
|
||||
final author = event.author?.trim() ?? '';
|
||||
final search = event.search?.trim() ?? '';
|
||||
|
||||
// Если поле автора не заполнено, загружаем данные из репозитория
|
||||
if (author.isEmpty) {
|
||||
emit(state.copyWith(
|
||||
data: repo.loadData(q: search).then((result) => result ?? []), // Обработка null
|
||||
));
|
||||
} else {
|
||||
// Если автор указан, фильтруем данные по тексту цитаты
|
||||
try {
|
||||
final currentData = await repo.loadData(
|
||||
q: search,
|
||||
author: author,
|
||||
);
|
||||
final filteredData = currentData?.where((quote) {
|
||||
final matchesAuthor = quote.author.toLowerCase().contains(author.toLowerCase());
|
||||
final matchesSearch =
|
||||
search.isEmpty || quote.text.toLowerCase().contains(search.toLowerCase());
|
||||
return matchesAuthor && matchesSearch;
|
||||
}).toList();
|
||||
emit(state.copyWith(data: Future.value(filteredData)));
|
||||
} catch (error) {
|
||||
emit(state.copyWith(data: Future.error(error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onRefreshData(HomeRefreshEvent event, Emitter<HomeState> emit) async {
|
||||
add(HomeLoadDataEvent(
|
||||
search: event.search, author: event.author)); // Просто перезапускаем загрузку
|
||||
}
|
||||
}
|
||||
17
lib/presentation/home_page/bloc/events.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
abstract class HomeEvent {
|
||||
const HomeEvent();
|
||||
}
|
||||
|
||||
class HomeLoadDataEvent extends HomeEvent {
|
||||
final String? search;
|
||||
final String? author;
|
||||
|
||||
const HomeLoadDataEvent({this.search, this.author});
|
||||
}
|
||||
|
||||
class HomeRefreshEvent extends HomeEvent {
|
||||
final String? search;
|
||||
final String? author;
|
||||
|
||||
const HomeRefreshEvent({this.search, this.author});
|
||||
}
|
||||
14
lib/presentation/home_page/bloc/state.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import '../../../domain/quote.dart';
|
||||
import '../home_page.dart';
|
||||
|
||||
class HomeState extends Equatable {
|
||||
final Future<List<Quote>>? data;
|
||||
|
||||
const HomeState({this.data});
|
||||
|
||||
HomeState copyWith({Future<List<Quote>>? data}) => HomeState(data: data ?? this.data);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [data];
|
||||
}
|
||||
99
lib/presentation/home_page/card.dart
Normal file
@@ -0,0 +1,99 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pmu_flutter_labs/components/extensions/context_x.dart';
|
||||
import '../../domain/quote.dart';
|
||||
import 'home_page.dart';
|
||||
|
||||
class QuoteCard extends StatelessWidget {
|
||||
final Quote quote;
|
||||
final VoidCallback onFavoriteToggle;
|
||||
final bool isLiked;
|
||||
|
||||
const QuoteCard({
|
||||
super.key,
|
||||
required this.quote,
|
||||
required this.onFavoriteToggle,
|
||||
required this.isLiked,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 8.0,
|
||||
horizontal: 10.0,
|
||||
),
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.all(8.0),
|
||||
leading: SizedBox(
|
||||
width: 50.0,
|
||||
child: Image.network(
|
||||
quote.imagePath,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => const Icon(Icons.error, color: Colors.red),
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
quote.text,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
|
||||
),
|
||||
subtitle: Text('- ${quote.author}'),
|
||||
trailing: IconButton(
|
||||
icon: Icon(
|
||||
isLiked ? Icons.favorite : Icons.favorite_border,
|
||||
color: isLiked ? Colors.red : null,
|
||||
),
|
||||
onPressed: onFavoriteToggle,
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => QuoteDetailScreen(quote: quote),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class QuoteDetailScreen extends StatelessWidget {
|
||||
final Quote quote;
|
||||
|
||||
const QuoteDetailScreen({super.key, required this.quote});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(context.locale.details),
|
||||
),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.network(
|
||||
quote.imagePath,
|
||||
height: 150,
|
||||
errorBuilder: (_, __, ___) => const Icon(Icons.error, color: Colors.red),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
quote.text,
|
||||
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'- ${quote.author}',
|
||||
style: const TextStyle(fontSize: 18, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
201
lib/presentation/home_page/home_page.dart
Normal file
@@ -0,0 +1,201 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:pmu_flutter_labs/components/extensions/context_x.dart';
|
||||
import 'package:pmu_flutter_labs/presentation/like_bloc/like_state.dart';
|
||||
import '../../components/utils/debounce.dart';
|
||||
import '../../domain/quote.dart';
|
||||
import '../common/svg_objects.dart';
|
||||
import '../like_bloc/like_bloc.dart';
|
||||
import '../like_bloc/like_event.dart';
|
||||
import '../locale_bloc/locale_bloc.dart';
|
||||
import '../locale_bloc/locale_events.dart';
|
||||
import '../locale_bloc/locale_state.dart';
|
||||
import '/data/repositories/quotes_repository.dart';
|
||||
import '/presentation/home_page/bloc/bloc.dart';
|
||||
import '/presentation/home_page/bloc/events.dart';
|
||||
import '/presentation/home_page/bloc/state.dart';
|
||||
import 'card.dart';
|
||||
|
||||
class MyHomePage extends StatelessWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
get searchController => null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => HomeBloc(QuotesRepository())..add(const HomeLoadDataEvent()),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(context.locale.title),
|
||||
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();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
const Expanded(
|
||||
child: _HomePageBody(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HomePageBody extends StatefulWidget {
|
||||
const _HomePageBody();
|
||||
|
||||
@override
|
||||
State<_HomePageBody> createState() => _HomePageBodyState();
|
||||
}
|
||||
|
||||
class _HomePageBodyState extends State<_HomePageBody> {
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
final TextEditingController authorController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
SvgObjects.init();
|
||||
super.initState();
|
||||
|
||||
searchController.addListener(() {
|
||||
Debounce.run(() {
|
||||
context.read<HomeBloc>().add(HomeLoadDataEvent(
|
||||
search: searchController.text,
|
||||
author: authorController.text,
|
||||
));
|
||||
});
|
||||
});
|
||||
|
||||
authorController.addListener(() {
|
||||
Debounce.run(() {
|
||||
context.read<HomeBloc>().add(HomeLoadDataEvent(
|
||||
search: searchController.text,
|
||||
author: authorController.text,
|
||||
));
|
||||
});
|
||||
});
|
||||
context.read<HomeBloc>().add(const HomeLoadDataEvent());
|
||||
context.read<LikeBloc>().add(const LoadLikesEvent());
|
||||
}
|
||||
|
||||
void _showSnackbar(BuildContext context, bool isLiked) {
|
||||
final message = isLiked ? context.locale.liked : context.locale.disliked;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<HomeBloc, HomeState>(
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: searchController,
|
||||
decoration: InputDecoration(
|
||||
labelText: context.locale.search_word,
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TextField(
|
||||
controller: authorController,
|
||||
decoration: InputDecoration(
|
||||
labelText: context.locale.search_author,
|
||||
prefixIcon: const Icon(Icons.person),
|
||||
),
|
||||
),
|
||||
),
|
||||
BlocBuilder<LikeBloc, LikeState>(
|
||||
builder: (context, likeState) {
|
||||
return Expanded(
|
||||
child: state.data == null
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: FutureBuilder<List<Quote>?>(
|
||||
future: state.data,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (snapshot.hasError) {
|
||||
return Center(child: Text('${context.locale.error}: ${snapshot.error}'));
|
||||
}
|
||||
if (snapshot.data == null || snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Text(
|
||||
context.locale.error,
|
||||
style: const TextStyle(fontSize: 18, color: Colors.grey),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final quotes = snapshot.data!;
|
||||
final likedIds = likeState.likedIds ?? [];
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
context.read<HomeBloc>().add(HomeRefreshEvent(
|
||||
search: searchController.text,
|
||||
author: authorController.text,
|
||||
));
|
||||
},
|
||||
child: ListView.builder(
|
||||
itemCount: quotes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final quote = quotes[index];
|
||||
final isLiked = likedIds.contains(quote.id);
|
||||
|
||||
return QuoteCard(
|
||||
quote: quote,
|
||||
isLiked: isLiked,
|
||||
onFavoriteToggle: () {
|
||||
context.read<LikeBloc>().add(ChangeLikeEvent(quote.id));
|
||||
_showSnackbar(context, !isLiked);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
36
lib/presentation/like_bloc/like_bloc.dart
Normal 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));
|
||||
}
|
||||
}
|
||||
12
lib/presentation/like_bloc/like_event.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
abstract class LikeEvent {
|
||||
const LikeEvent();
|
||||
}
|
||||
|
||||
class LoadLikesEvent extends LikeEvent {
|
||||
const LoadLikesEvent();
|
||||
}
|
||||
|
||||
class ChangeLikeEvent extends LikeEvent {
|
||||
final String id;
|
||||
const ChangeLikeEvent(this.id);
|
||||
}
|
||||
14
lib/presentation/like_bloc/like_state.dart
Normal 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];
|
||||
}
|
||||
56
lib/presentation/like_bloc/like_state.g.dart
Normal 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);
|
||||
}
|
||||
17
lib/presentation/locale_bloc/locale_bloc.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../components/locale/l10n/app_locale.dart';
|
||||
import 'locale_events.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));
|
||||
}
|
||||
}
|
||||
7
lib/presentation/locale_bloc/locale_events.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
abstract class LocaleEvent {
|
||||
const LocaleEvent();
|
||||
}
|
||||
|
||||
class ChangeLocaleEvent extends LocaleEvent {
|
||||
const ChangeLocaleEvent();
|
||||
}
|
||||
15
lib/presentation/locale_bloc/locale_state.dart
Normal 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];
|
||||
}
|
||||
56
lib/presentation/locale_bloc/locale_state.g.dart
Normal 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()
|
||||
? _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);
|
||||
}
|
||||
14
makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
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
|
||||
format:
|
||||
dart format . --line-length 100
|
||||
res:
|
||||
fgen --output lib/components/resources.g.dart --no-watch --no-preview;\
|
||||
dart format . --line-length 100
|
||||
loc:
|
||||
flutter gen-l10n
|
||||
dart format . --line-length 100
|
||||
697
pubspec.lock
@@ -1,6 +1,43 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "72.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.2"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.0"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "08064924cbf0ab88280a0c3f60db9dd24fec693927e725ecb176f16c629d1cb8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -9,6 +46,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -17,6 +62,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.13"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.2"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -25,6 +134,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -33,6 +158,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -41,6 +174,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
copy_with_extension:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: copy_with_extension
|
||||
sha256: ed472ae80d807094d7a7d7ef67901f8167d18c7998e6db81785a51364aede627
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
copy_with_extension_gen:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: copy_with_extension_gen
|
||||
sha256: "0be2694d3d50df16d91c04e7444181bfb2a0ad8a3b169d58de0c38a69864e4bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -49,6 +214,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.7"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.7.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -57,24 +254,178 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
sha256: "31cd0885738e87c72d6f055564d37fabcdacee743b396b78c7636c169cac64f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.2"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
|
||||
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "5.0.0"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_svg
|
||||
sha256: "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:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "20842a5ad1555be624c314b0c0cc0566e8ece412f61e859a42efeb6d4101a26c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.9.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -103,10 +454,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
|
||||
sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
version: "5.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2-main.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -131,6 +498,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -139,11 +530,203 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
path_parsing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_parsing
|
||||
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
posix:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: posix
|
||||
sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.1"
|
||||
pretty_dio_logger:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pretty_dio_logger
|
||||
sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.2"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "3c7e73920c694a436afaf65ab60ce3453d91f84208d761fbd83fc21182134d93"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.5"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -168,6 +751,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -192,6 +783,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics
|
||||
sha256: "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:
|
||||
@@ -208,6 +839,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.5"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.6"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.4 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
103
pubspec.yaml
@@ -1,90 +1,57 @@
|
||||
name: pmu_flutter_labs
|
||||
description: "A new Flutter project."
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
# followed by an optional build number separated by a +.
|
||||
# Both the version and the builder number may be overridden in flutter
|
||||
# build by specifying --build-name and --build-number, respectively.
|
||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
publish_to: 'none'
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.4
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||
# dependencies can be manually updated by changing the version numbers below to
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
shared_preferences: ^2.3.3
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
# Виджеты
|
||||
cupertino_icons: ^1.0.8
|
||||
flutter_svg: ^2.0.16
|
||||
|
||||
# Сетевое взаимодействие
|
||||
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: ^6.0.0
|
||||
|
||||
# Localization
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: ^0.19.0
|
||||
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^5.0.0
|
||||
# Иконки
|
||||
flutter_launcher_icons: ^0.14.2
|
||||
# Сетевое взаимодействие
|
||||
build_runner: ^2.4.9
|
||||
json_serializable: ^6.7.1
|
||||
|
||||
# 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_icons:
|
||||
android: "ic_launcher"
|
||||
image_path: "assets/launcher.png"
|
||||
min_sdk_android: 21
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
generate: true
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
assets:
|
||||
- assets/svg/
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/to/asset-from-package
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/to/font-from-package
|
||||
|
||||