laba 5
This commit is contained in:
parent
50c1b3600e
commit
2acfc62ea2
107
lib/card_item.dart
Normal file
107
lib/card_item.dart
Normal 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
15
lib/card_model.dart
Normal 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
19
lib/card_repository.dart
Normal 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('Ошибка загрузки карт');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
133
lib/main.dart
133
lib/main.dart
@ -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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user