Language
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
arb-dir: lib/l10n
|
||||
template-arb-file: app_en.arb
|
||||
output-localization-file: app_localizations.dart
|
||||
@@ -65,4 +65,12 @@ class HiveCategoryRepository implements ICategoryRepository {
|
||||
.where((c) => c.userId == userId && !c.isIncome)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addAll(List<Category> categories) async {
|
||||
final Map<String, Category> categoryMap = {
|
||||
for (var cat in categories) cat.id: cat
|
||||
};
|
||||
await _box.putAll(categoryMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,4 +40,12 @@ class HiveTagRepository implements ITagRepository {
|
||||
// у которых поле `userId` совпадает с идентификатором, переданным в метод.
|
||||
return _box.values.where((tag) => tag.userId == userId).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addAll(List<Tag> tags) async {
|
||||
final Map<String, Tag> tagMap = {
|
||||
for (var tag in tags) tag.id: tag
|
||||
};
|
||||
await _box.putAll(tagMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,4 +93,12 @@ class HiveTransactionRepository implements ITransactionRepository {
|
||||
.where((t) => t.userId == userId && t.tag?.id == tagId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addAll(List<TransactionRecord> transactions) async {
|
||||
final Map<String, TransactionRecord> transactionMap = {
|
||||
for (var tr in transactions) tr.id: tr
|
||||
};
|
||||
await _box.putAll(transactionMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,7 @@ abstract class ICategoryRepository {
|
||||
|
||||
/// Получить категории расходов конкретного пользователя
|
||||
Future<List<Category>> getExpenseCategoriesByUser(String userId);
|
||||
|
||||
/// Добавить список категорий
|
||||
Future<void> addAll(List<Category> categories);
|
||||
}
|
||||
|
||||
@@ -12,4 +12,7 @@ abstract class ITagRepository {
|
||||
// реализацию этого метода. Это гарантирует, что наш репозиторий
|
||||
// сможет получать теги для конкретного пользователя.
|
||||
Future<List<Tag>> getAllByUser(String userId);
|
||||
|
||||
/// Добавить список тегов
|
||||
Future<void> addAll(List<Tag> tags);
|
||||
}
|
||||
|
||||
@@ -17,4 +17,7 @@ abstract class ITransactionRepository {
|
||||
Future<List<TransactionRecord>> getByUserAndDateRange(String userId, DateTime from, DateTime to);
|
||||
Future<List<TransactionRecord>> getByUserAndCategory(String userId, String categoryId);
|
||||
Future<List<TransactionRecord>> getByUserAndTag(String userId, String tagId);
|
||||
|
||||
/// Добавить список транзакций
|
||||
Future<void> addAll(List<TransactionRecord> transactions);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ Future<void> initDependencies() async {
|
||||
);
|
||||
|
||||
// Services
|
||||
getIt.registerSingleton<UserService>(UserService(getIt()));
|
||||
getIt.registerSingleton<UserService>(UserService(getIt(), getIt(), getIt(), getIt()));
|
||||
|
||||
// Blocs
|
||||
getIt.registerFactory<AuthBloc>(() => AuthBloc(userService: getIt()));
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
"appTitle": "Budget App",
|
||||
"homePageTitle": "Home",
|
||||
"reportsPageTitle": "Reports",
|
||||
"settingsPageTitle": "Settings",
|
||||
"addTransactionButton": "Add new transaction",
|
||||
"noTransactionsText": "No transactions yet",
|
||||
"loadingTransactionsText": "Loading transactions...",
|
||||
"transactionErrorText": "Error: {message}",
|
||||
"loginPageTitle": "Login",
|
||||
"nameFieldLabel": "Name",
|
||||
"emailFieldLabel": "Email",
|
||||
"nameFieldEmptyError": "Please enter your name",
|
||||
"emailFieldEmptyError": "Please enter your email",
|
||||
"loginButtonText": "Login / Register",
|
||||
"darkModeSetting": "Dark Theme",
|
||||
"darkModeDescription": "Toggle between light and dark theme",
|
||||
"languageSetting": "Language",
|
||||
"languageDescription": "Change application language",
|
||||
"russianLanguage": "Russian",
|
||||
"englishLanguage": "English",
|
||||
"defaultUser": "Default User"
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
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_localizations_en.dart';
|
||||
import 'app_localizations_ru.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// Callers can lookup localized strings with an instance of AppLocalizations
|
||||
/// returned by `AppLocalizations.of(context)`.
|
||||
///
|
||||
/// Applications need to include `AppLocalizations.delegate()` in their app's
|
||||
/// `localizationDelegates` list, and the locales they support in the app's
|
||||
/// `supportedLocales` list. For example:
|
||||
///
|
||||
/// ```dart
|
||||
/// import 'l10n/app_localizations.dart';
|
||||
///
|
||||
/// return MaterialApp(
|
||||
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
/// supportedLocales: AppLocalizations.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 AppLocalizations.supportedLocales
|
||||
/// property.
|
||||
abstract class AppLocalizations {
|
||||
AppLocalizations(String locale)
|
||||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||||
|
||||
final String localeName;
|
||||
|
||||
static AppLocalizations? of(BuildContext context) {
|
||||
return Localizations.of<AppLocalizations>(context, AppLocalizations);
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||||
_AppLocalizationsDelegate();
|
||||
|
||||
/// 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 @appTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Budget App'**
|
||||
String get appTitle;
|
||||
|
||||
/// No description provided for @homePageTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Home'**
|
||||
String get homePageTitle;
|
||||
|
||||
/// No description provided for @reportsPageTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Reports'**
|
||||
String get reportsPageTitle;
|
||||
|
||||
/// No description provided for @settingsPageTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Settings'**
|
||||
String get settingsPageTitle;
|
||||
|
||||
/// No description provided for @addTransactionButton.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add new transaction'**
|
||||
String get addTransactionButton;
|
||||
|
||||
/// No description provided for @noTransactionsText.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No transactions yet'**
|
||||
String get noTransactionsText;
|
||||
|
||||
/// No description provided for @loadingTransactionsText.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Loading transactions...'**
|
||||
String get loadingTransactionsText;
|
||||
|
||||
/// No description provided for @transactionErrorText.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error: {message}'**
|
||||
String transactionErrorText(Object message);
|
||||
|
||||
/// No description provided for @loginPageTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Login'**
|
||||
String get loginPageTitle;
|
||||
|
||||
/// No description provided for @nameFieldLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Name'**
|
||||
String get nameFieldLabel;
|
||||
|
||||
/// No description provided for @emailFieldLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Email'**
|
||||
String get emailFieldLabel;
|
||||
|
||||
/// No description provided for @nameFieldEmptyError.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Please enter your name'**
|
||||
String get nameFieldEmptyError;
|
||||
|
||||
/// No description provided for @emailFieldEmptyError.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Please enter your email'**
|
||||
String get emailFieldEmptyError;
|
||||
|
||||
/// No description provided for @loginButtonText.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Login / Register'**
|
||||
String get loginButtonText;
|
||||
|
||||
/// No description provided for @darkModeSetting.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Dark Theme'**
|
||||
String get darkModeSetting;
|
||||
|
||||
/// No description provided for @darkModeDescription.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Toggle between light and dark theme'**
|
||||
String get darkModeDescription;
|
||||
|
||||
/// No description provided for @languageSetting.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Language'**
|
||||
String get languageSetting;
|
||||
|
||||
/// No description provided for @languageDescription.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Change application language'**
|
||||
String get languageDescription;
|
||||
|
||||
/// No description provided for @russianLanguage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Russian'**
|
||||
String get russianLanguage;
|
||||
|
||||
/// No description provided for @englishLanguage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'English'**
|
||||
String get englishLanguage;
|
||||
|
||||
/// No description provided for @defaultUser.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Default User'**
|
||||
String get defaultUser;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
extends LocalizationsDelegate<AppLocalizations> {
|
||||
const _AppLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
Future<AppLocalizations> load(Locale locale) {
|
||||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) =>
|
||||
<String>['en', 'ru'].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
AppLocalizations lookupAppLocalizations(Locale locale) {
|
||||
// Lookup logic when only language code is specified.
|
||||
switch (locale.languageCode) {
|
||||
case 'en':
|
||||
return AppLocalizationsEn();
|
||||
case 'ru':
|
||||
return AppLocalizationsRu();
|
||||
}
|
||||
|
||||
throw FlutterError(
|
||||
'AppLocalizations.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.',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for English (`en`).
|
||||
class AppLocalizationsEn extends AppLocalizations {
|
||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appTitle => 'Budget App';
|
||||
|
||||
@override
|
||||
String get homePageTitle => 'Home';
|
||||
|
||||
@override
|
||||
String get reportsPageTitle => 'Reports';
|
||||
|
||||
@override
|
||||
String get settingsPageTitle => 'Settings';
|
||||
|
||||
@override
|
||||
String get addTransactionButton => 'Add new transaction';
|
||||
|
||||
@override
|
||||
String get noTransactionsText => 'No transactions yet';
|
||||
|
||||
@override
|
||||
String get loadingTransactionsText => 'Loading transactions...';
|
||||
|
||||
@override
|
||||
String transactionErrorText(Object message) {
|
||||
return 'Error: $message';
|
||||
}
|
||||
|
||||
@override
|
||||
String get loginPageTitle => 'Login';
|
||||
|
||||
@override
|
||||
String get nameFieldLabel => 'Name';
|
||||
|
||||
@override
|
||||
String get emailFieldLabel => 'Email';
|
||||
|
||||
@override
|
||||
String get nameFieldEmptyError => 'Please enter your name';
|
||||
|
||||
@override
|
||||
String get emailFieldEmptyError => 'Please enter your email';
|
||||
|
||||
@override
|
||||
String get loginButtonText => 'Login / Register';
|
||||
|
||||
@override
|
||||
String get darkModeSetting => 'Dark Theme';
|
||||
|
||||
@override
|
||||
String get darkModeDescription => 'Toggle between light and dark theme';
|
||||
|
||||
@override
|
||||
String get languageSetting => 'Language';
|
||||
|
||||
@override
|
||||
String get languageDescription => 'Change application language';
|
||||
|
||||
@override
|
||||
String get russianLanguage => 'Russian';
|
||||
|
||||
@override
|
||||
String get englishLanguage => 'English';
|
||||
|
||||
@override
|
||||
String get defaultUser => 'Default User';
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for Russian (`ru`).
|
||||
class AppLocalizationsRu extends AppLocalizations {
|
||||
AppLocalizationsRu([String locale = 'ru']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appTitle => 'Бюджетное приложение';
|
||||
|
||||
@override
|
||||
String get homePageTitle => 'Главная';
|
||||
|
||||
@override
|
||||
String get reportsPageTitle => 'Отчеты';
|
||||
|
||||
@override
|
||||
String get settingsPageTitle => 'Настройки';
|
||||
|
||||
@override
|
||||
String get addTransactionButton => 'Добавить новую транзакцию';
|
||||
|
||||
@override
|
||||
String get noTransactionsText => 'Нет транзакций';
|
||||
|
||||
@override
|
||||
String get loadingTransactionsText => 'Загрузка транзакций...';
|
||||
|
||||
@override
|
||||
String transactionErrorText(Object message) {
|
||||
return 'Ошибка: $message';
|
||||
}
|
||||
|
||||
@override
|
||||
String get loginPageTitle => 'Вход';
|
||||
|
||||
@override
|
||||
String get nameFieldLabel => 'Имя';
|
||||
|
||||
@override
|
||||
String get emailFieldLabel => 'Email';
|
||||
|
||||
@override
|
||||
String get nameFieldEmptyError => 'Пожалуйста, введите имя';
|
||||
|
||||
@override
|
||||
String get emailFieldEmptyError => 'Пожалуйста, введите email';
|
||||
|
||||
@override
|
||||
String get loginButtonText => 'Войти / Зарегистрироваться';
|
||||
|
||||
@override
|
||||
String get darkModeSetting => 'Темная тема';
|
||||
|
||||
@override
|
||||
String get darkModeDescription => 'Переключить между светлой и темной темой';
|
||||
|
||||
@override
|
||||
String get languageSetting => 'Язык';
|
||||
|
||||
@override
|
||||
String get languageDescription => 'Изменить язык приложения';
|
||||
|
||||
@override
|
||||
String get russianLanguage => 'Русский';
|
||||
|
||||
@override
|
||||
String get englishLanguage => 'Английский';
|
||||
|
||||
@override
|
||||
String get defaultUser => 'Пользователь по умолчанию';
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"@@locale": "ru",
|
||||
"appTitle": "Бюджетное приложение",
|
||||
"homePageTitle": "Главная",
|
||||
"reportsPageTitle": "Отчеты",
|
||||
"settingsPageTitle": "Настройки",
|
||||
"addTransactionButton": "Добавить новую транзакцию",
|
||||
"noTransactionsText": "Нет транзакций",
|
||||
"loadingTransactionsText": "Загрузка транзакций...",
|
||||
"transactionErrorText": "Ошибка: {message}",
|
||||
"loginPageTitle": "Вход",
|
||||
"nameFieldLabel": "Имя",
|
||||
"emailFieldLabel": "Email",
|
||||
"nameFieldEmptyError": "Пожалуйста, введите имя",
|
||||
"emailFieldEmptyError": "Пожалуйста, введите email",
|
||||
"loginButtonText": "Войти / Зарегистрироваться",
|
||||
"darkModeSetting": "Темная тема",
|
||||
"darkModeDescription": "Переключить между светлой и темной темой",
|
||||
"languageSetting": "Язык",
|
||||
"languageDescription": "Изменить язык приложения",
|
||||
"russianLanguage": "Русский",
|
||||
"englishLanguage": "Английский",
|
||||
"defaultUser": "Пользователь по умолчанию"
|
||||
}
|
||||
+36
-36
@@ -1,6 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart'; // Добавляем импортсса
|
||||
import 'l10n/app_localizations.dart';
|
||||
import 'logic/auth/auth_bloc.dart';
|
||||
import 'pages/login/login_page.dart';
|
||||
import 'package:budget_app/pages/home_page.dart';
|
||||
import 'theme/app_theme.dart';
|
||||
import 'package:hive_ce_flutter/hive_flutter.dart';
|
||||
@@ -14,66 +18,62 @@ void main() async {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => GetIt.instance<AuthBloc>()..add(AuthStarted()),
|
||||
child: const AppView(),
|
||||
);
|
||||
}
|
||||
State<MyApp> createState() => _MyAppState();
|
||||
}
|
||||
|
||||
class AppView extends StatefulWidget {
|
||||
const AppView({super.key});
|
||||
|
||||
@override
|
||||
State<AppView> createState() => _AppViewState();
|
||||
}
|
||||
|
||||
class _AppViewState extends State<AppView> {
|
||||
class _MyAppState extends State<MyApp> {
|
||||
late final SettingsService _settingsService;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_settingsService = GetIt.instance<SettingsService>();
|
||||
_settingsService.addListener(_onThemeChanged);
|
||||
_settingsService.addListener(_onSettingsChanged);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_settingsService.removeListener(_onThemeChanged);
|
||||
_settingsService.removeListener(_onSettingsChanged);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onThemeChanged() {
|
||||
void _onSettingsChanged() {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Budget App',
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: _settingsService.isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
home: BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, state) {
|
||||
if (state is AuthAuthenticated) {
|
||||
return const HomePage();
|
||||
} else {
|
||||
// Здесь будет страница входа, пока просто заглушка
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: Text('Требуется аутентификация'),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
return BlocProvider(
|
||||
create: (context) => GetIt.instance<AuthBloc>()..add(AuthStarted()),
|
||||
child: MaterialApp(
|
||||
title: AppLocalizations.of(context)?.appTitle ?? 'Budget App', // Используем локализованный заголовок
|
||||
theme: AppTheme.lightTheme(),
|
||||
darkTheme: AppTheme.darkTheme(),
|
||||
themeMode: _settingsService.isDarkMode ? ThemeMode.dark : ThemeMode.light,
|
||||
// Добавляем локализацию
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
locale: Locale(_settingsService.languageCode), // Устанавливаем текущий язык из настроек
|
||||
home: BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, state) {
|
||||
if (state is AuthAuthenticated) {
|
||||
return const HomePage();
|
||||
} else {
|
||||
return const LoginPage();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,16 @@ class User {
|
||||
@HiveField(2) // Поле 2 в Hive - третье поле модели
|
||||
final String email;
|
||||
|
||||
@HiveField(3) // Поле 3 в Hive - язык пользователя
|
||||
final String language;
|
||||
|
||||
/// Конструктор пользователя
|
||||
/// id генерируется автоматически, если не передан
|
||||
User({
|
||||
String? id, // Опциональный параметр - если null, сгенерируется автоматически
|
||||
required this.name, // Обязательный параметр
|
||||
required this.email, // Обязательный параметр
|
||||
this.language = 'ru', // Язык по умолчанию - русский
|
||||
}) : id = id ?? IdGenerator.generateId(); // Если id не передан, генерируем новый
|
||||
|
||||
/// Преобразование объекта в Map для сохранения в JSON или передачи по сети
|
||||
@@ -35,6 +39,7 @@ class User {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'email': email,
|
||||
'language': language,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,6 +49,7 @@ class User {
|
||||
id: map['id'],
|
||||
name: map['name'],
|
||||
email: map['email'],
|
||||
language: map['language'] ?? 'ru', // Устанавливаем русский по умолчанию, если язык не указан
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,19 +20,22 @@ class UserAdapter extends TypeAdapter<User> {
|
||||
id: fields[0] as String?,
|
||||
name: fields[1] as String,
|
||||
email: fields[2] as String,
|
||||
language: fields[3] == null ? 'ru' : fields[3] as String,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, User obj) {
|
||||
writer
|
||||
..writeByte(3)
|
||||
..writeByte(4)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.name)
|
||||
..writeByte(2)
|
||||
..write(obj.email);
|
||||
..write(obj.email)
|
||||
..writeByte(3)
|
||||
..write(obj.language);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
+173
-38
@@ -1,24 +1,148 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import '/l10n/app_localizations.dart';
|
||||
import '../logic/auth/auth_bloc.dart';
|
||||
import '../logic/transaction/transaction_bloc.dart';
|
||||
import '../models/transaction_record.dart';
|
||||
import 'settings_page.dart';
|
||||
import 'reports_page.dart'; // Импортируем новую страницу отчетов
|
||||
|
||||
/**
|
||||
* Главная страница приложения для управления бюджетом.
|
||||
*
|
||||
* Этот виджет является основным экраном приложения и содержит:
|
||||
* - AppBar с заголовком и кнопкой настроек
|
||||
* - Основной контент с приветственным сообщением
|
||||
*
|
||||
* Особенности реализации:
|
||||
* 1. Наследуется от StatelessWidget, так как не содержит собственного состояния
|
||||
* 2. Использует Material Design через Scaffold
|
||||
* 3. Управление темой вынесено в SettingsService
|
||||
*/
|
||||
class HomePage extends StatelessWidget {
|
||||
/// Конструктор виджета
|
||||
///
|
||||
/// @param key - опциональный ключ для идентификации виджета
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
int _selectedIndex = 0; // Индекс выбранной вкладки
|
||||
late String _currentUserId; // ID текущего пользователя
|
||||
|
||||
// Список виджетов для каждой вкладки нижней навигации
|
||||
static final List<Widget> _widgetOptions = <Widget>[
|
||||
const TransactionsPage(), // Главная страница с транзакциями
|
||||
const ReportsPage(), // Страница отчетов
|
||||
const SettingsPage(), // Страница настроек
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Получаем ID текущего пользователя из AuthBloc
|
||||
final authState = context.read<AuthBloc>().state;
|
||||
if (authState is AuthAuthenticated) {
|
||||
_currentUserId = authState.user.id;
|
||||
} else {
|
||||
// Если пользователь не аутентифицирован, можно перенаправить на страницу входа
|
||||
// или использовать ID по умолчанию, если это применимо.
|
||||
// В данном случае, для простоты, мы предполагаем, что пользователь всегда аутентифицирован.
|
||||
_currentUserId = 'default_user'; // Заглушка, если что-то пошло не так
|
||||
}
|
||||
}
|
||||
|
||||
void _onItemTapped(int index) {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!; // Получаем экземпляр локализации
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) {
|
||||
return GetIt.instance<TransactionBloc>()..add(LoadTransactions(userId: _currentUserId));
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(localizations.appTitle), // Локализованный заголовок
|
||||
// Кнопки действий в AppBar теперь не нужны, так как настройки перенесены в BottomNavigationBar
|
||||
),
|
||||
body: Center(
|
||||
child: _widgetOptions[_selectedIndex], // Отображаем выбранный виджет
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
// TODO: Добавить логику для добавления новой транзакции
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(localizations.addTransactionButton)), // Локализованный текст
|
||||
);
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
items: <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
icon: const Icon(Icons.home),
|
||||
label: localizations.homePageTitle, // Локализованный текст
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: const Icon(Icons.bar_chart),
|
||||
label: localizations.reportsPageTitle, // Локализованный текст
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: const Icon(Icons.settings),
|
||||
label: localizations.settingsPageTitle, // Локализованный текст
|
||||
),
|
||||
],
|
||||
currentIndex: _selectedIndex,
|
||||
selectedItemColor: Theme.of(context).colorScheme.primary,
|
||||
onTap: _onItemTapped,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Отдельный виджет для страницы транзакций, чтобы HomeView был чище
|
||||
class TransactionsPage extends StatelessWidget {
|
||||
const TransactionsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!; // Получаем экземпляр локализации
|
||||
|
||||
return BlocBuilder<TransactionBloc, TransactionState>(
|
||||
builder: (context, state) {
|
||||
if (state is TransactionLoading) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
} else if (state is TransactionLoaded) {
|
||||
if (state.transactions.isEmpty) {
|
||||
return Center(child: Text(localizations.noTransactionsText)); // Локализованный текст
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: state.transactions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final transaction = state.transactions[index];
|
||||
return ListTile(
|
||||
leading: Icon(transaction.category.icon, color: transaction.category.color),
|
||||
title: Text(transaction.vendor),
|
||||
subtitle: Text(transaction.category.name),
|
||||
trailing: Text(
|
||||
'${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
|
||||
style: TextStyle(
|
||||
color: transaction.isIncome ? Colors.green : Colors.red,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if (state is TransactionError) {
|
||||
return Center(child: Text(localizations.transactionErrorText(state.message))); // Локализованный текст
|
||||
} else {
|
||||
return Center(child: Text(localizations.loadingTransactionsText)); // Локализованный текст
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomeView extends StatelessWidget {
|
||||
const HomeView({super.key});
|
||||
|
||||
|
||||
/**
|
||||
* Основной метод построения интерфейса виджета.
|
||||
*
|
||||
@@ -29,6 +153,8 @@ class HomePage extends StatelessWidget {
|
||||
*/
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!; // Получаем экземпляр локализации
|
||||
|
||||
return Scaffold(
|
||||
// Верхняя панель приложения
|
||||
appBar: AppBar(
|
||||
@@ -51,28 +177,37 @@ class HomePage extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
|
||||
// Основное содержимое страницы
|
||||
body: const Center(
|
||||
// Вертикальное расположение элементов
|
||||
child: Column(
|
||||
// Выравнивание по центру по вертикали
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
// Дочерние виджеты колонки
|
||||
children: [
|
||||
// Иконка кошелька
|
||||
Icon(Icons.account_balance_wallet, size: 64),
|
||||
|
||||
// Отступ между элементами
|
||||
SizedBox(height: 16),
|
||||
|
||||
// Приветственный текст
|
||||
Text(
|
||||
'Добро пожаловать в Budget App!',
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<TransactionBloc, TransactionState>(
|
||||
builder: (context, state) {
|
||||
if (state is TransactionLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is TransactionLoaded) {
|
||||
if (state.transactions.isEmpty) {
|
||||
return const Center(child: Text('Нет транзакций'));
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: state.transactions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final transaction = state.transactions[index];
|
||||
return ListTile(
|
||||
leading: Icon(transaction.category.icon, color: transaction.category.color),
|
||||
title: Text(transaction.vendor),
|
||||
subtitle: Text(transaction.category.name),
|
||||
trailing: Text(
|
||||
'${transaction.amount.toStringAsFixed(2)} ${transaction.currency}',
|
||||
style: TextStyle(
|
||||
color: transaction.isIncome ? Colors.green : Colors.red,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if (state is TransactionError) {
|
||||
return Center(child: Text(localizations.transactionErrorText(state.message))); // Локализованный текст
|
||||
} else {
|
||||
return Center(child: Text(localizations.loadingTransactionsText)); // Локализованный текст
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import '/l10n/app_localizations.dart';
|
||||
import '../../logic/auth/auth_bloc.dart';
|
||||
import '../../services/user_service.dart';
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
|
||||
@override
|
||||
State<LoginPage> createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _emailController = TextEditingController();
|
||||
final _nameController = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!; // Получаем экземпляр локализации
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(localizations.loginPageTitle)), // Локализованный заголовок
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _nameController,
|
||||
decoration: InputDecoration(labelText: localizations.nameFieldLabel), // Локализованный текст
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return localizations.nameFieldEmptyError; // Локализованный текст
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
decoration: InputDecoration(labelText: localizations.emailFieldLabel), // Локализованный текст
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return localizations.emailFieldEmptyError; // Локализованный текст
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final userService = GetIt.instance<UserService>();
|
||||
userService.createAndSetUser(
|
||||
_nameController.text,
|
||||
_emailController.text,
|
||||
).then((_) {
|
||||
final user = userService.currentUser;
|
||||
if (user != null) {
|
||||
context.read<AuthBloc>().add(AuthLoggedIn(user: user));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Text(localizations.loginButtonText), // Локализованный текст
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '/l10n/app_localizations.dart';
|
||||
|
||||
class ReportsPage extends StatelessWidget {
|
||||
const ReportsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!; // Получаем экземпляр локализации
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(localizations.reportsPageTitle), // Локализованный заголовок
|
||||
),
|
||||
body: Center(
|
||||
child: Text(localizations.reportsPageTitle), // Локализованный текст
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import '/l10n/app_localizations.dart';
|
||||
import '../services/settings_service.dart';
|
||||
|
||||
class SettingsPage extends StatefulWidget {
|
||||
@@ -31,9 +32,11 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localizations = AppLocalizations.of(context)!; // Получаем экземпляр локализации
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Настройки'),
|
||||
title: Text(localizations.settingsPageTitle), // Локализованный заголовок
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
@@ -41,14 +44,34 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: const Text('Темная тема'),
|
||||
subtitle: const Text('Переключить между светлой и темной темой'),
|
||||
title: Text(localizations.darkModeSetting), // Локализованный текст
|
||||
subtitle: Text(localizations.darkModeDescription), // Локализованный текст
|
||||
value: _settingsService.isDarkMode,
|
||||
onChanged: (value) async {
|
||||
await _settingsService.setDarkMode(value);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
title: Text(localizations.languageSetting), // Локализованный текст
|
||||
subtitle: Text(localizations.languageDescription), // Локализованный текст
|
||||
trailing: DropdownButton<String>(
|
||||
value: _settingsService.languageCode, // Текущий выбранный язык
|
||||
onChanged: (String? newValue) async {
|
||||
if (newValue != null) {
|
||||
await _settingsService.setLanguageCode(newValue);
|
||||
}
|
||||
},
|
||||
items: <String>['en', 'ru'] // Доступные языки
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value == 'en' ? localizations.englishLanguage : localizations.russianLanguage), // Локализованные названия языков
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
// Здесь можно добавить другие настройки
|
||||
],
|
||||
),
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:hive_ce/hive.dart';
|
||||
|
||||
class SettingsService extends ChangeNotifier {
|
||||
static const String _darkModeKey = 'darkMode';
|
||||
static const String _languageCodeKey = 'languageCode'; // Новый ключ для языка
|
||||
late final Box _settingsBox;
|
||||
|
||||
SettingsService() {
|
||||
@@ -19,4 +20,15 @@ class SettingsService extends ChangeNotifier {
|
||||
Future<void> toggleTheme() async {
|
||||
await setDarkMode(!isDarkMode);
|
||||
}
|
||||
|
||||
// Новый геттер для получения кода языка
|
||||
String get languageCode => _settingsBox.get(_languageCodeKey, defaultValue: 'ru');
|
||||
|
||||
// Новый метод для установки кода языка
|
||||
Future<void> setLanguageCode(String code) async {
|
||||
await _settingsBox.put(_languageCodeKey, code);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import 'package:budget_app/data/repositories/interfaces/icategory_repository.dart';
|
||||
import 'package:budget_app/data/repositories/interfaces/itag_repository.dart';
|
||||
import 'package:budget_app/data/repositories/interfaces/itransaction_repository.dart';
|
||||
import 'package:budget_app/utils/category_utils.dart';
|
||||
import 'package:budget_app/utils/tag_utils.dart';
|
||||
import 'package:budget_app/utils/transaction_utils.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import '/models/user.dart';
|
||||
@@ -10,7 +16,10 @@ class UserService extends ChangeNotifier {
|
||||
static const String _currentUserKey = 'currentUserId';
|
||||
|
||||
late final Box _settingsBox; // Box для хранения настроек
|
||||
final IUserRepository _userRepository; // Репозиторий для работы с пользователями
|
||||
final IUserRepository _userRepository;
|
||||
final ICategoryRepository _categoryRepository;
|
||||
final ITagRepository _tagRepository;
|
||||
final ITransactionRepository _transactionRepository;
|
||||
|
||||
User? _currentUser; // Текущий активный пользователь
|
||||
|
||||
@@ -21,7 +30,7 @@ class UserService extends ChangeNotifier {
|
||||
bool get hasCurrentUser => _currentUser != null;
|
||||
|
||||
/// Конструктор сервиса
|
||||
UserService(this._userRepository) {
|
||||
UserService(this._userRepository, this._categoryRepository, this._tagRepository, this._transactionRepository) {
|
||||
_settingsBox = Hive.box('settings');
|
||||
_loadCurrentUser(); // Загружаем сохраненного пользователя при запуске
|
||||
}
|
||||
@@ -52,8 +61,14 @@ class UserService extends ChangeNotifier {
|
||||
|
||||
/// Создание нового пользователя и установка его как текущего
|
||||
Future<void> createAndSetUser(String name, String email) async {
|
||||
final user = User(name: name, email: email);
|
||||
final user = User(name: name, email: email, language: 'ru'); // Устанавливаем язык по умолчанию
|
||||
await _userRepository.add(user);
|
||||
|
||||
// Добавляем начальные категории, теги и транзакции для нового пользователя
|
||||
await _categoryRepository.addAll(CategoryUtils.getDefaultCategories(user.id));
|
||||
await _tagRepository.addAll(TagUtils.getDefaultTags(user.id));
|
||||
await _transactionRepository.addAll(TransactionUtils.getSampleTransactions(user.id));
|
||||
|
||||
await setCurrentUser(user);
|
||||
}
|
||||
|
||||
|
||||
+30
-17
@@ -5,18 +5,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f
|
||||
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "82.0.0"
|
||||
version: "85.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0"
|
||||
sha256: f6154230675c44a191f2e20d16eeceb4aa18b30ca732db4efaf94c6a7d43cfa6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.4.5"
|
||||
version: "7.5.2"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -34,7 +34,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189"
|
||||
@@ -53,10 +53,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
||||
sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.5.4"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -77,26 +77,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
|
||||
sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
version: "2.5.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
|
||||
sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.15"
|
||||
version: "2.5.4"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||
sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
version: "9.1.2"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -246,6 +246,11 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -331,6 +336,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -399,10 +412,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: logger
|
||||
sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
|
||||
sha256: "2621da01aabaf223f8f961e751f2c943dbb374dc3559b982f200ccedadaa6999"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.6.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -700,10 +713,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
|
||||
sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.1.2"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -42,6 +42,10 @@ dependencies:
|
||||
logger: ^2.5.0
|
||||
flutter_bloc: ^9.1.1
|
||||
equatable: ^2.0.7
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: ^0.20.2
|
||||
bloc:
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
@@ -57,6 +61,7 @@ dev_dependencies:
|
||||
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user