laba5_complited_lab

This commit is contained in:
Extrimal 2024-10-27 19:37:12 +04:00
parent 25e205f450
commit 01a545e99d
11 changed files with 136 additions and 145 deletions

View 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);
}

View File

@ -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);
}

View 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',
);
}

View File

@ -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}',
);
}

View File

@ -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
});
}

View File

@ -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,
),
],
),
);

View File

@ -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,
// ),
],
),
],

View File

@ -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); // Обновление данных при поиске
});
},
),

View 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;
}
}
}

View File

@ -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',
// )
// ];
// }
// }

View File

@ -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;