сделал 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;
url, if (q == null) {
queryParameters: q != null ? {'filter[name_cont]': 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( 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,11 +71,24 @@ 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(
widget._text, crossAxisAlignment: CrossAxisAlignment.start,
style: Theme.of(context).textTheme.headlineMedium, children: [
overflow: TextOverflow.ellipsis, Text(
maxLines: 4, 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(); 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( children: [
mainAxisAlignment: MainAxisAlignment.center, Positioned.fill(
children: [ child: FutureBuilder<List<CardData>?>(
CupertinoSearchTextField( future: data,
controller: searchController, builder: (context, snapshot) {
onChanged: (search) { var cards = Column(
setState(() {}); children: [],
data = repo.loadData(
q: search,
onError: (e) => showErrorDialog(context, error: e),
); );
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, Align(
builder: (context, snapshot) => snapshot.hasData alignment: Alignment.topCenter,
? Column( child: Padding(
mainAxisAlignment: MainAxisAlignment.center, padding: const EdgeInsets.only(left: 8.0, right: 8),
children: snapshot.data child: CupertinoSearchTextField(
?.map((e) => _Card.fromData( borderRadius: BorderRadius.only(
e, bottomLeft: Radius.circular(20),
onLike: (bool isLiked) { bottomRight: Radius.circular(20),
_showSnackBar(context, isLiked); ),
}, backgroundColor: Colors.amberAccent,
onTap: () => _navToDetails(context, e), controller: searchController,
)) onChanged: (search) {
.toList() ?? setState(() {
[], data = repo.loadData(
) q: search,
: const CircularProgressIndicator(), onError: (e) => showErrorDialog(context, error: e),
);
});
},
),
), ),
], ),
), ],
), ),
); );
} }