почти сделал 5 лабу

This commit is contained in:
Timourka 2024-10-01 18:21:37 +04:00
parent 6310e327ca
commit fe516de6e2
11 changed files with 278 additions and 37 deletions

View File

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

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

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

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

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

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

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

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

View File

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

View File

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

View File

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