все переделал
This commit is contained in:
parent
75f40b4a06
commit
dfa87e8488
@ -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);
|
||||
}
|
@ -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?,
|
||||
);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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});
|
||||
}
|
||||
|
@ -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),
|
||||
),
|
||||
],
|
||||
|
@ -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))
|
||||
],
|
||||
|
@ -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(),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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")
|
||||
];
|
||||
}
|
||||
|
40
lib/repositories/potter_repository.dart
Normal file
40
lib/repositories/potter_repository.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user