сделал 5 лабу

This commit is contained in:
Timourka 2024-10-01 22:57:57 +04:00
parent fe516de6e2
commit de9e03155d
8 changed files with 168 additions and 51 deletions

View File

@ -3,14 +3,14 @@ import 'package:json_annotation/json_annotation.dart';
part 'spells_dto.g.dart';
@JsonSerializable(createToJson: false)
class SpellDto {
class SpellsDto {
final List<SpellDataDto>? data;
const SpellDto({
const SpellsDto({
this.data,
});
factory SpellDto.fromJson(Map<String, dynamic> json) =>
factory SpellsDto.fromJson(Map<String, dynamic> json) =>
_$SpellDtoFromJson(json);
}

View File

@ -6,7 +6,7 @@ part of 'spells_dto.dart';
// JsonSerializableGenerator
// **************************************************************************
SpellDto _$SpellDtoFromJson(Map<String, dynamic> json) => SpellDto(
SpellsDto _$SpellDtoFromJson(Map<String, dynamic> json) => SpellsDto(
data: (json['data'] as List<dynamic>?)
?.map((e) => SpellDataDto.fromJson(e as Map<String, dynamic>))
.toList(),

View File

@ -8,5 +8,30 @@ extension SpellsDataDtoToModel on SpellDataDto {
CardData toDomain() => CardData(
attributes?.name ?? 'UNKNOWN',
imageUrl: attributes?.image ?? _imagePlaceHolder,
description: _makeDescription(attributes),
);
}
String _makeDescription(SpellAttributesDataDto? attributes){
return 'incantation: ${attributes?.incantation ?? 'UNKNOWN'},\n'
'category: ${attributes?.category ?? 'UNKNOWN'},\n'
'creator: ${attributes?.creator ?? 'UNKNOWN'},\n'
'effect: ${attributes?.effect ?? 'UNKNOWN'},\n'
'hand: ${attributes?.hand ?? 'UNKNOWN'},\n'
'light: ${attributes?.light ?? 'UNKNOWN'},\n'
'slug: ${attributes?.slug ?? 'UNKNOWN'}'
;
}
/*
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; // ссылка на вики
*/

View File

@ -21,13 +21,40 @@ class PotterRepository extends ApiInterface {
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,
);
SpellsDto dto;
if (q == null) {
final Response<dynamic> response =
await _dio.get<Map<dynamic, dynamic>>(url);
dto = SpellsDto.fromJson(response.data as Map<String, dynamic>);
} else {
final Response<dynamic> response1 =
await _dio.get<Map<dynamic, dynamic>>(
url,
queryParameters: q != null ? {'filter[name_cont]': q} : null,
);
final Response<dynamic> response2 =
await _dio.get<Map<dynamic, dynamic>>(
url,
queryParameters: q != null ? {'filter[incantation_cont]': q} : null,
);
SpellsDto dto1 =
SpellsDto.fromJson(response1.data as Map<String, dynamic>);
SpellsDto dto2 =
SpellsDto.fromJson(response2.data as Map<String, dynamic>);
final SpellDto dto = SpellDto.fromJson(
response.data as Map<String, dynamic>);
List<SpellDataDto> combinedData = [
...?dto1.data, // добавляем данные из dto1
...?dto2.data // добавляем данные из dto2
];
// Удаляем дубликаты по полю id
Map<String?, SpellDataDto> uniqueSpellsMap = {
for (var spell in combinedData) spell.id: spell
};
// Преобразуем обратно в список
List<SpellDataDto> uniqueSpells = uniqueSpellsMap.values.toList();
// Создаем новый SpellsDto с уникальными данными
dto = SpellsDto(data: uniqueSpells);
}
final List<CardData>? data = dto.data?.map((e) => e.toDomain()).toList();
return data;
} on DioException catch (e) {
@ -35,4 +62,4 @@ class PotterRepository extends ApiInterface {
return null;
}
}
}
}

View File

@ -1,9 +1,11 @@
class CardData {
final String text;
final String? description;
final String? imageUrl;
CardData(
this.text, {
this.description,
this.imageUrl,
});
}

View File

@ -53,6 +53,30 @@ class DetailsPage extends StatelessWidget {
),
),
),
Container(
margin: const EdgeInsets.all(20),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: const Color.fromARGB(255, 250, 235, 159),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(.5),
blurRadius: 8,
offset: const Offset(0, 5),
spreadRadius: 4,
)
]),
child: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: Text(
data.description ?? '',
style: Theme.of(context).textTheme.headlineLarge,
),
),
),
),
],
),
),

View File

@ -4,11 +4,12 @@ typedef OnLikeCallback = void Function(bool isLiked)?;
class _Card extends StatefulWidget {
final String _text;
final String? description;
final String? imageUrl;
final OnLikeCallback onLike;
final VoidCallback? onTap;
const _Card(this._text, {Key? key, this.imageUrl, this.onLike, this.onTap})
const _Card(this._text, {Key? key, this.imageUrl, this.description, this.onLike, this.onTap})
: super(key: key);
factory _Card.fromData(
@ -19,6 +20,7 @@ class _Card extends StatefulWidget {
_Card(
data.text,
imageUrl: data.imageUrl,
description: data.description,
onLike: onLike,
onTap: onTap,
);
@ -69,11 +71,24 @@ class _CardState extends State<_Card> {
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 8.0, bottom: 8),
child: Text(
widget._text,
style: Theme.of(context).textTheme.headlineMedium,
overflow: TextOverflow.ellipsis,
maxLines: 4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget._text,
style: Theme.of(context).textTheme.headlineMedium,
overflow: TextOverflow.ellipsis,
maxLines: 4,
),
Flexible(
child: Text(
widget.description ?? '',
style: Theme.of(context).textTheme.bodyLarge,
overflow: TextOverflow.ellipsis,
maxLines: 7,
),
),
],
),
),
),

View File

@ -39,46 +39,70 @@ class _BodyState extends State<Body> {
final repo = PotterRepository();
@override
Widget build(BuildContext context) {
data = repo.loadData(
onError: (e) => showErrorDialog(context, error: e),
);
void initState() {
data = repo.loadData(onError: (e) => showErrorDialog(context, error: e));
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CupertinoSearchTextField(
controller: searchController,
onChanged: (search) {
setState(() {});
data = repo.loadData(
q: search,
onError: (e) => showErrorDialog(context, error: e),
child: Stack(
children: [
Positioned.fill(
child: FutureBuilder<List<CardData>?>(
future: data,
builder: (context, snapshot) {
var cards = Column(
children: [],
);
cards.children.add(Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8),
child: CupertinoSearchTextField(),
));
cards.children.addAll(
snapshot.data
?.map((e) => _Card.fromData(
e,
onLike: (bool isLiked) {
_showSnackBar(context, isLiked);
},
onTap: () => _navToDetails(context, e),
))
.toList() ??
[],
);
return snapshot.hasData
? SingleChildScrollView(
child: cards,
)
: Center(child: CircularProgressIndicator());
},
),
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(),
),
Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8),
child: CupertinoSearchTextField(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
backgroundColor: Colors.amberAccent,
controller: searchController,
onChanged: (search) {
setState(() {
data = repo.loadData(
q: search,
onError: (e) => showErrorDialog(context, error: e),
);
});
},
),
),
],
),
),
],
),
);
}