laba5_complited_lab
This commit is contained in:
parent
25e205f450
commit
01a545e99d
19
lib/data/dtos/bars_dto.dart
Normal file
19
lib/data/dtos/bars_dto.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
part 'bars_dto.g.dart';
|
||||
@JsonSerializable(createToJson: false)
|
||||
class BarsDto {
|
||||
final List<BarDataDto>? data;
|
||||
|
||||
const BarsDto({this.data});
|
||||
factory BarsDto.fromJson(Map<String, dynamic> json) => _$BarsDtoFromJson(json);
|
||||
}
|
||||
@JsonSerializable(createToJson: false)
|
||||
class BarDataDto {
|
||||
final String? coctailId;
|
||||
final String? coctailName;
|
||||
final String? coctailImage;
|
||||
|
||||
|
||||
const BarDataDto({this.coctailId, this.coctailName, this.coctailImage});
|
||||
factory BarDataDto.fromJson(Map<String, dynamic> json) => _$BarDataDtoFromJson(json);
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
part 'characters_dto.g.dart';
|
||||
@JsonSerializable(createToJson: false)
|
||||
class CharactersDto {
|
||||
final List<CharacterDataDto>? data;
|
||||
|
||||
const CharactersDto({this.data});
|
||||
factory CharactersDto.fromJson(Map<String, dynamic> json) => _$CharactersDtoFromJson(json);
|
||||
}
|
||||
@JsonSerializable(createToJson: false)
|
||||
class CharacterDataDto {
|
||||
final String? id;
|
||||
final String? type;
|
||||
final CharacterAttributesDataDto? attributes;
|
||||
|
||||
const CharacterDataDto({this.id, this.type, this.attributes});
|
||||
factory CharacterDataDto.fromJson(Map<String, dynamic> json) => _$CharacterDataDtoFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class CharacterAttributesDataDto{
|
||||
final String? name;
|
||||
final String? born;
|
||||
final String? died;
|
||||
final String? image;
|
||||
|
||||
const CharacterAttributesDataDto({this.name, this.born, this.died, this.image});
|
||||
factory CharacterAttributesDataDto.fromJson(Map<String, dynamic> json) => _$CharacterAttributesDataDtoFromJson(json);
|
||||
|
||||
}
|
10
lib/data/mappers/bars_mapper.dart
Normal file
10
lib/data/mappers/bars_mapper.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:mobile_app/data/dtos/bars_dto.dart';
|
||||
import 'package:mobile_app/domain/models/card.dart';
|
||||
|
||||
extension BarDataDtoToModel on BarDataDto{
|
||||
CardData toDomain() => CardData(
|
||||
coctailName ?? 'UNKNOWN',
|
||||
coctailId: coctailId ?? 'UNKNOWN',
|
||||
coctailImage: coctailImage ?? 'UNKNOWN',
|
||||
);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import 'package:mobile_app/data/dtos/characters_dto.dart';
|
||||
import 'package:mobile_app/domain/models/card.dart';
|
||||
|
||||
extension CharacterDataDtoToModel on CharacterDataDto{
|
||||
CardData toDomain() => CardData(
|
||||
attributes?.name ?? 'UNKNOWN',
|
||||
imageUrl: attributes?.image,
|
||||
model: '${attributes?.born}-${attributes?.died}',
|
||||
);
|
||||
}
|
@ -2,19 +2,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
|
||||
class CardData {
|
||||
final String brand;
|
||||
final String model;
|
||||
// final String cost;
|
||||
// final IconData icon;
|
||||
// final String typeCar;
|
||||
final String? imageUrl;
|
||||
final String coctailName;
|
||||
final String coctailId;
|
||||
final String coctailImage;
|
||||
|
||||
CardData(
|
||||
this.brand, {
|
||||
required this.model,
|
||||
// required this.cost,
|
||||
// this.icon = FontAwesomeIcons.dollarSign,
|
||||
// required this.typeCar,
|
||||
this.imageUrl,
|
||||
this.coctailName, {
|
||||
required this.coctailId,
|
||||
required this.coctailImage
|
||||
});
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile_app/domain/models/card.dart';
|
||||
|
||||
import 'dart:math';
|
||||
class DetailsPage extends StatelessWidget {
|
||||
final CardData data;
|
||||
|
||||
const DetailsPage(this.data, {super.key});
|
||||
final double price;
|
||||
DetailsPage(this.data, {super.key}): price = 10 + Random().nextDouble() * 30;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -16,12 +16,12 @@ class DetailsPage extends StatelessWidget {
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Image.network(data.imageUrl ?? ' ',),
|
||||
child: Image.network(data.coctailImage ?? ' ',),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4.0),
|
||||
child: Text(
|
||||
data.brand,
|
||||
data.coctailName,
|
||||
style: Theme
|
||||
.of(context)
|
||||
.textTheme
|
||||
@ -29,12 +29,16 @@ class DetailsPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Text(
|
||||
data.model,
|
||||
'Id Coctail: ' + data.coctailId,
|
||||
style: Theme
|
||||
.of(context)
|
||||
.textTheme
|
||||
.bodyLarge,
|
||||
)
|
||||
.headlineMedium,
|
||||
),
|
||||
Text(
|
||||
'Cost: ' + price.toStringAsFixed(2),
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -3,22 +3,16 @@ part of 'home_page.dart';
|
||||
typedef OnLikeCallback = void Function(String title, bool isLiked)?;
|
||||
|
||||
class _Card extends StatefulWidget {
|
||||
final String brand;
|
||||
final String model;
|
||||
// final String cost;
|
||||
// final IconData icon;
|
||||
// final String typeCar;
|
||||
final String? imageUrl;
|
||||
final String coctailName;
|
||||
final String coctailId;
|
||||
final String? coctailImage;
|
||||
final OnLikeCallback onLike;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const _Card(
|
||||
this.brand,{
|
||||
required this.model,
|
||||
// required this.cost,
|
||||
// required this.typeCar,
|
||||
// this.icon = FontAwesomeIcons.dollarSign,
|
||||
this.imageUrl,
|
||||
this.coctailName,{
|
||||
required this.coctailId,
|
||||
this.coctailImage,
|
||||
this.onLike,
|
||||
this.onTap,
|
||||
});
|
||||
@ -26,16 +20,12 @@ class _Card extends StatefulWidget {
|
||||
OnLikeCallback onLike,
|
||||
VoidCallback? onTap,
|
||||
}) => _Card(
|
||||
data.brand,
|
||||
model: data.model,
|
||||
// cost: data.cost,
|
||||
// typeCar: data.typeCar,
|
||||
// icon: data.icon,
|
||||
imageUrl: data.imageUrl,
|
||||
data.coctailName,
|
||||
coctailId: data.coctailId,
|
||||
coctailImage: data.coctailImage,
|
||||
onLike: onLike,
|
||||
onTap: onTap,
|
||||
);
|
||||
|
||||
@override
|
||||
State<_Card> createState() => _CardState();
|
||||
}
|
||||
@ -43,7 +33,6 @@ class _Card extends StatefulWidget {
|
||||
|
||||
class _CardState extends State<_Card> {
|
||||
bool isLiked = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
@ -70,7 +59,7 @@ class _CardState extends State<_Card> {
|
||||
height: 300,
|
||||
width: 800,
|
||||
child: Image.network(
|
||||
widget.imageUrl ?? '',
|
||||
widget.coctailImage ?? '',
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => const Placeholder(),
|
||||
),
|
||||
@ -81,7 +70,7 @@ class _CardState extends State<_Card> {
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() => isLiked = !isLiked);
|
||||
widget.onLike?.call(widget.brand, isLiked);
|
||||
widget.onLike?.call(widget.coctailName, isLiked);
|
||||
},
|
||||
child: Icon(
|
||||
isLiked ? Icons.favorite : Icons.favorite_border,
|
||||
@ -94,37 +83,12 @@ class _CardState extends State<_Card> {
|
||||
),
|
||||
const SizedBox(height: 16), // Добавим отступ между Stack и текстовой информацией
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
widget.brand,
|
||||
widget.coctailName,
|
||||
style: Theme.of(context).textTheme.headlineLarge,
|
||||
),
|
||||
Text(
|
||||
'Model: ' + widget.model,
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
// Flexible(
|
||||
// child: Text(
|
||||
// 'Cost: ' + widget.cost,
|
||||
// style: Theme.of(context).textTheme.headlineMedium,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// maxLines: 1,
|
||||
// ),
|
||||
// ),
|
||||
const SizedBox(width: 4),
|
||||
// Icon(
|
||||
// widget.icon,
|
||||
// size: 24, // Размер иконки
|
||||
// ),
|
||||
],
|
||||
),
|
||||
// Text(
|
||||
// 'Type: ' + widget.typeCar,
|
||||
// style: Theme.of(context).textTheme.headlineMedium,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -6,6 +6,7 @@ import 'package:mobile_app/domain/models/card.dart';
|
||||
import 'package:mobile_app/presentation/details_page/details_page.dart';
|
||||
import 'package:mobile_app/repositories/mock_repository.dart';
|
||||
import 'package:mobile_app/repositories/potter_repository.dart';
|
||||
import 'package:mobile_app/repositories/coctel_repository.dart';
|
||||
|
||||
part 'card.dart';
|
||||
class MyHomePage extends StatefulWidget {
|
||||
@ -42,7 +43,7 @@ class Body extends StatefulWidget {
|
||||
class _BodyState extends State<Body> {
|
||||
late TextEditingController searchController;
|
||||
late Future<List<CardData>?> data;
|
||||
final PotterRepository repo = PotterRepository();
|
||||
final CocktailRepository repo = CocktailRepository();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -69,7 +70,7 @@ class _BodyState extends State<Body> {
|
||||
controller: searchController,
|
||||
onChanged: (search) {
|
||||
setState(() {
|
||||
data = repo.loadData(search); // Обновление данных при поиске
|
||||
data = repo.loadData(search.isNotEmpty ? search : null); // Обновление данных при поиске
|
||||
});
|
||||
},
|
||||
),
|
||||
|
39
lib/repositories/coctel_repository.dart
Normal file
39
lib/repositories/coctel_repository.dart
Normal file
@ -0,0 +1,39 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:mobile_app/domain/models/card.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
import 'api_interface.dart';
|
||||
|
||||
class CocktailRepository extends ApiInterface {
|
||||
static final Dio _dio = Dio()
|
||||
..interceptors.add(PrettyDioLogger(
|
||||
requestHeader: true,
|
||||
requestBody: true,
|
||||
));
|
||||
|
||||
static const String _baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1';
|
||||
|
||||
@override
|
||||
Future<List<CardData>?> loadData(String? q) async {
|
||||
try {
|
||||
final String url = q != null
|
||||
? '$_baseUrl/search.php?s=$q' // Поиск по названию
|
||||
: '$_baseUrl/filter.php?c=Cocktail'; // Получение списка коктейлей по категории
|
||||
|
||||
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(url);
|
||||
|
||||
if (response.data != null && response.data['drinks'] != null) {
|
||||
final List<CardData> data = (response.data['drinks'] as List).map((drink) {
|
||||
return CardData(
|
||||
drink['strDrink'], // Название коктейля
|
||||
coctailId: drink['idDrink'], // ID коктейля
|
||||
coctailImage: drink['strDrinkThumb'],
|
||||
);
|
||||
}).toList();
|
||||
return data;
|
||||
}
|
||||
return null;
|
||||
} on DioException catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,31 +1,31 @@
|
||||
import 'package:mobile_app/domain/models/card.dart';
|
||||
import 'package:mobile_app/repositories/api_interface.dart';
|
||||
|
||||
class MockRepository extends ApiInterface{
|
||||
@override
|
||||
Future<List<CardData>?> loadData(String? q) async {
|
||||
return[
|
||||
CardData(
|
||||
'Mercedes',
|
||||
model: 'glc',
|
||||
// cost: '30000,79',
|
||||
// typeCar: 'crossover',
|
||||
imageUrl: 'https://360view.3dmodels.org/zoom/Mercedes-Benz/Mercedes-Benz_GLC-class_Mk1f_X253_2019_1000_0001.jpg',
|
||||
),
|
||||
CardData(
|
||||
'Renault',
|
||||
model: 'Duster',
|
||||
// cost: '21455',
|
||||
// typeCar: 'Sedan',
|
||||
imageUrl: 'https://avatars.mds.yandex.net/get-verba/787013/2a000001609d3fab888af8565b7ff0fc8f13/cattouchret',
|
||||
),
|
||||
CardData(
|
||||
'Renault',
|
||||
model: 'Logan',
|
||||
// cost: '10000',
|
||||
// typeCar: 'Cov',
|
||||
imageUrl: 'https://avatars.mds.yandex.net/get-verba/787013/2a000001609c5392087e70561227a870e4bf/cattouchret',
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
// import 'package:mobile_app/domain/models/card.dart';
|
||||
// import 'package:mobile_app/repositories/api_interface.dart';
|
||||
//
|
||||
// class MockRepository extends ApiInterface{
|
||||
// @override
|
||||
// Future<List<CardData>?> loadData(String? q) async {
|
||||
// return[
|
||||
// CardData(
|
||||
// 'Mercedes',
|
||||
// type: 'glc',
|
||||
// // cost: '30000,79',
|
||||
// // typeCar: 'crossover',
|
||||
// imageUrl: 'https://360view.3dmodels.org/zoom/Mercedes-Benz/Mercedes-Benz_GLC-class_Mk1f_X253_2019_1000_0001.jpg',
|
||||
// ),
|
||||
// CardData(
|
||||
// 'Renault',
|
||||
// type: 'Duster',
|
||||
// // cost: '21455',
|
||||
// // typeCar: 'Sedan',
|
||||
// imageUrl: 'https://avatars.mds.yandex.net/get-verba/787013/2a000001609d3fab888af8565b7ff0fc8f13/cattouchret',
|
||||
// ),
|
||||
// CardData(
|
||||
// 'Renault',
|
||||
// type: 'Logan',
|
||||
// // cost: '10000',
|
||||
// // typeCar: 'Cov',
|
||||
// imageUrl: 'https://avatars.mds.yandex.net/get-verba/787013/2a000001609c5392087e70561227a870e4bf/cattouchret',
|
||||
// )
|
||||
// ];
|
||||
// }
|
||||
// }
|
@ -1,8 +1,8 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:mobile_app/data/mappers/characters_mapper.dart';
|
||||
import 'package:mobile_app/data/mappers/bars_mapper.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
|
||||
import '../data/dtos/characters_dto.dart';
|
||||
import '../data/dtos/bars_dto.dart';
|
||||
import '../domain/models/card.dart';
|
||||
import 'api_interface.dart';
|
||||
|
||||
@ -25,7 +25,7 @@ class PotterRepository extends ApiInterface {
|
||||
queryParameters: q != null ? {"filter[name_cont]": q} : null,
|
||||
);
|
||||
|
||||
final CharactersDto dto = CharactersDto.fromJson(
|
||||
final BarsDto dto = BarsDto.fromJson(
|
||||
response.data as Map<String, dynamic>);
|
||||
final List<CardData>? data = dto.data?.map((e) => e.toDomain()).toList();
|
||||
return data;
|
||||
|
Loading…
x
Reference in New Issue
Block a user