все переделал

This commit is contained in:
dyakonovr 2024-09-17 00:03:15 +04:00
parent 75f40b4a06
commit dfa87e8488
11 changed files with 174 additions and 95 deletions

View File

@ -5,13 +5,31 @@ import 'package:json_annotation/json_annotation.dart';
part 'characters_dto.g.dart';
@JsonSerializable(createToJson: false)
class CharacterDataDto {
final String? title;
final double? price;
@JsonKey(name: "image")
final String? imageUrl;
class CharactersDto {
final List<CharacterDataDto>? data;
const CharacterDataDto(this.title, this.price, this.imageUrl);
const CharactersDto({ this.data });
factory CharactersDto.fromJson(Map<String, dynamic> json) => _$CharactersDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
class CharacterDataDto {
final String? id;
final CharacterAttributesDataDto? attributes;
const CharacterDataDto(this.id, this.attributes);
factory CharacterDataDto.fromJson(Map<String, dynamic> json) => _$CharacterDataDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
class CharacterAttributesDataDto {
final String? name;
final String? image;
final String? species;
CharacterAttributesDataDto(this.name, this.image, this.species);
factory CharacterAttributesDataDto.fromJson(Map<String, dynamic> json) => _$CharacterAttributesDataDtoFromJson(json);
}

View File

@ -6,9 +6,26 @@ part of 'characters_dto.dart';
// JsonSerializableGenerator
// **************************************************************************
CharactersDto _$CharactersDtoFromJson(Map<String, dynamic> json) =>
CharactersDto(
data: (json['data'] as List<dynamic>?)
?.map((e) => CharacterDataDto.fromJson(e as Map<String, dynamic>))
.toList(),
);
CharacterDataDto _$CharacterDataDtoFromJson(Map<String, dynamic> json) =>
CharacterDataDto(
json['title'] as String?,
(json['price'] as num?)?.toDouble(),
json['image'] as String?,
json['id'] as String?,
json['attributes'] == null
? null
: CharacterAttributesDataDto.fromJson(
json['attributes'] as Map<String, dynamic>),
);
CharacterAttributesDataDto _$CharacterAttributesDataDtoFromJson(
Map<String, dynamic> json) =>
CharacterAttributesDataDto(
json['name'] as String?,
json['image'] as String?,
json['species'] as String?,
);

View File

@ -3,5 +3,8 @@ import 'package:lab/domain/models/card_data.dart';
extension CharacterDataDtoToModel on CharacterDataDto {
CardData toDomain() => CardData(
name: title ?? "UNKNOWN", price: price ?? 0.0, imageUrl: imageUrl ?? "");
name: attributes?.name ?? "UNKNOWN",
image: attributes?.image ??
"https://upload.wikimedia.org/wikipedia/commons/a/a2/Person_Image_Placeholder.png",
species: attributes?.species ?? "UNKNOWN");
}

View File

@ -1,7 +1,7 @@
class CardData {
final String name;
final double price;
final String imageUrl;
final String image;
final String species;
CardData({required this.name, required this.price, required this.imageUrl});
CardData({required this.name, required this.image, required this.species});
}

View File

@ -10,7 +10,7 @@ class DetailsPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Product ${data.name}'),
title: Text('Character ${data.name}'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(30),
@ -19,7 +19,7 @@ class DetailsPage extends StatelessWidget {
children: [
Center(
child: Image.network(
data.imageUrl,
data.image,
width: 250,
height: 250,
),
@ -31,7 +31,7 @@ class DetailsPage extends StatelessWidget {
),
const SizedBox(height: 20),
Text(
"Цена: ${data.price} Руб",
"Species: ${data.species}",
style: const TextStyle(fontSize: 25, color: Colors.orange),
),
],

View File

@ -4,16 +4,16 @@ typedef OnLikeFunction = void Function(String text);
class _Card extends StatefulWidget {
final String name;
final double price;
final String imageUrl;
final String image;
final String species;
final OnLikeFunction? onLike;
final VoidCallback? onTap;
const _Card(
{required this.name,
required this.price,
required this.imageUrl,
required this.species,
required this.image,
this.onLike,
this.onTap});
@ -21,8 +21,8 @@ class _Card extends StatefulWidget {
CardData data, OnLikeFunction? onLike, VoidCallback? onTap) =>
_Card(
name: data.name,
price: data.price,
imageUrl: data.imageUrl,
image: data.image,
species: data.species,
onTap: onTap,
onLike: onLike,
);
@ -39,8 +39,8 @@ class _CardState extends State<_Card> {
_isFavourite = !_isFavourite;
});
widget.onLike?.call(_isFavourite
? "Product added in favourite"
: "Product deleted from favourite");
? "Card added in favourite"
: "Card deleted from favourite");
}
@override
@ -81,7 +81,11 @@ class _CardState extends State<_Card> {
),
const SizedBox(height: 20),
Center(
child: Image.network(widget.imageUrl, width: 250, height: 250,),
child: Image.network(
widget.image,
width: 250,
height: 250,
),
),
Padding(
padding: const EdgeInsets.only(top: 50),
@ -89,7 +93,7 @@ class _CardState extends State<_Card> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.name, style: const TextStyle(fontSize: 17)),
Text("${widget.price} Руб",
Text("Species: ${widget.species}",
style:
const TextStyle(fontSize: 17, color: Colors.orange))
],

View File

@ -2,7 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:lab/domain/models/card_data.dart';
import 'package:lab/presentation/details_page/details_page.dart';
import 'package:lab/repositories/shop_repository.dart';
import 'package:lab/repositories/api_interface.dart';
import 'package:lab/repositories/potter_repository.dart';
part 'card.dart';
@ -16,6 +17,16 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _searchController = TextEditingController();
final ApiInterface repo = PotterRepository();
late Future<List<CardData>?> _data;
@override
void initState() {
super.initState();
_data = repo.loadData(null);
}
void _showSnackBar(BuildContext context, String text) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
@ -35,36 +46,59 @@ class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
final data = ShopRepository().loadData();
// final d = repo.loadData(null);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: FutureBuilder<List<CardData>?>(
future: data,
builder: (context, snapshot) => SingleChildScrollView(
padding: const EdgeInsets.all(30),
child: snapshot.hasData
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: snapshot.data?.map((cardData) {
return Column(
children: [
_Card.fromData(
cardData,
(String text) => _showSnackBar(context, text),
() => _navigateToDetailsPage(context, cardData),
),
const SizedBox(height: 20)
],
);
}).toList() ??
[],
)
: const CircularProgressIndicator(),
),
body: ListView(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(right: 30, left: 30, top: 20),
child: CupertinoSearchTextField(
controller: _searchController,
onChanged: (search) {
setState(() {
_data = repo.loadData(search);
});
},
),
),
FutureBuilder<List<CardData>?>(
future: _data,
builder: (context, snapshot) => SingleChildScrollView(
padding: const EdgeInsets.symmetric(
horizontal: 30,
vertical: 15,
),
child: snapshot.hasData
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: snapshot.data?.map((cardData) {
return Column(
children: [
const SizedBox(height: 20),
_Card.fromData(
cardData,
(String text) =>
_showSnackBar(context, text),
() => _navigateToDetailsPage(
context, cardData),
),
const SizedBox(height: 20)
],
);
}).toList() ??
[])
: const CircularProgressIndicator(),
),
),
],
)
],
),
);
}

