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 'package:flutter/material.dart';
import 'card_repository.dart';
import 'card_item.dart';
import 'card_model.dart'; // Убедитесь, что этот импорт присутствует
void main() { void main() {
runApp(MyApp()); runApp(MyApp());
@ -8,7 +11,7 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Card List', title: 'Card App',
theme: ThemeData( theme: ThemeData(
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
), ),
@ -17,71 +20,83 @@ class MyApp extends StatelessWidget {
} }
} }
class CardListScreen extends StatelessWidget { class CardListScreen extends StatefulWidget {
final List<Map<String, String>> items = [ @override
{ _CardListScreenState createState() => _CardListScreenState();
'title': 'Москва-сити', }
'description': 'ул. Пресненская Набережная, 6.',
'image': 'https://avatars.mds.yandex.net/i?id=3b3ba2410de1f807162074752fa01cbf8b8f75ed-4507796-images-thumbs&n=13' class _CardListScreenState extends State<CardListScreen> {
}, List<CardModel> cards = [];
{ List<CardModel> filteredCards = [];
'title': 'Красная площадь', bool isLoading = true;
'description': 'ул. Тверская, д. 10.', String searchQuery = '';
'image': 'https://avatars.mds.yandex.net/i?id=855279ca345f259516f990bd24c62645cc8eab24-4012351-images-thumbs&n=13'
}, @override
{ void initState() {
'title': 'ГУМ', super.initState();
'description': 'Красная площадь, 3.', fetchCards();
'image': 'https://avatars.mds.yandex.net/i?id=471812ce52e0416c0172e2ad2df2de66a7601625-13101691-images-thumbs&n=13' }
},
]; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Куда сходить в Москве'), title: Text('Колода карт'),
bottom: PreferredSize(
), preferredSize: Size.fromHeight(50.0),
body: GridView.builder( child: Padding(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( padding: const EdgeInsets.all(8.0),
crossAxisCount: 2, // Количество колонок child: TextField(
crossAxisSpacing: 10, // Расстояние между колонками decoration: InputDecoration(
mainAxisSpacing: 10, // Расстояние между строками hintText: 'Поиск по значению или масти',
), border: OutlineInputBorder(),
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, // Центрирование текста
),
],
), ),
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 description: flutter
source: sdk source: sdk
version: "0.0.0" 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: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -192,6 +208,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.2" 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: vector_math:
dependency: transitive dependency: transitive
description: description:

View File

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

View File

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