почти сделал 5 лабу
This commit is contained in:
parent
6310e327ca
commit
fe516de6e2
@ -3,27 +3,59 @@ import 'package:json_annotation/json_annotation.dart';
|
||||
part 'spells_dto.g.dart';
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class SpellDto{
|
||||
class SpellDto {
|
||||
final List<SpellDataDto>? data;
|
||||
|
||||
const SpellDto({
|
||||
this.data,
|
||||
});
|
||||
|
||||
factory SpellDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpellDtoFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class SpellDto{
|
||||
class SpellDataDto {
|
||||
final String? id;
|
||||
final String? type?;
|
||||
final CharacterAttributesDataDto? attributes;
|
||||
final String? type;
|
||||
final SpellAttributesDataDto? attributes;
|
||||
|
||||
const SpellDataDto({
|
||||
this.id,
|
||||
this.type,
|
||||
this.attributes,
|
||||
});
|
||||
|
||||
factory SpellDataDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpellDataDtoFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable(createToJson: false)
|
||||
class CharacterAttributesDataDto{
|
||||
final String? category;
|
||||
final String? incantation;
|
||||
final String? name;
|
||||
final String? image;
|
||||
class SpellAttributesDataDto {
|
||||
final String? slug; // хз
|
||||
final String? category; // категория
|
||||
final String? creator; // создатель
|
||||
final String? effect; // воздействие
|
||||
final String? hand; // взмах
|
||||
final String? image; // картинка
|
||||
final String? incantation; // произношение
|
||||
final String? light; // цвет
|
||||
final String? name; // название
|
||||
final String? wiki; // ссылка на вики
|
||||
|
||||
const SpellAttributesDataDto({
|
||||
this.slug,
|
||||
this.category,
|
||||
this.creator,
|
||||
this.effect,
|
||||
this.hand,
|
||||
this.image,
|
||||
this.incantation,
|
||||
this.light,
|
||||
this.name,
|
||||
this.wiki,
|
||||
});
|
||||
|
||||
}
|
||||
factory SpellAttributesDataDto.fromJson(Map<String, dynamic> json) =>
|
||||
_$SpellAttributesDataDtoFromJson(json);
|
||||
}
|
||||
|
37
lib/data/dtos/spells_dto.g.dart
Normal file
37
lib/data/dtos/spells_dto.g.dart
Normal file
@ -0,0 +1,37 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'spells_dto.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
SpellDto _$SpellDtoFromJson(Map<String, dynamic> json) => SpellDto(
|
||||
data: (json['data'] as List<dynamic>?)
|
||||
?.map((e) => SpellDataDto.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
SpellDataDto _$SpellDataDtoFromJson(Map<String, dynamic> json) => SpellDataDto(
|
||||
id: json['id'] as String?,
|
||||
type: json['type'] as String?,
|
||||
attributes: json['attributes'] == null
|
||||
? null
|
||||
: SpellAttributesDataDto.fromJson(
|
||||
json['attributes'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
SpellAttributesDataDto _$SpellAttributesDataDtoFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
SpellAttributesDataDto(
|
||||
slug: json['slug'] as String?,
|
||||
category: json['category'] as String?,
|
||||
creator: json['creator'] as String?,
|
||||
effect: json['effect'] as String?,
|
||||
hand: json['hand'] as String?,
|
||||
image: json['image'] as String?,
|
||||
incantation: json['incantation'] as String?,
|
||||
light: json['light'] as String?,
|
||||
name: json['name'] as String?,
|
||||
wiki: json['wiki'] as String?,
|
||||
);
|
12
lib/data/mappers/spells_mapper.dart
Normal file
12
lib/data/mappers/spells_mapper.dart
Normal file
@ -0,0 +1,12 @@
|
||||
import 'package:laba1/domain/models/card.dart';
|
||||
|
||||
import '../dtos/spells_dto.dart';
|
||||
|
||||
const _imagePlaceHolder ='https://cdn-icons-png.flaticon.com/512/1277/1277244.png';
|
||||
|
||||
extension SpellsDataDtoToModel on SpellDataDto {
|
||||
CardData toDomain() => CardData(
|
||||
attributes?.name ?? 'UNKNOWN',
|
||||
imageUrl: attributes?.image ?? _imagePlaceHolder,
|
||||
);
|
||||
}
|
7
lib/data/repositories/api_interface.dart
Normal file
7
lib/data/repositories/api_interface.dart
Normal file
@ -0,0 +1,7 @@
|
||||
import '../../domain/models/card.dart';
|
||||
|
||||
typedef OnErrorCallback = void Function(String? error);
|
||||
|
||||
abstract class ApiInterface {
|
||||
Future<List<CardData>?> loadData({OnErrorCallback? onError});
|
||||
}
|
25
lib/data/repositories/mock_repository.dart
Normal file
25
lib/data/repositories/mock_repository.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import '../../domain/models/card.dart';
|
||||
import 'api_interface.dart';
|
||||
|
||||
class MockRepository extends ApiInterface {
|
||||
@override
|
||||
Future<List<CardData>?> loadData({OnErrorCallback? onError}) async {
|
||||
return [
|
||||
CardData('orange',
|
||||
imageUrl:
|
||||
'https://kuban24.tv/wp-content/uploads/2023/10/photo_2023-10-02_16-08-02.jpg'),
|
||||
CardData("aboba",
|
||||
imageUrl:
|
||||
'https://masterpiecer-images.s3.yandex.net/5fa453a2d4c51a7:upscaled'),
|
||||
CardData("Hello world!!!",
|
||||
imageUrl:
|
||||
'https://m.media-amazon.com/images/I/81YqUbAZ0GL._AC_UF1000,1000_QL80_.jpg'),
|
||||
CardData('(=^・^=)',
|
||||
imageUrl:
|
||||
'https://i.pinimg.com/236x/c8/cc/24/c8cc24bba37a25c009647b8875aae0e3.jpg'),
|
||||
CardData('плохо быть старым ' + 'трезвым и больным, ' * 5,
|
||||
imageUrl:
|
||||
'https://images.genius.com/c754c6f1755acee741881d55985a6c34.865x865x1.jpg'),
|
||||
];
|
||||
}
|
||||
}
|
38
lib/data/repositories/potter_repository.dart
Normal file
38
lib/data/repositories/potter_repository.dart
Normal file
@ -0,0 +1,38 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:laba1/data/mappers/spells_mapper.dart';
|
||||
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
|
||||
|
||||
import '../../domain/models/card.dart';
|
||||
import '../dtos/spells_dto.dart';
|
||||
import 'api_interface.dart';
|
||||
|
||||
class PotterRepository extends ApiInterface {
|
||||
static final Dio _dio = Dio()
|
||||
..interceptors.add(PrettyDioLogger(
|
||||
requestHeader: true,
|
||||
requestBody: true,
|
||||
));
|
||||
|
||||
static const String _baseUrl = 'https://api.potterdb.com';
|
||||
|
||||
@override
|
||||
Future<List<CardData>?> loadData(
|
||||
{String? q, OnErrorCallback? onError}) async {
|
||||
try {
|
||||
const String url = '$_baseUrl/v1/spells';
|
||||
|
||||
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
|
||||
url,
|
||||
queryParameters: q != null ? {'filter[name_cont]': q} : null,
|
||||
);
|
||||
|
||||
final SpellDto dto = SpellDto.fromJson(
|
||||
response.data as Map<String, dynamic>);
|
||||
final List<CardData>? data = dto.data?.map((e) => e.toDomain()).toList();
|
||||
return data;
|
||||
} on DioException catch (e) {
|
||||
onError?.call(e.response?.statusMessage);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
32
lib/presentation/dialogs/error_dialog.dart
Normal file
32
lib/presentation/dialogs/error_dialog.dart
Normal file
@ -0,0 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
class ErrorDialog extends StatelessWidget {
|
||||
final String? error;
|
||||
const ErrorDialog(this.error, {super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(36),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.grey,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.error, color: Colors.white),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
error ?? 'UNKNOWN',
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
13
lib/presentation/dialogs/show_dialog.dart
Normal file
13
lib/presentation/dialogs/show_dialog.dart
Normal file
@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'error_dialog.dart';
|
||||
|
||||
void showErrorDialog(
|
||||
BuildContext context, {
|
||||
required String? error,
|
||||
}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => ErrorDialog(error),
|
||||
);
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:laba1/data/repositories/potter_repository.dart';
|
||||
import 'package:laba1/presentation/details_page/details_page.dart';
|
||||
|
||||
import '../../data/repositories/mock_repository.dart';
|
||||
import '../../domain/models/card.dart';
|
||||
import '../dialogs/show_dialog.dart';
|
||||
|
||||
part 'card.dart';
|
||||
|
||||
@ -23,42 +26,58 @@ class MyHomePage extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class Body extends StatelessWidget {
|
||||
class Body extends StatefulWidget {
|
||||
const Body({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<Body> createState() => _BodyState();
|
||||
}
|
||||
|
||||
class _BodyState extends State<Body> {
|
||||
final searchController = TextEditingController();
|
||||
late Future<List<CardData>?> data;
|
||||
final repo = PotterRepository();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<CardData> data = [
|
||||
CardData('orange',
|
||||
imageUrl:
|
||||
'https://kuban24.tv/wp-content/uploads/2023/10/photo_2023-10-02_16-08-02.jpg'),
|
||||
CardData("aboba",
|
||||
imageUrl:
|
||||
'https://masterpiecer-images.s3.yandex.net/5fa453a2d4c51a7:upscaled'),
|
||||
CardData("Hello world!!!",
|
||||
imageUrl:
|
||||
'https://m.media-amazon.com/images/I/81YqUbAZ0GL._AC_UF1000,1000_QL80_.jpg'),
|
||||
CardData('(=^・^=)',
|
||||
imageUrl:
|
||||
'https://i.pinimg.com/236x/c8/cc/24/c8cc24bba37a25c009647b8875aae0e3.jpg'),
|
||||
CardData('плохо быть старым ' + 'трезвым и больным, ' * 5,
|
||||
imageUrl:
|
||||
'https://images.genius.com/c754c6f1755acee741881d55985a6c34.865x865x1.jpg'),
|
||||
];
|
||||
data = repo.loadData(
|
||||
onError: (e) => showErrorDialog(context, error: e),
|
||||
);
|
||||
|
||||
return Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: data
|
||||
.map((e) => _Card.fromData(
|
||||
e,
|
||||
onLike: (bool isLiked) {
|
||||
_showSnackBar(context, isLiked);
|
||||
},
|
||||
onTap: () => _navToDetails(context, e),
|
||||
))
|
||||
.toList(),
|
||||
children: [
|
||||
CupertinoSearchTextField(
|
||||
controller: searchController,
|
||||
onChanged: (search) {
|
||||
setState(() {});
|
||||
data = repo.loadData(
|
||||
q: search,
|
||||
onError: (e) => showErrorDialog(context, error: e),
|
||||
);
|
||||
},
|
||||
),
|
||||
FutureBuilder<List<CardData>?>(
|
||||
future: data,
|
||||
builder: (context, snapshot) => snapshot.hasData
|
||||
? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: snapshot.data
|
||||
?.map((e) => _Card.fromData(
|
||||
e,
|
||||
onLike: (bool isLiked) {
|
||||
_showSnackBar(context, isLiked);
|
||||
},
|
||||
onTap: () => _navToDetails(context, e),
|
||||
))
|
||||
.toList() ??
|
||||
[],
|
||||
)
|
||||
: const CircularProgressIndicator(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
24
pubspec.lock
24
pubspec.lock
@ -182,6 +182,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.7"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.7.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -400,6 +416,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
pretty_dio_logger:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pretty_dio_logger
|
||||
sha256: "36f2101299786d567869493e2f5731de61ce130faa14679473b26905a92b6407"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -37,6 +37,8 @@ dependencies:
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
json_annotation: ^4.8.1
|
||||
dio: ^5.4.2+1
|
||||
pretty_dio_logger: ^1.3.1
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
Loading…
Reference in New Issue
Block a user