сделал 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'; part 'spells_dto.g.dart';
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class SpellDto { class SpellsDto {
final List<SpellDataDto>? data; final List<SpellDataDto>? data;
const SpellDto({ const SpellsDto({
this.data, this.data,
}); });
factory SpellDto.fromJson(Map<String, dynamic> json) => factory SpellsDto.fromJson(Map<String, dynamic> json) =>
_$SpellDtoFromJson(json); _$SpellDtoFromJson(json);
} }

View File

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

View File

@ -8,5 +8,30 @@ extension SpellsDataDtoToModel on SpellDataDto {
CardData toDomain() => CardData( CardData toDomain() => CardData(
attributes?.name ?? 'UNKNOWN', attributes?.name ?? 'UNKNOWN',
imageUrl: attributes?.image ?? _imagePlaceHolder, 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 { try {
const String url = '$_baseUrl/v1/spells'; const String url = '$_baseUrl/v1/spells';
final Response<dynamic> response = await _dio.get<Map<dynamic, dynamic>>( 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, url,
queryParameters: q != null ? {'filter[name_cont]': q} : null, 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( List<SpellDataDto> combinedData = [
response.data as Map<String, dynamic>); ...?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(); final List<CardData>? data = dto.data?.map((e) => e.toDomain()).toList();
return data; return data;
} on DioException catch (e) { } on DioException catch (e) {

View File

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

View File

@ -39,32 +39,29 @@ class _BodyState extends State<Body> {
final repo = PotterRepository(); final repo = PotterRepository();
@override @override
Widget build(BuildContext context) { void initState() {
data = repo.loadData( data = repo.loadData(onError: (e) => showErrorDialog(context, error: e));
onError: (e) => showErrorDialog(context, error: e), super.initState();
); }
@override
Widget build(BuildContext context) {
return Center( return Center(
child: SingleChildScrollView( child: Stack(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
CupertinoSearchTextField( Positioned.fill(
controller: searchController, child: FutureBuilder<List<CardData>?>(
onChanged: (search) {
setState(() {});
data = repo.loadData(
q: search,
onError: (e) => showErrorDialog(context, error: e),
);
},
),
FutureBuilder<List<CardData>?>(
future: data, future: data,
builder: (context, snapshot) => snapshot.hasData builder: (context, snapshot) {
? Column( var cards = Column(
mainAxisAlignment: MainAxisAlignment.center, children: [],
children: snapshot.data );
cards.children.add(Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8),
child: CupertinoSearchTextField(),
));
cards.children.addAll(
snapshot.data
?.map((e) => _Card.fromData( ?.map((e) => _Card.fromData(
e, e,
onLike: (bool isLiked) { onLike: (bool isLiked) {
@ -74,12 +71,39 @@ class _BodyState extends State<Body> {
)) ))
.toList() ?? .toList() ??
[], [],
);
return snapshot.hasData
? SingleChildScrollView(
child: cards,
) )
: const CircularProgressIndicator(), : Center(child: 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),
);
});
},
),
),
), ),
], ],
), ),
),
); );
} }