This commit is contained in:
Alenka 2024-10-08 23:25:53 +04:00
parent 50c1b3600e
commit 2acfc62ea2
7 changed files with 244 additions and 60 deletions

107
lib/card_item.dart Normal file
View File

@ -0,0 +1,107 @@
import 'package:flutter/material.dart';
class CardItem extends StatefulWidget {
final String suit;
final String value;
final String imageUrl;
const CardItem({required this.suit, required this.value, required this.imageUrl});
@override
_CardItemState createState() => _CardItemState();
}
class _CardItemState extends State<CardItem> {
bool isLiked = false;
void _toggleLike() {
setState(() {
isLiked = !isLiked;
});
final snackBar = SnackBar(
content: Text(isLiked ? 'Вы поставили лайк!' : 'Вы убрали лайк!'),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
void _showDetails(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('${widget.value} из ${widget.suit}'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(widget.imageUrl),
SizedBox(height: 10),
Text('Это карта ${widget.value} из масти ${widget.suit}.'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Закрыть'),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _showDetails(context),
child: Container(
width: 150,
child: Card(
color: Colors.grey[300],
margin: EdgeInsets.all(10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(1),
child: Container(
height: 150,
child: Image.network(
widget.imageUrl,
fit: BoxFit.cover,
),
),
),
SizedBox(height: 10),
Text(
'${widget.value} из ${widget.suit}',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 5),
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: IconButton(
key: ValueKey<bool>(isLiked),
icon: Icon(
isLiked ? Icons.favorite : Icons.favorite_border,
color: isLiked ? Colors.red : null,
),
onPressed: _toggleLike,
),
),
],
),
),
),
)
);
}
}

15
lib/card_model.dart Normal file
View File

@ -0,0 +1,15 @@
class CardModel {
final String suit;
final String value;
final String imageUrl;
CardModel({required this.suit, required this.value, required this.imageUrl});
factory CardModel.fromJson(Map<String, dynamic> json) {
return CardModel(
suit: json['suit'],
value: json['value'],
imageUrl: json['image'],
);
}
}

19
lib/card_repository.dart Normal file
View File

@ -0,0 +1,19 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'card_model.dart';
class CardRepository {
Future<List<CardModel>> fetchCards() async {
final response = await http.get(Uri.parse('https://deckofcardsapi.com/api/deck/new/draw/?count=5'));
if (response.statusCode == 200) {
final data = json.decode(response.body);
List<CardModel> cards = (data['cards'] as List)
.map((card) => CardModel.fromJson(card))
.toList();
return cards;
} else {
throw Exception('Ошибка загрузки карт');
}
}
}

View File

@ -1,4 +1,7 @@
import 'package:flutter/material.dart';
import 'card_repository.dart';
import 'card_item.dart';
import 'card_model.dart'; // Убедитесь, что этот импорт присутствует
void main() {
runApp(MyApp());
@ -8,7 +11,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Card List',
title: 'Card App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
@ -17,71 +20,83 @@ class MyApp extends StatelessWidget {
}
}
class CardListScreen extends StatelessWidget {
final List<Map<String, String>> items = [
{
'title': 'Москва-сити',
'description': 'ул. Пресненская Набережная, 6.',
'image': 'https://avatars.mds.yandex.net/i?id=3b3ba2410de1f807162074752fa01cbf8b8f75ed-4507796-images-thumbs&n=13'
},
{
'title': 'Красная площадь',
'description': 'ул. Тверская, д. 10.',
'image': 'https://avatars.mds.yandex.net/i?id=855279ca345f259516f990bd24c62645cc8eab24-4012351-images-thumbs&n=13'
},
{
'title': 'ГУМ',
'description': 'Красная площадь, 3.',
'image': 'https://avatars.mds.yandex.net/i?id=471812ce52e0416c0172e2ad2df2de66a7601625-13101691-images-thumbs&n=13'
},
];
class CardListScreen extends StatefulWidget {
@override
_CardListScreenState createState() => _CardListScreenState();
}
class _CardListScreenState extends State<CardListScreen> {
List<CardModel> cards = [];
List<CardModel> filteredCards = [];
bool isLoading = true;
String searchQuery = '';
@override
void initState() {
super.initState();
fetchCards();
}
Future<void> fetchCards() async {
final repository = CardRepository();
try {
final fetchedCards = await repository.fetchCards();
setState(() {
cards = fetchedCards;
filteredCards = cards;
isLoading = false;
});
} catch (e) {
setState(() {
isLoading = false;
});
}
}
void filterCards(String query) {
setState(() {
searchQuery = query;
filteredCards = cards.where((card) {
return card.value.toLowerCase().contains(query.toLowerCase()) ||
card.suit.toLowerCase().contains(query.toLowerCase());
}).toList();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Куда сходить в Москве'),
),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // Количество колонок
crossAxisSpacing: 10, // Расстояние между колонками
mainAxisSpacing: 10, // Расстояние между строками
),
itemCount: items.length,
itemBuilder: (context, index) {
return Card(
color: Colors.grey[300],
margin: EdgeInsets.all(10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(8.0), // Уменьшение отступов
child: Column(
mainAxisSize: MainAxisSize.min, // Заставляет карточку занимать минимальный размер
children: [
Expanded(
child: Image.network(
items[index]['image']!,
fit: BoxFit.cover, // Подгоняет изображение по размеру
),
),
SizedBox(height: 5), // Отступ между изображением и текстом
Text(
items[index]['title']!,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), // Увеличение размера текста
),
SizedBox(height: 5), // Отступ между заголовком и описанием
Text(
items[index]['description']!,
style: TextStyle(fontSize: 14), // Увеличение размера текста
textAlign: TextAlign.center, // Центрирование текста
),
],
title: Text('Колода карт'),
bottom: PreferredSize(
preferredSize: Size.fromHeight(50.0),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
hintText: 'Поиск по значению или масти',
border: OutlineInputBorder(),
),
onChanged: filterCards,
),
),
),
),
body: isLoading
? Center(child: CircularProgressIndicator())
: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.9,
),
itemCount: filteredCards.length,
itemBuilder: (context, index) {
final card = filteredCards[index];
return CardItem(
suit: card.suit,
value: card.value,
imageUrl: card.imageUrl,
);
},
),

View File

@ -75,6 +75,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
url: "https://pub.dev"
source: hosted
version: "0.13.6"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
leak_tracker:
dependency: transitive
description:
@ -192,6 +208,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.2"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
vector_math:
dependency: transitive
description:

View File

@ -28,10 +28,14 @@ environment:
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
http: ^0.13.3 # или другая актуальная версия
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8

View File

@ -13,7 +13,7 @@ import 'package:untitled/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
await tester.pumpWidget(MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);