lab5 done

This commit is contained in:
sofia7ya 2024-12-11 12:04:34 +04:00
parent fa4fcb5acb
commit d1c005db18
12 changed files with 275 additions and 70 deletions

View File

@ -0,0 +1,32 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'potions_dto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
PotionsDto _$PotionsDtoFromJson(Map<String, dynamic> json) => PotionsDto(
data: (json['data'] as List<dynamic>?)
?.map((e) => PotionDataDto.fromJson(e as Map<String, dynamic>))
.toList(),
);
PotionDataDto _$PotionDataDtoFromJson(Map<String, dynamic> json) =>
PotionDataDto(
id: json['id'] as String?,
type: json['type'] as String?,
attributes: json['attributes'] == null
? null
: PotionAttributesDataDto.fromJson(
json['attributes'] as Map<String, dynamic>),
);
PotionAttributesDataDto _$PotionAttributesDataDtoFromJson(
Map<String, dynamic> json) =>
PotionAttributesDataDto(
name: json['name'] as String?,
characteristics: json['characteristics'] as String?,
effect: json['effect'] as String?,
image: json['image'] as String?,
);

View File

@ -1,10 +1,23 @@
import 'package:flutter_labs/data/dtos/potions_dto.dart';
import 'package:flutter_labs/domain/models/card.dart';
const _imagePlaceholder =
'https://cdn-icons-png.flaticon.com/512/4036/4036418.png';
extension PotionDataDtoToModel on PotionDataDto {
CardData toDomain() => CardData(
attributes?.name ?? 'UNKNOWN',
imageUrl: attributes?.image,
descriptionText: '${attributes?.characteristics} - ${attributes?.effect}',
imageUrl: attributes?.image ?? _imagePlaceholder,
descriptionText: _makeDescriptionText(attributes?.characteristics, attributes?.effect),
);
String _makeDescriptionText(String? characteristics, String? effect) {
return characteristics != null && effect != null
? '$characteristics - $effect'
: characteristics != null
? 'characteristics: $characteristics'
: effect != null
? 'effect: $effect'
: '';
}
}

View File

@ -0,0 +1,7 @@
import 'package:flutter_labs/domain/models/card.dart';
typedef OnErrorCallback = void Function(String? error);
abstract class ApiInterface {
Future<List<CardData>?> loadData({OnErrorCallback? onError});
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:flutter_labs/data/repositories/api_interface.dart';
import 'package:flutter_labs/domain/models/card.dart';
class MockRepository extends ApiInterface {
@override
Future<List<CardData>?> loadData({OnErrorCallback? onError}) async {
return [
CardData(
'Mouse',
descriptionText: 'hehehe',
imageUrl:
'https://sun9-26.userapi.com/impg/sB_tLPWPCIqxQWmlbcmlRYiw1ibFb70_QMtNwg/56qpyc_C8Go.jpg?size=736x711&quality=95&sign=8f7163b54538a2e7bad5f36a857485d4&type=album',
),
CardData(
'Mouse2',
descriptionText: 'pretty face',
icon: Icons.hail,
imageUrl:
'https://sun165-1.userapi.com/impg/EVLbaLilqr8xw5tsqZLQQb6DSYrdKo7Q9sYSsw/H4FRwyMR6Ec.jpg?size=1280x960&quality=96&sign=f606e4ae3d1ccd27917cd1ffa6d91e58&type=album',
),
CardData(
'Mouse3',
descriptionText: 'I like hamsters',
icon: Icons.warning_amber,
imageUrl: 'https://sun34-1.userapi.com/impg/_DLT-op0LbBdgh5h-ILvC7IMDY5kbLR349v7vA/tX7vtk6mNlA.jpg?size=736x736&quality=96&sign=47f2b0f63bf249c62f4498fb637695d5&type=album',
),
];
}
}

View File

@ -0,0 +1,35 @@
import 'package:dio/dio.dart';
import 'package:flutter_labs/data/dtos/potions_dto.dart';
import 'package:flutter_labs/data/mappers/potions_mapper.dart';
import 'package:flutter_labs/data/repositories/api_interface.dart';
import 'package:flutter_labs/domain/models/card.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.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/potions';
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>(
url,
queryParameters: q != null ? {'filter[name_cont]': q} : null,
);
final PotionsDto dto = PotionsDto.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

@ -16,7 +16,7 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const HomePage(title: 'The coolest hamsters on earth ^^'),
home: const HomePage(title: 'The most unusual liquids O_o'),
);
}
}

View File

@ -0,0 +1,33 @@
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,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_labs/presentation/dialogs/error_dialog.dart';
void showErrorDialog(
BuildContext context, {
required String? error,
}) {
showDialog(
context: context,
builder: (_) => ErrorDialog(error),
);
}

View File

