implemented mock crypto repository
changed Cupertino search bar to Material UI 3 changed baseline color and card colors implemented fetching crypto data from public API (untested)
This commit is contained in:
parent
efdbe85897
commit
a676d454ca
@ -1,7 +1,7 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
|
||||
"appBarTitle": "Crypto Exchange",
|
||||
"appBarTitle": "Harry Potter characters",
|
||||
|
||||
"search": "Search",
|
||||
"liked": "You liked",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"@@locale": "ru",
|
||||
|
||||
"appBarTitle": "Криптобиржа",
|
||||
"appBarTitle": "Персонажи из Гарри Поттера",
|
||||
|
||||
"search": "Поиск",
|
||||
"liked": "Вы добавили в избранное",
|
||||
|
@ -98,7 +98,7 @@ abstract class AppLocale {
|
||||
/// No description provided for @appBarTitle.
|
||||
///
|
||||
/// In ru, this message translates to:
|
||||
/// **'Криптобиржа'**
|
||||
/// **'Персонажи из Гарри Поттера'**
|
||||
String get appBarTitle;
|
||||
|
||||
/// No description provided for @search.
|
||||
|
@ -7,7 +7,7 @@ class AppLocaleEn extends AppLocale {
|
||||
AppLocaleEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appBarTitle => 'Crypto Exchange';
|
||||
String get appBarTitle => 'Harry Potter characters';
|
||||
|
||||
@override
|
||||
String get search => 'Search';
|
||||
|
@ -7,7 +7,7 @@ class AppLocaleRu extends AppLocale {
|
||||
AppLocaleRu([String locale = 'ru']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appBarTitle => 'Криптобиржа';
|
||||
String get appBarTitle => 'Персонажи из Гарри Поттера';
|
||||
|
||||
@override
|
||||
String get search => 'Поиск';
|
||||
|
31
lib/data/dtos/coins_dto.dart
Normal file
31
lib/data/dtos/coins_dto.dart
Normal file
@ -0,0 +1,31 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'coins_dto.g.dart';
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class CoinsDto {
|
||||
final List<CoinDataDto>? coins;
|
||||
|
||||
CoinsDto({this.coins});
|
||||
|
||||
factory CoinsDto.fromJson(Map<String, dynamic> json) => _$CoinsDtoFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class CoinDataDto {
|
||||
final String? id;
|
||||
final String? name;
|
||||
final String? image;
|
||||
final double? currentPrice;
|
||||
final double? priceChange24h;
|
||||
|
||||
CoinDataDto({
|
||||
this.id,
|
||||
this.name,
|
||||
this.image,
|
||||
this.currentPrice,
|
||||
this.priceChange24h,
|
||||
});
|
||||
|
||||
factory CoinDataDto.fromJson(Map<String, dynamic> json) => _$CoinDataDtoFromJson(json);
|
||||
}
|
21
lib/data/dtos/coins_dto.g.dart
Normal file
21
lib/data/dtos/coins_dto.g.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'coins_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CoinsDto _$CoinsDtoFromJson(Map<String, dynamic> json) => CoinsDto(
|
||||
coins: (json['coins'] as List<dynamic>?)
|
||||
?.map((e) => CoinDataDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
CoinDataDto _$CoinDataDtoFromJson(Map<String, dynamic> json) => CoinDataDto(
|
||||
id: json['id'] as String?,
|
||||
name: json['name'] as String?,
|
||||
image: json['image'] as String?,
|
||||
currentPrice: (json['currentPrice'] as num?)?.toDouble(),
|
||||
priceChange24h: (json['priceChange24h'] as num?)?.toDouble(),
|
||||
);
|
21
lib/data/dtos/search_coins_dto.dart
Normal file
21
lib/data/dtos/search_coins_dto.dart
Normal file
@ -0,0 +1,21 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'search_coins_dto.g.dart';
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class SearchCoinsDto {
|
||||
final List<SearchCoinDto>? coins;
|
||||
|
||||
const SearchCoinsDto({this.coins});
|
||||
|
||||
factory SearchCoinsDto.fromJson(Map<String, dynamic> json) => _$SearchCoinsDtoFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class SearchCoinDto {
|
||||
final String? id;
|
||||
|
||||
const SearchCoinDto({this.id});
|
||||
|
||||
factory SearchCoinDto.fromJson(Map<String, dynamic> json) => _$SearchCoinDtoFromJson(json);
|
||||
}
|
19
lib/data/dtos/search_coins_dto.g.dart
Normal file
19
lib/data/dtos/search_coins_dto.g.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'search_coins_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
SearchCoinsDto _$SearchCoinsDtoFromJson(Map<String, dynamic> json) =>
|
||||
SearchCoinsDto(
|
||||
coins: (json['coins'] as List<dynamic>?)
|
||||
?.map((e) => SearchCoinDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
SearchCoinDto _$SearchCoinDtoFromJson(Map<String, dynamic> json) =>
|
||||
SearchCoinDto(
|
||||
id: json['id'] as String?,
|
||||
);
|
@ -5,6 +5,7 @@ import '../../domain/models/home.dart';
|
||||
|
||||
const _imagePlaceholder = 'https://gryazoveckij-r19.gosweb.gosuslugi.ru/netcat_files/460/2008/net_foto_muzh.jpg';
|
||||
|
||||
/*
|
||||
extension CharactersDataDto on CharactersDto {
|
||||
HomeData toDomain() => HomeData(
|
||||
data: data?.map((e) => e.toDomain()).toList(),
|
||||
@ -30,3 +31,4 @@ String _makeDescriptionText(String? born, String? died) {
|
||||
? 'died: $died'
|
||||
: '';
|
||||
}
|
||||
*/
|
||||
|
47
lib/data/mappers/crypto_mapper.dart
Normal file
47
lib/data/mappers/crypto_mapper.dart
Normal file
@ -0,0 +1,47 @@
|
||||
import 'package:flutter_android_app/components/locale/l10n/app_localizations.dart';
|
||||
import 'package:flutter_android_app/data/dtos/coins_dto.dart';
|
||||
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(
|
||||
id: id ?? 'UNKNOWN',
|
||||
title: name ?? 'UNKNOWN',
|
||||
imageUrl: image,
|
||||
currentPrice: _getLocalizedPrice(currentPrice, locale?.localeName),
|
||||
priceChange: _getLocalizedPriceChange(priceChange24h, locale),
|
||||
);
|
||||
|
||||
String _getLocalizedPrice(double? price, String? localeName) {
|
||||
if (localeName == null) {
|
||||
return '$price \$';
|
||||
}
|
||||
|
||||
return switch (localeName) {
|
||||
'ru' => '$price ₽',
|
||||
_ => '$price \$',
|
||||
};
|
||||
}
|
||||
|
||||
String _getLocalizedPriceChange(double? priceChange, AppLocale? locale) {
|
||||
if (priceChange == null) {
|
||||
return '+${_getLocalizedPrice(0, locale?.localeName)}';
|
||||
}
|
||||
|
||||
String retVal = '';
|
||||
if (priceChange >= 0.0) {
|
||||
retVal += '+';
|
||||
}
|
||||
|
||||
retVal += _getLocalizedPrice(priceChange, locale?.localeName);
|
||||
|
||||
return '$retVal за последние 24 часа';
|
||||
}
|
||||
}
|
||||
|
||||
extension CoinsDtoToModel on CoinsDto {
|
||||
HomeData toDomain(AppLocale? locale) => HomeData(
|
||||
data: coins?.map((e) => e.toDomain(locale)).toList(),
|
||||
nextPage: 1,
|
||||
);
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CardData {
|
||||
final String id;
|
||||
final String title;
|
||||
final String description;
|
||||
final IconData icon;
|
||||
final String? imageUrl;
|
||||
final String? id;
|
||||
final String currentPrice;
|
||||
final String priceChange;
|
||||
|
||||
CardData({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.description,
|
||||
this.icon = Icons.adb,
|
||||
this.imageUrl,
|
||||
this.id,
|
||||
required this.currentPrice,
|
||||
required this.priceChange,
|
||||
});
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter_android_app/presentation/home_page/home_page.dart';
|
||||
import 'package:flutter_android_app/presentation/like_bloc/like_bloc.dart';
|
||||
import 'package:flutter_android_app/presentation/locale_bloc/locale_bloc.dart';
|
||||
import 'package:flutter_android_app/presentation/locale_bloc/locale_state.dart';
|
||||
import 'package:flutter_android_app/repositories/potter_repository.dart';
|
||||
import 'package:flutter_android_app/repositories/mock_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'components/locale/l10n/app_localizations.dart';
|
||||
@ -28,21 +28,22 @@ class MyApp extends StatelessWidget {
|
||||
create: (context) => LocaleBloc(Locale(Platform.localeName)),
|
||||
child: BlocBuilder<LocaleBloc, LocaleState>(
|
||||
builder: (context, state) => MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
title: 'Cryptocurrency Exchange App',
|
||||
locale: state.currentLocale,
|
||||
localizationsDelegates: AppLocale.localizationsDelegates,
|
||||
supportedLocales: AppLocale.supportedLocales,
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigoAccent),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: RepositoryProvider<PotterRepository>(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: RepositoryProvider<MockRepository>(
|
||||
lazy: true,
|
||||
create: (_) => PotterRepository(),
|
||||
create: (_) => MockRepository(),
|
||||
child: BlocProvider<HomeBloc>(
|
||||
lazy: false,
|
||||
create: (context) => HomeBloc(context.read<PotterRepository>()),
|
||||
child: const MyHomePage(title: 'Harry Potter characters'),
|
||||
create: (context) => HomeBloc(context.read<MockRepository>()),
|
||||
child: const MyHomePage(title: 'Cryptocurrency Exchange'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -21,7 +21,7 @@ class DetailsPage extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Text(data.title, style: Theme.of(context).textTheme.headlineLarge),
|
||||
),
|
||||
Text(data.description, style: Theme.of(context).textTheme.bodyLarge),
|
||||
Text(data.currentPrice, style: Theme.of(context).textTheme.bodyLarge),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -1,10 +1,10 @@
|
||||
import 'package:flutter_android_app/presentation/home_page/bloc/events.dart';
|
||||
import 'package:flutter_android_app/presentation/home_page/bloc/state.dart';
|
||||
import 'package:flutter_android_app/repositories/potter_repository.dart';
|
||||
import 'package:flutter_android_app/repositories/api_interface.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
final PotterRepository repo;
|
||||
final ApiInterface repo;
|
||||
|
||||
HomeBloc(this.repo) : super(const HomeState()) {
|
||||
on<HomeLoadDataEvent>(_onLoadData);
|
||||
@ -20,7 +20,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
String? error;
|
||||
|
||||
final data = await repo.loadData(
|
||||
q: event.search,
|
||||
search: event.search,
|
||||
page: event.nextPage ?? 1,
|
||||
onError: (e) => error = e,
|
||||
);
|
||||
|
@ -3,26 +3,26 @@ part of 'home_page.dart';
|
||||
typedef OnLikeCallback = void Function(String? id, String title, bool isLiked)?;
|
||||
|
||||
class _Card extends StatelessWidget {
|
||||
final AppLocale locale;
|
||||
final String id;
|
||||
final String title;
|
||||
final String description;
|
||||
final IconData icon;
|
||||
final String? imageUrl;
|
||||
final String currentPrice;
|
||||
final String priceChange;
|
||||
final AppLocale locale;
|
||||
final OnLikeCallback onLike;
|
||||
final VoidCallback? onTap;
|
||||
final String? id;
|
||||
final bool isLiked;
|
||||
|
||||
const _Card({
|
||||
super.key,
|
||||
required this.locale,
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.description,
|
||||
this.icon = Icons.hail,
|
||||
this.imageUrl,
|
||||
required this.currentPrice,
|
||||
required this.priceChange,
|
||||
required this.locale,
|
||||
this.onLike,
|
||||
this.onTap,
|
||||
this.id,
|
||||
this.isLiked = false,
|
||||
});
|
||||
|
||||
@ -33,15 +33,15 @@ class _Card extends StatelessWidget {
|
||||
VoidCallback? onTap,
|
||||
bool isLiked = false,
|
||||
}) => _Card(
|
||||
locale: locale,
|
||||
id: data.id,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
icon: data.icon,
|
||||
imageUrl: data.imageUrl,
|
||||
currentPrice: data.currentPrice,
|
||||
priceChange: data.priceChange,
|
||||
locale: locale,
|
||||
onLike: onLike,
|
||||
onTap: onTap,
|
||||
isLiked: isLiked,
|
||||
id: data.id,
|
||||
);
|
||||
|
||||
@override
|
||||
@ -52,7 +52,7 @@ class _Card extends StatelessWidget {
|
||||
margin: const EdgeInsets.fromLTRB(20, 8, 20, 8),
|
||||
constraints: const BoxConstraints(minHeight: 140),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.deepPurple.shade200,
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
@ -94,7 +94,7 @@ class _Card extends StatelessWidget {
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
currentPrice,
|
||||
style: Theme.of(context).textTheme.bodyLarge),
|
||||
],
|
||||
),
|
||||
|
@ -17,25 +17,33 @@ import '../like_bloc/like_events.dart';
|
||||
import '../locale_bloc/locale_bloc.dart';
|
||||
import '../locale_bloc/locale_events.dart';
|
||||
import '../locale_bloc/locale_state.dart';
|
||||
import '../settings_page/settings_page.dart';
|
||||
|
||||
part 'card.dart';
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
class MyHomePage extends StatelessWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.deepPurple.shade200,
|
||||
title: Text(widget.title),
|
||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||
title: Text(title),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12.0),
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () => Navigator.push(context, MaterialPageRoute<void>(
|
||||
builder: (BuildContext context) => const SettingsPage(),
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
),
|
||||
body: const Body(),
|
||||
);
|
||||
@ -79,29 +87,26 @@ class _BodyState extends State<Body> {
|
||||
flex: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: CupertinoSearchTextField(
|
||||
child: SearchBar(
|
||||
controller: searchController,
|
||||
onChanged: (search) {
|
||||
Debounce.run(() => context.read<HomeBloc>().add(HomeLoadDataEvent(search: search)));
|
||||
},
|
||||
leading: const Icon(Icons.search),
|
||||
trailing: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () { searchController.clear(); },
|
||||
),
|
||||
],
|
||||
hintText: context.locale.search,
|
||||
elevation: const WidgetStatePropertyAll(0.0),
|
||||
padding: const WidgetStatePropertyAll(EdgeInsets.only(left: 18, right: 10)),
|
||||
backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.secondaryContainer),
|
||||
),
|
||||
),
|
||||
),
|
||||
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 SvgUs();
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
BlocBuilder<HomeBloc, HomeState>(
|
||||
|
@ -3,12 +3,12 @@ import 'like_events.dart';
|
||||
import 'like_state.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
const String _likedPrefsKey = 'liked';
|
||||
|
||||
class LikeBloc extends Bloc<LikeEvent, LikeState> {
|
||||
static const String _likedPrefsKey = 'liked';
|
||||
|
||||
LikeBloc() : super(const LikeState(likedIds: [])) {
|
||||
on<ChangeLikeEvent>(_onChangeLike);
|
||||
on<LoadLikesEvent>(_onLoadLikes);
|
||||
on<ChangeLikeEvent>(_onChangeLike);
|
||||
}
|
||||
|
||||
Future<void> _onLoadLikes(
|
||||
|
@ -8,5 +8,6 @@ class LoadLikesEvent extends LikeEvent {
|
||||
|
||||
class ChangeLikeEvent extends LikeEvent {
|
||||
final String id;
|
||||
|
||||
const ChangeLikeEvent(this.id);
|
||||
}
|
||||
|
35
lib/presentation/settings_page/settings_page.dart
Normal file
35
lib/presentation/settings_page/settings_page.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../common/svg_objects.dart';
|
||||
import '../locale_bloc/locale_bloc.dart';
|
||||
import '../locale_bloc/locale_events.dart';
|
||||
import '../locale_bloc/locale_state.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Settings')),
|
||||
body: Center(
|
||||
child: 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 SvgUs();
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,7 +1,14 @@
|
||||
import 'package:flutter_android_app/domain/models/home.dart';
|
||||
|
||||
import '../components/locale/l10n/app_localizations.dart';
|
||||
import '../components/utils/error_callback.dart';
|
||||
|
||||
abstract class ApiInterface {
|
||||
Future<HomeData?> loadData({OnErrorCallback? onError});
|
||||
Future<HomeData?> loadData({
|
||||
OnErrorCallback? onError,
|
||||
String? search,
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
AppLocale? locale,
|
||||
});
|
||||
}
|
||||
|
84
lib/repositories/crypto_repository.dart
Normal file
84
lib/repositories/crypto_repository.dart
Normal file
@ -0,0 +1,84 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_android_app/components/locale/l10n/app_localizations.dart';
|
||||
import 'package:flutter_android_app/components/utils/error_callback.dart';
|
||||
import 'package:flutter_android_app/data/dtos/coins_dto.dart';
|
||||
import 'package:flutter_android_app/data/dtos/search_coins_dto.dart';
|
||||
import 'package:flutter_android_app/data/mappers/crypto_mapper.dart';
|
||||
import 'package:flutter_android_app/repositories/api_interface.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
|
||||
import '../domain/models/home.dart';
|
||||
|
||||
class CryptoRepository extends ApiInterface {
|
||||
static const String _baseUrl = 'https://api.coingecko.com/api/v3';
|
||||
static const String _searchUrl = '/search';
|
||||
static const String _coinsDataUrl = '/coins/markets';
|
||||
|
||||
static const String _apiKey = 'CG-oer6F3AAhVpNxGDxc7mjzZCo';
|
||||
|
||||
static final Dio _dio = Dio()
|
||||
..interceptors.add(PrettyDioLogger(
|
||||
requestBody: true,
|
||||
requestHeader: true,
|
||||
));
|
||||
|
||||
@override
|
||||
Future<HomeData?> loadData({
|
||||
OnErrorCallback? onError,
|
||||
String? search,
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
AppLocale? locale,
|
||||
}) async {
|
||||
try {
|
||||
Map<String, dynamic> queryParams = {
|
||||
'vs_currency': _getCurrencyName(locale?.localeName),
|
||||
'per_page': pageSize,
|
||||
'page': page,
|
||||
};
|
||||
|
||||
if (search != null) {
|
||||
final Response<dynamic> searchResponse = await _dio.get<Map<dynamic, dynamic>>(
|
||||
'$_baseUrl$_searchUrl',
|
||||
queryParameters: {
|
||||
'x_cg_demo_api_key': _apiKey,
|
||||
'query': search,
|
||||
}
|
||||
);
|
||||
|
||||
final SearchCoinsDto searchCoinsDto = SearchCoinsDto.fromJson(searchResponse.data as Map<String, dynamic>);
|
||||
if (searchCoinsDto.coins != null) {
|
||||
String ids = '';
|
||||
for (var coinData in searchCoinsDto.coins!) {
|
||||
ids += coinData.id != null ? '${coinData.id},' : '';
|
||||
}
|
||||
queryParams['ids'] = ids;
|
||||
}
|
||||
}
|
||||
|
||||
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
|
||||
'$_baseUrl$_coinsDataUrl',
|
||||
queryParameters: queryParams,
|
||||
);
|
||||
|
||||
final CoinsDto dto = CoinsDto.fromJson(response.data as Map<String, dynamic>);
|
||||
final HomeData data = dto.toDomain(locale);
|
||||
return data;
|
||||
|
||||
} on DioException catch (e) {
|
||||
onError?.call(e.error?.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String _getCurrencyName(String? localeName) {
|
||||
if (localeName == null) {
|
||||
return 'usd';
|
||||
}
|
||||
|
||||
return switch (localeName) {
|
||||
'ru' => 'rub',
|
||||
_ => 'usd',
|
||||
};
|
||||
}
|
||||
}
|
@ -1,29 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_android_app/domain/models/card.dart';
|
||||
import 'package:flutter_android_app/repositories/api_interface.dart';
|
||||
|
||||
import '../components/locale/l10n/app_localizations.dart';
|
||||
import '../components/utils/error_callback.dart';
|
||||
import '../domain/models/home.dart';
|
||||
|
||||
class MockRepository extends ApiInterface {
|
||||
@override
|
||||
Future<HomeData?> loadData({OnErrorCallback? onError}) async {
|
||||
Future<HomeData?> loadData({
|
||||
OnErrorCallback? onError,
|
||||
String? search,
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
AppLocale? locale,
|
||||
}) async {
|
||||
return HomeData(data: [
|
||||
CardData(
|
||||
title: 'Title 1',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
||||
imageUrl: 'https://i.imgur.com/a9WA68S.png',
|
||||
id: 'bitcoin',
|
||||
title: 'Bitcoin',
|
||||
imageUrl: 'https://coin-images.coingecko.com/coins/images/1/large/bitcoin.png?1696501400',
|
||||
currentPrice: '103233 \$',
|
||||
priceChange: '+2207.71 \$ for the last 24 hours',
|
||||
),
|
||||
CardData(
|
||||
title: 'Title 2',
|
||||
description: 'Lorem ipsum dolor sit amet',
|
||||
icon: Icons.add_chart_outlined,
|
||||
imageUrl: 'https://i.imgur.com/dAUcs6I.png',
|
||||
id: 'ethereum',
|
||||
title: 'Ethereum',
|
||||
imageUrl: 'https://coin-images.coingecko.com/coins/images/279/large/ethereum.png?1696501628',
|
||||
currentPrice: '3900.92 \$',
|
||||
priceChange: '+58.27 \$ for the last 24 hours',
|
||||
),
|
||||
CardData(
|
||||
title: 'Title 3',
|
||||
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor',
|
||||
imageUrl: 'https://i.imgur.com/m2FhVAK.png',
|
||||
id: 'tether',
|
||||
title: 'Tether',
|
||||
imageUrl: 'https://coin-images.coingecko.com/coins/images/325/large/Tether.png?1696501661',
|
||||
currentPrice: '1.001 \$',
|
||||
priceChange: '+0.00059798 \$ for the last 24 hours',
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter_android_app/repositories/api_interface.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
|
||||
import '../components/utils/error_callback.dart';
|
||||
|
||||
/*
|
||||
class PotterRepository extends ApiInterface {
|
||||
static final Dio _dio = Dio()
|
||||
..interceptors.add(PrettyDioLogger(
|
||||
@ -43,3 +43,4 @@ class PotterRepository extends ApiInterface {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user