View File

@ -1,5 +1,5 @@
import 'package:lab/domain/models/card_data.dart';
abstract class ApiInterface {
Future<List<CardData>?> loadData();
Future<List<CardData>?> loadData(String? q);
}

View File

@ -3,17 +3,17 @@ import 'package:lab/repositories/api_interface.dart';
class MockRepository extends ApiInterface {
@override
Future<List<CardData>> loadData() async {
Future<List<CardData>> loadData(String? q) async {
return [
CardData(
name: "test 0",
price: 11.0,
imageUrl:
species: "Species 0",
image:
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTtAT11wKgHrJBUYzIBFogucXg0a9fE0fQXDQ&s"),
CardData(
name: "test 1",
price: 22.0,
imageUrl:
species: "Species 1",
image:
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTtAT11wKgHrJBUYzIBFogucXg0a9fE0fQXDQ&s")
];
}

View File

@ -0,0 +1,40 @@
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:lab/data/dtos/characters_dto.dart';
import 'package:lab/data/mappers/characters_mapper.dart';
import 'package:lab/domain/models/card_data.dart';
import 'package:lab/repositories/api_interface.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class PotterRepository extends ApiInterface {
static final Dio _dio = Dio()
..interceptors.add(
PrettyDioLogger(request: true, requestHeader: true, requestBody: true));
static const String _baseUrl = "https://api.potterdb.com/v1";
@override
Future<List<CardData>?> loadData(String? q) async {
try {
const String url = '$_baseUrl/characters?page[size]=5';
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
url,
queryParameters: q != null ? {"filter[name_cont]": q} : null,
);
final CharactersDto dto =
CharactersDto.fromJson(response.data as Map<String, dynamic>);
final List<CardData>? data = dto.data?.map((e) => e.toDomain()).toList();
return data;
} on DioException catch (e) {
log("DioException: $e");
return null;
} catch (e) {
log('Unknown error: $e');
return null;
}
}
}

View File

@ -1,37 +0,0 @@
import 'dart:developer';
import 'package:dio/dio.dart';
import 'package:lab/data/dtos/characters_dto.dart';
import 'package:lab/data/mappers/characters_mapper.dart';
import 'package:lab/domain/models/card_data.dart';
import 'package:lab/repositories/api_interface.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class ShopRepository extends ApiInterface {
static final Dio _dio = Dio()
..interceptors.add(PrettyDioLogger(requestHeader: true, requestBody: true));
static const String _baseUrl = "https://fakestoreapi.com";
@override
Future<List<CardData>?> loadData() async {
try {
const String url = '$_baseUrl/products?limit=5';
final Response<dynamic> response = await _dio.get<List<dynamic>>(url);
final List<CharacterDataDto> dto = (response.data as List)
.map((item) => CharacterDataDto.fromJson(item as Map<String, dynamic>))
.toList();
final List<CardData> data = dto.map((e) => e.toDomain()).toList();
return data;
} on DioException catch (e) {
log("DioException: $e");
return null;
} catch (e) {
log('Unknown error: $e');
return null;
}
}
}