@ -80,25 +80,26 @@ class _CardState extends State<_Card> {
errorBuilder: (_, __, ___) => const Placeholder(),
),
),
Align(
alignment: Alignment.bottomLeft,
child: Container(
decoration: const BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
//
)),
padding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
child: Text(
'скидок нет',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: Colors.black),
),
),
),
// Пусть будет для скидки
//Align(
// alignment: Alignment.bottomLeft,
// child: Container(
// decoration: const BoxDecoration(
// color: Colors.orangeAccent,
// borderRadius: BorderRadius.only(
// topRight: Radius.circular(20),
// //
// )),
// padding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
// child: Text(
// 'скидок нет',
// style: Theme.of(context)
// .textTheme
// .bodyMedium
// ?.copyWith(color: Colors.black),
// ),
// ),
//),
],
),
),
@ -111,7 +112,7 @@ class _CardState extends State<_Card> {
children: [
Text(
widget.text,
style: Theme.of(context).textTheme.headlineLarge,
style: Theme.of(context).textTheme.headlineSmall,
),
Text(
widget.descriptionText,

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_labs/data/repositories/potter_repository.dart';
import 'package:flutter_labs/presentation/details_page/details_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_labs/domain/models/card.dart';
import 'package:flutter_labs/presentation/dialogs/show_dialog.dart';
part 'card.dart';
@ -15,74 +17,89 @@ class HomePage extends StatefulWidget {
}
class _HomePageState extends State<HomePage> {
final Color _color = Colors.orangeAccent;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: _color,
title: Text(widget.title),
),
body: const Body(),
);
return const Scaffold(body: Body());
}
}
class Body extends StatelessWidget {
class Body extends StatefulWidget {
const Body({super.key}); // ключи
@override
Widget build(BuildContext context) {
final data = [
CardData('mouse',
descriptionText: 'hahaha',
icon: Icons.abc,
imageUrl:
'https://sun9-26.userapi.com/impg/sB_tLPWPCIqxQWmlbcmlRYiw1ibFb70_QMtNwg/56qpyc_C8Go.jpg?size=736x711&quality=95&sign=8f7163b54538a2e7bad5f36a857485d4&type=album'),
CardData('mouse2',
descriptionText: 'ahahaha',
icon: Icons.access_alarm_outlined,
imageUrl:
'https://sun165-1.userapi.com/impg/EVLbaLilqr8xw5tsqZLQQb6DSYrdKo7Q9sYSsw/H4FRwyMR6Ec.jpg?size=1280x960&quality=96&sign=f606e4ae3d1ccd27917cd1ffa6d91e58&type=album'),
CardData('mouse3',
descriptionText: 'eeee',
icon: Icons.access_alarm_rounded,
imageUrl:
'https://sun34-1.userapi.com/impg/_DLT-op0LbBdgh5h-ILvC7IMDY5kbLR349v7vA/tX7vtk6mNlA.jpg?size=736x736&quality=96&sign=47f2b0f63bf249c62f4498fb637695d5&type=album'),
];
State<Body> createState() => _BodyState();
}
return Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: data.map((data) {
return _Card.fromData(
data,
onLike: (String title, bool isLiked) =>
_showSnackBar(context, title, isLiked),
onTap: () => _navToDetails(context, data),
);
}).toList(),
),
class _BodyState extends State<Body> {
final searchController = TextEditingController();
late Future<List<CardData>?> data;
final repo = PotterRepository();
@override
void initState() {
data = repo.loadData(onError: (e) => showErrorDialog(context, error: e));
super.initState();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: CupertinoSearchTextField(
controller: searchController,
onChanged: (search) {
setState(() {
data = repo.loadData(q: search);
});
},
),
),
Expanded(
child: Center(
child: FutureBuilder<List<CardData>?>(
future: data,
builder: (context, snapshot) => SingleChildScrollView(
child: snapshot.hasData
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: snapshot.data?.map((data) {
return _Card.fromData(
data,
onLike: (String title, bool isLiked) =>
_showSnackBar(context, title, isLiked),
onTap: () => _navToDetails(context, data),
);
}).toList() ??
[],
)
: const CircularProgressIndicator(),
),
),
),
),
],
),
);
}
void _navToDetails(BuildContext context, CardData data) {
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => DetailsPage(data)),
);
}
void _showSnackBar(BuildContext context, String title, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Cute hamster $title ${isLiked ? 'liked!' : 'disliked :('}',
style: Theme.of(context).textTheme.bodyLarge,
),
content: Text(
'$title ${isLiked ? 'liked!' : 'disliked :('}',
style: Theme.of(context).textTheme.bodyLarge,
),
backgroundColor: Colors.orangeAccent,
duration: const Duration(seconds: 1),
));

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

@ -11,8 +11,9 @@ dependencies:
sdk: flutter
cupertino_icons: ^1.0.8
json_annotation: ^4.8.1
dio: ^5.4.2+1
pretty_dio_logger: ^1.3.1
dev_dependencies:
flutter_test: