added separate setting to change currency

This commit is contained in:
ShabOl 2024-12-30 11:57:56 +04:00
parent f0a17af845
commit 1377a5dbdd
22 changed files with 495 additions and 176 deletions

View File

@ -10,9 +10,12 @@
"addedToFavourite": "is added to favourites",
"removedFromFavourite": "is removed from favourites",
"cardsLoadingFailed": "Server is unreachable",
"cardsLoadingFailedSnackBar": "Failed to load crypto data",
"coinDataPriceChange": "for the last 24 hours",
"settingsLanguage": "Language",
"settingsCurrency": "Currency",
"navigationHome": "Home",
"navigationFavourites": "Favourites",

View File

@ -10,9 +10,12 @@
"addedToFavourite": "добавлен в избранное",
"removedFromFavourite": "удален из избранного",
"cardsLoadingFailed": "Сервер недоступен",
"cardsLoadingFailedSnackBar": "Не удалось загрузить данные о криптовалюте",
"coinDataPriceChange": "за последние 24 часа",
"settingsLanguage": "Язык",
"settingsCurrency": "Валюта",
"navigationHome": "Главная",
"navigationFavourites": "Избранное",

View File

@ -137,6 +137,18 @@ abstract class AppLocale {
/// **'is removed from favourites'**
String get removedFromFavourite;
/// No description provided for @cardsLoadingFailed.
///
/// In en, this message translates to:
/// **'Server is unreachable'**
String get cardsLoadingFailed;
/// No description provided for @cardsLoadingFailedSnackBar.
///
/// In en, this message translates to:
/// **'Failed to load crypto data'**
String get cardsLoadingFailedSnackBar;
/// No description provided for @coinDataPriceChange.
///
/// In en, this message translates to:
@ -149,6 +161,12 @@ abstract class AppLocale {
/// **'Language'**
String get settingsLanguage;
/// No description provided for @settingsCurrency.
///
/// In en, this message translates to:
/// **'Currency'**
String get settingsCurrency;
/// No description provided for @navigationHome.
///
/// In en, this message translates to:

View File

@ -27,12 +27,21 @@ class AppLocaleEn extends AppLocale {
@override
String get removedFromFavourite => 'is removed from favourites';
@override
String get cardsLoadingFailed => 'Server is unreachable';
@override
String get cardsLoadingFailedSnackBar => 'Failed to load crypto data';
@override
String get coinDataPriceChange => 'for the last 24 hours';
@override
String get settingsLanguage => 'Language';
@override
String get settingsCurrency => 'Currency';
@override
String get navigationHome => 'Home';

View File

@ -27,12 +27,21 @@ class AppLocaleRu extends AppLocale {
@override
String get removedFromFavourite => 'удален из избранного';
@override
String get cardsLoadingFailed => 'Сервер недоступен';
@override
String get cardsLoadingFailedSnackBar => 'Не удалось загрузить данные о криптовалюте';
@override
String get coinDataPriceChange => 'за последние 24 часа';
@override
String get settingsLanguage => 'Язык';
@override
String get settingsCurrency => 'Валюта';
@override
String get navigationHome => 'Главная';

View File

@ -4,28 +4,26 @@ import 'package:flutter_android_app/domain/models/card.dart';
import 'package:flutter_android_app/domain/models/home.dart';
extension CoinDataDtoToModel on CoinDataDto {
CardData toDomain(AppLocale? locale) => CardData(
CardData toDomain(AppLocale? locale, String currencyId) => CardData(
id: id ?? 'UNKNOWN',
title: name ?? 'UNKNOWN',
imageUrl: image,
currentPrice: _getLocalizedPrice(currentPrice, locale?.localeName),
priceChange: _getLocalizedPriceChange(priceChange24h, locale),
currentPrice: '$currentPrice ${_getCurrencySymbol(currencyId)}',
priceChange: _getLocalizedPriceChange(priceChange24h, locale, currencyId),
);
String _getLocalizedPrice(double? price, String? localeName) {
if (localeName == null) {
return '$price \$';
}
String _getCurrencySymbol(String currencyId) {
return switch (currencyId) {
'rub' => '',
'usd' => '\$',
return switch (localeName) {
'ru' => '$price',
_ => '$price \$',
_ => '?',
};
}
String _getLocalizedPriceChange(double? priceChange, AppLocale? locale) {
String _getLocalizedPriceChange(double? priceChange, AppLocale? locale, String currencyId) {
if (priceChange == null) {
return '+${_getLocalizedPrice(0, locale?.localeName)}';
return '+0 ${_getCurrencySymbol(currencyId)}';
}
String retVal = '';
@ -33,15 +31,15 @@ extension CoinDataDtoToModel on CoinDataDto {
retVal += '+';
}
retVal += _getLocalizedPrice(priceChange, locale?.localeName);
retVal += '$priceChange ${_getCurrencySymbol(currencyId)}';
return '$retVal ${locale?.coinDataPriceChange}';
}
}
extension CoinsDtoToModel on CoinsDto {
HomeData toDomain(AppLocale? locale, int currentPage) => HomeData(
data: coins?.map((e) => e.toDomain(locale)).toList(),
HomeData toDomain(AppLocale? locale, String currencyId, int currentPage) => HomeData(
data: coins?.map((e) => e.toDomain(locale, currencyId)).toList(),
nextPage: currentPage + 1,
);
}

View File

@ -1,6 +1,7 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_bloc.dart';
import 'package:flutter_android_app/presentation/favourites_bloc/favourites_bloc.dart';
import 'package:flutter_android_app/presentation/home_page/bloc/bloc.dart';
import 'package:flutter_android_app/presentation/home_page/home_page.dart';
@ -30,6 +31,9 @@ class _MyAppState extends State<MyApp> {
return BlocProvider<FavouritesBloc>(
lazy: false,
create: (context) => FavouritesBloc(),
child: BlocProvider<CurrencyBloc>(
lazy: false,
create: (context) => CurrencyBloc(),
child: BlocProvider<LocaleBloc>(
lazy: false,
create: (context) => LocaleBloc(Locale(_getLangCode(Platform.localeName))),
@ -63,6 +67,7 @@ class _MyAppState extends State<MyApp> {
),
),
),
),
);
}

View File

@ -0,0 +1,61 @@
import 'package:flutter_android_app/presentation/currency_bloc/currency_events.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CurrencyBloc extends Bloc<CurrencyEvent, CurrencyState> {
static const String _currencyPrefsKey = 'local_currency';
static const List<String> _availableCurrencyIds = ['usd', 'rub'];
static final String _defaultCurrencyId = _availableCurrencyIds[0];
CurrencyBloc() : super(const CurrencyState()) {
on<LoadLocalCurrencyEvent>(_onLoadLocalCurrency);
on<ToggleLocalCurrencyEvent>(_onToggleLocalCurrency);
}
Future<void> _onLoadLocalCurrency(
LoadLocalCurrencyEvent event, Emitter<CurrencyState> emit
) async {
emit(state.copyWith(
hasCurrencyLoaded: false,
));
final prefs = await SharedPreferences.getInstance();
final data = prefs.getString(_currencyPrefsKey);
String currencyId = '';
if (data != null) {
currencyId = data;
} else {
currencyId = _defaultCurrencyId;
}
emit(state.copyWith(
currencyId: currencyId,
hasCurrencyLoaded: true,
));
}
Future<void> _onToggleLocalCurrency(
ToggleLocalCurrencyEvent event, Emitter<CurrencyState> emit
) async {
if (state.currencyId == null) {
return;
}
final int oldCurrencyIdIdx = _availableCurrencyIds.indexOf(state.currencyId!);
int newCurrencyIdIdx = oldCurrencyIdIdx + 1;
if (newCurrencyIdIdx >= _availableCurrencyIds.length){
newCurrencyIdIdx = 0;
}
final newCurrencyId = _availableCurrencyIds[newCurrencyIdIdx];
final prefs = await SharedPreferences.getInstance();
prefs.setString(_currencyPrefsKey, newCurrencyId);
emit(state.copyWith(
currencyId: newCurrencyId,
));
}
}

View File

@ -0,0 +1,11 @@
abstract class CurrencyEvent {
const CurrencyEvent();
}
class LoadLocalCurrencyEvent extends CurrencyEvent {
const LoadLocalCurrencyEvent();
}
class ToggleLocalCurrencyEvent extends CurrencyEvent {
const ToggleLocalCurrencyEvent();
}

View File

@ -0,0 +1,21 @@
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:equatable/equatable.dart';
part 'currency_state.g.dart';
@CopyWith()
class CurrencyState extends Equatable {
final String? currencyId;
final bool hasCurrencyLoaded;
const CurrencyState({
this.currencyId,
this.hasCurrencyLoaded = false
});
@override
List<Object?> get props => [
currencyId,
hasCurrencyLoaded,
];
}

View File

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

View File

@ -22,7 +22,7 @@ class FavouritesBloc extends Bloc<FavouritesEvent, FavouritesState> {
final data = prefs.getStringList(_likedPrefsKey);
emit(state.copyWith(
likedIds: data,
favouritesIds: data,
hasFavouritesLoaded: true,
));
}
@ -42,7 +42,7 @@ class FavouritesBloc extends Bloc<FavouritesEvent, FavouritesState> {
prefs.setStringList(_likedPrefsKey, updatedList);
emit(state.copyWith(
likedIds: updatedList,
favouritesIds: updatedList,
hasFavouritesLoaded: true,
));
}

View File

@ -7,7 +7,7 @@ part of 'favourites_state.dart';
// **************************************************************************
abstract class _$FavouritesStateCWProxy {
FavouritesState likedIds(List<String>? likedIds);
FavouritesState favouritesIds(List<String>? favouritesIds);
FavouritesState hasFavouritesLoaded(bool hasFavouritesLoaded);
@ -18,7 +18,7 @@ abstract class _$FavouritesStateCWProxy {
/// FavouritesState(...).copyWith(id: 12, name: "My name")
/// ````
FavouritesState call({
List<String>? likedIds,
List<String>? favouritesIds,
bool? hasFavouritesLoaded,
});
}
@ -30,7 +30,8 @@ class _$FavouritesStateCWProxyImpl implements _$FavouritesStateCWProxy {
final FavouritesState _value;
@override
FavouritesState likedIds(List<String>? likedIds) => this(likedIds: likedIds);
FavouritesState favouritesIds(List<String>? favouritesIds) =>
this(favouritesIds: favouritesIds);
@override
FavouritesState hasFavouritesLoaded(bool hasFavouritesLoaded) =>
@ -45,14 +46,14 @@ class _$FavouritesStateCWProxyImpl implements _$FavouritesStateCWProxy {
/// FavouritesState(...).copyWith(id: 12, name: "My name")
/// ````
FavouritesState call({
Object? likedIds = const $CopyWithPlaceholder(),
Object? favouritesIds = const $CopyWithPlaceholder(),
Object? hasFavouritesLoaded = const $CopyWithPlaceholder(),
}) {
return FavouritesState(
favouritesIds: likedIds == const $CopyWithPlaceholder()
favouritesIds: favouritesIds == const $CopyWithPlaceholder()
? _value.favouritesIds
// ignore: cast_nullable_to_non_nullable
: likedIds as List<String>?,
: favouritesIds as List<String>?,
hasFavouritesLoaded:
hasFavouritesLoaded == const $CopyWithPlaceholder() ||
hasFavouritesLoaded == null

View File

@ -1,5 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_android_app/components/extensions/context_x.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_bloc.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_events.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_state.dart';
import 'package:flutter_android_app/presentation/favourites_bloc/favourites_bloc.dart';
import 'package:flutter_android_app/presentation/favourites_bloc/favourites_events.dart';
import 'package:flutter_android_app/presentation/favourites_bloc/favourites_state.dart';
@ -21,9 +24,12 @@ class FavouritesPage extends StatefulWidget {
class _FavouritesPageState extends State<FavouritesPage> {
final ScrollController scrollController = ScrollController();
List<String>? favouritesIds;
bool wereIdsPreviouslyLoaded = false;
String? currencyId;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
@ -47,14 +53,22 @@ class _FavouritesPageState extends State<FavouritesPage> {
if (state.hasFavouritesLoaded && !wereIdsPreviouslyLoaded) {
wereIdsPreviouslyLoaded = true;
favouritesIds = state.favouritesIds;
context.read<CurrencyBloc>().add(const LoadLocalCurrencyEvent());
}
},
child: BlocListener<CurrencyBloc, CurrencyState>(
listener: (context, state) {
if (state.hasCurrencyLoaded && state.currencyId != null) {
currencyId = state.currencyId;
context.read<HomeBloc>().add(HomeLoadFavouritesDataEvent(
ids: favouritesIds,
locale: context.locale,
currencyId: currencyId!,
));
}
},
child: Padding(
padding: EdgeInsets.only(top: 12),
padding: const EdgeInsets.only(top: 12),
child: Column(
children: [
CardsList(
@ -74,14 +88,11 @@ class _FavouritesPageState extends State<FavouritesPage> {
],
),
),
),
);
}
Future<void> _onRefresh() {
//context.read<HomeBloc>().add(HomeLoadFavouritesDataEvent(
// ids: favouritesIds,
// locale: context.locale,
//));
wereIdsPreviouslyLoaded = false;
context.read<FavouritesBloc>().add(const LoadFavouritesEvent());
return Future.value(null);
@ -120,6 +131,7 @@ class _FavouritesPageState extends State<FavouritesPage> {
ids: favouritesIds,
nextPage: bloc.state.data?.nextPage,
locale: context.locale,
currencyId: currencyId!,
));
}
}

View File

@ -15,7 +15,10 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
const int pageSize = 20;
if (event.nextPage == null) {
emit(state.copyWith(isLoading: true));
emit(state.copyWith(
isLoading: true,
error: 'NO_ERROR',
));
} else {
emit(state.copyWith(isPaginationLoading: true));
}
@ -28,6 +31,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
pageSize: pageSize,
onError: (e) => error = e,
locale: event.locale,
currencyId: event.currencyId,
);
bool isLastPage = false;
@ -52,7 +56,10 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
const int pageSize = 10;
if (event.nextPage == null) {
emit(state.copyWith(isLoading: true));
emit(state.copyWith(
isLoading: true,
error: 'NO_ERROR',
));
} else {
emit(state.copyWith(isPaginationLoading: true));
}
@ -65,6 +72,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
pageSize: pageSize,
onError: (e) => error = e,
locale: event.locale,
currencyId: event.currencyId,
);
bool isLastPage = false;

View File

@ -8,14 +8,26 @@ class HomeLoadDataEvent extends HomeEvent {
final String? search;
final int? nextPage;
final AppLocale? locale;
final String currencyId;
const HomeLoadDataEvent({this.search, this.nextPage, this.locale});
const HomeLoadDataEvent({
this.search,
this.nextPage,
this.locale,
required this.currencyId,
});
}
class HomeLoadFavouritesDataEvent extends HomeEvent {
final List<String>? ids;
final int? nextPage;
final AppLocale? locale;
final String currencyId;
const HomeLoadFavouritesDataEvent({this.ids, this.nextPage, this.locale});
const HomeLoadFavouritesDataEvent({
this.ids,
this.nextPage,
this.locale,
required this.currencyId,
});
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_android_app/components/extensions/context_x.dart';
import 'package:flutter_android_app/domain/models/card.dart';
import 'package:flutter_android_app/presentation/home_page/card_crypto.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -45,17 +46,39 @@ class _CardsListState extends State<CardsList> {
@override
Widget build(BuildContext context) {
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.error != null
? Text(
state.error ?? '',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.red),
return BlocListener<HomeBloc, HomeState>(
listener: (context, state) {
if (state.error != null && state.error != 'NO_ERROR') {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
context.locale.cardsLoadingFailedSnackBar,
style: Theme.of(context).textTheme.bodyLarge,
),
backgroundColor: Theme.of(context).colorScheme.errorContainer,
duration: const Duration(seconds: 2),
));
});
}
},
child: BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.error != null && state.error != 'NO_ERROR'
? Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: Text(
context.locale.cardsLoadingFailed,
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Theme.of(context).disabledColor),
),
),
)
: state.isLoading
? const Center(child: Padding(
? const Center(
child: Padding(
padding: EdgeInsets.only(top: 20),
child: CircularProgressIndicator(),
))
)
)
: BlocBuilder<FavouritesBloc, FavouritesState>(
builder: (context, likeState) => Expanded(
child: RefreshIndicator(
@ -79,6 +102,7 @@ class _CardsListState extends State<CardsList> {
),
),
)
),
);
}

View File

@ -11,6 +11,9 @@ import 'package:flutter_android_app/presentation/home_page/cards_list.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../common/svg_objects.dart';
import '../currency_bloc/currency_bloc.dart';
import '../currency_bloc/currency_events.dart';
import '../currency_bloc/currency_state.dart';
import '../favourites_bloc/favourites_bloc.dart';
import '../favourites_bloc/favourites_events.dart';
import '../settings_page/settings_page.dart';
@ -83,13 +86,14 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
final TextEditingController searchController = TextEditingController();
late String? currencyId;
@override
void initState() {
SvgObjects.init();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<HomeBloc>().add(HomeLoadDataEvent(locale: context.locale));
context.read<CurrencyBloc>().add(const LoadLocalCurrencyEvent());
context.read<FavouritesBloc>().add(const LoadFavouritesEvent());
});
@ -105,7 +109,17 @@ class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Padding(
return BlocListener<CurrencyBloc, CurrencyState>(
listener: (context, state) {
if (state.hasCurrencyLoaded && state.currencyId != null) {
currencyId = state.currencyId;
context.read<HomeBloc>().add(HomeLoadDataEvent(
locale: context.locale,
currencyId: currencyId!,
));
}
},
child: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
children: [
@ -114,7 +128,11 @@ class _HomePageState extends State<HomePage> {
child: SearchBar(
controller: searchController,
onChanged: (search) {
Debounce.run(() => context.read<HomeBloc>().add(HomeLoadDataEvent(search: search, locale: context.locale)));
Debounce.run(() => context.read<HomeBloc>().add(HomeLoadDataEvent(
search: search,
locale: context.locale,
currencyId: currencyId!,
)));
},
leading: const Icon(Icons.search),
trailing: [
@ -122,8 +140,12 @@ class _HomePageState extends State<HomePage> {
icon: const Icon(Icons.close),
onPressed: () {
if (searchController.text.isNotEmpty) {
FocusManager.instance.primaryFocus?.unfocus();
searchController.clear();
context.read<HomeBloc>().add(HomeLoadDataEvent(locale: context.locale));
context.read<HomeBloc>().add(HomeLoadDataEvent(
locale: context.locale,
currencyId: currencyId!,
));
}
},
),
@ -150,6 +172,7 @@ class _HomePageState extends State<HomePage> {
),
],
)
),
);
}
@ -167,7 +190,11 @@ class _HomePageState extends State<HomePage> {
}
Future<void> _onRefresh() {
context.read<HomeBloc>().add(HomeLoadDataEvent(search: searchController.text, locale: context.locale));
context.read<HomeBloc>().add(HomeLoadDataEvent(
search: searchController.text,
locale: context.locale,
currencyId: currencyId!,
));
return Future.value(null);
}
@ -179,6 +206,7 @@ class _HomePageState extends State<HomePage> {
}
void _navToDetails(CardData data) {
FocusManager.instance.primaryFocus?.unfocus();
Navigator.push(context, MaterialPageRoute<void>(
builder: (context) => DetailsPage(data)),
);
@ -191,6 +219,7 @@ class _HomePageState extends State<HomePage> {
search: searchController.text,
nextPage: bloc.state.data?.nextPage,
locale: context.locale,
currencyId: currencyId!,
));
}
}

View File

@ -1,5 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_android_app/components/extensions/context_x.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_bloc.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_events.dart';
import 'package:flutter_android_app/presentation/currency_bloc/currency_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../common/svg_objects.dart';
@ -40,6 +43,34 @@ class SettingsPage extends StatelessWidget {
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${context.locale.settingsCurrency}:',
style: Theme.of(context).textTheme.titleMedium,
),
BlocBuilder<CurrencyBloc, CurrencyState>(
builder: (context, currencyState) => GestureDetector(
onTap: () => context.read<CurrencyBloc>().add(const ToggleLocalCurrencyEvent()),
child: SizedBox.square(
dimension: 50,
child: Padding(
padding: const EdgeInsets.only(right: 0),
child: BlocBuilder<LocaleBloc, LocaleState>(
builder: (context, state) {
return switch (currencyState.currencyId) {
'rub' => const SvgRu(),
'usd' => const SvgUs(),
_ => const SvgUs(),
};
}),
),
),
),
),
],
),
],
),
);

View File

@ -10,6 +10,7 @@ abstract class ApiInterface {
int page = 1,
int pageSize = 20,
AppLocale? locale,
String currencyId,
});
Future<HomeData?> loadDataWithIds({
@ -18,5 +19,6 @@ abstract class ApiInterface {
int page = 1,
int pageSize = 8,
AppLocale? locale,
String currencyId,
});
}

View File

@ -29,11 +29,12 @@ class CryptoRepository extends ApiInterface {
int page = 1,
int pageSize = 20,
AppLocale? locale,
String currencyId = '',
}) async {
try {
Map<String, dynamic> queryParams = {
'x_cg_demo_api_key': _apiKey,
'vs_currency': _getCurrencyName(locale?.localeName),
'vs_currency': currencyId,
'per_page': pageSize,
'page': page,
};
@ -68,7 +69,7 @@ class CryptoRepository extends ApiInterface {
);
final CoinsDto dto = CoinsDto.fromJson(response.data as List<dynamic>);
final HomeData data = dto.toDomain(locale, page);
final HomeData data = dto.toDomain(locale, currencyId, page);
return data;
} on DioException catch (e) {
@ -84,11 +85,12 @@ class CryptoRepository extends ApiInterface {
int page = 1,
int pageSize = 20,
AppLocale? locale,
String currencyId = '',
}) async {
try {
Map<String, dynamic> queryParams = {
'x_cg_demo_api_key': _apiKey,
'vs_currency': _getCurrencyName(locale?.localeName),
'vs_currency': currencyId,
'per_page': pageSize,
'page': page,
};
@ -108,7 +110,7 @@ class CryptoRepository extends ApiInterface {
);
final CoinsDto dto = CoinsDto.fromJson(response.data as List<dynamic>);
final HomeData data = dto.toDomain(locale, page);
final HomeData data = dto.toDomain(locale, currencyId, page);
return data;
} on DioException catch (e) {
@ -116,15 +118,4 @@ class CryptoRepository extends ApiInterface {
return null;
}
}
String _getCurrencyName(String? localeName) {
if (localeName == null) {
return 'usd';
}
return switch (localeName) {
'ru' => 'rub',
_ => 'usd',
};
}
}

View File

@ -13,6 +13,7 @@ class MockRepository extends ApiInterface {
int page = 1,
int pageSize = 20,
AppLocale? locale,
String currencyId = '',
}) async {
return HomeData(data: [
CardData(
@ -46,6 +47,7 @@ class MockRepository extends ApiInterface {
int page = 1,
int pageSize = 20,
AppLocale? locale,
String currencyId = '',
}) async {
return HomeData(data: [
CardData(