From 8f0102f97b678a81b512819e312ae3340ef4d3db Mon Sep 17 00:00:00 2001
From: Stepan <afanasevstepan67@gmail.com>
Date: Sun, 10 Nov 2024 18:01:20 +0400
Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20Lab4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/domain/models/card.dart                   |  17 ++
 lib/main.dart                                 | 158 +----------------
 .../details_page/details_page.dart            |  43 +++++
 lib/presentation/home_page/card.dart          | 163 ++++++++++++++++++
 lib/presentation/home_page/home_page.dart     | 106 ++++++++++++
 5 files changed, 330 insertions(+), 157 deletions(-)
 create mode 100644 lib/domain/models/card.dart
 create mode 100644 lib/presentation/details_page/details_page.dart
 create mode 100644 lib/presentation/home_page/card.dart
 create mode 100644 lib/presentation/home_page/home_page.dart

diff --git a/lib/domain/models/card.dart b/lib/domain/models/card.dart
new file mode 100644
index 0000000..61afed6
--- /dev/null
+++ b/lib/domain/models/card.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/material.dart';
+
+class CardData {
+  final String text;
+  final String descriptionText;
+  final IconData icon;
+  final String? imageUrl;
+  final String gameDesc;
+
+  CardData(
+      this.text, {
+        required this.descriptionText,
+        this.icon = Icons.ac_unit_outlined,
+        this.imageUrl,
+        required this.gameDesc,
+      });
+}
\ No newline at end of file
diff --git a/lib/main.dart b/lib/main.dart
index c18748f..fde759a 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,5 @@
-import 'dart:math';
-
 import 'package:flutter/material.dart';
+import 'presentation/home_page/home_page.dart';
 
 void main() {
   runApp(const MyApp());
@@ -23,163 +22,8 @@ class MyApp extends StatelessWidget {
   }
 }
 
-class MyHomePage extends StatefulWidget {
-  const MyHomePage({super.key, required this.title});
-
-  final String title;
-
-  @override
-  State<MyHomePage> createState() => _MyHomePageState();
-}
-
-class _MyHomePageState extends State<MyHomePage> {
-  final Color _color = Colors.orangeAccent;
-
-  @override
-  Widget build(BuildContext context) {
-     return Scaffold(
-      appBar: AppBar(
-        backgroundColor: _color,
-        title: Text(widget.title),
-      ),
-       body: const MyWidget(),
-     );
-  }
-}
 
 
-class _CardData {
-  final String text;
-  final String descriptionText;
-  final IconData icon;
-  final String? imageUrl;
 
-  _CardData(
-    this.text, {
-    required this.descriptionText,
-    this.icon = Icons.ac_unit_outlined,
-    this.imageUrl,
-    });
-}
 
-class MyWidget extends StatelessWidget {
-  const MyWidget({super.key}); // ключи
-
-  @override
-  Widget build(BuildContext context) {
-    final data = [
-      _CardData(
-        'Hello',
-        descriptionText: 'hshhshs',
-        imageUrl: 'https://images.stopgame.ru/games/logos/8351/c1536x856/VdQJNv5ECykaiteg212k5g/far_cry_2-square_1.jpg'
-      ), // создаётся объект ранее созданного контейнера
-      _CardData(
-          'Ivan',
-          descriptionText: 'hshhshs',
-          imageUrl: 'https://images.stopgame.ru/games/logos/8351/c1536x856/VdQJNv5ECykaiteg212k5g/far_cry_2-square_1.jpg'
-
-      ),
-      _CardData(
-          'Stepan',
-          descriptionText: 'hshhshs',
-          imageUrl: 'https://images.stopgame.ru/games/logos/8351/c1536x856/VdQJNv5ECykaiteg212k5g/far_cry_2-square_1.jpg'
-
-      ),
-    ];
-    return Center(
-      child: SingleChildScrollView(
-        child: Column(
-          mainAxisAlignment: MainAxisAlignment.center, //расположение по главной оси
-          children: data.map((e) => _Card.fromData(e)).toList(),
-        ),
-      ),
-    );
-  }
-}
-
-class _Card extends StatefulWidget {
-  final String text;
-  final String descriptionText;
-  final IconData icon;
-  final String? imageUrl;
-
-  const _Card(
-      this.text, {
-        this.icon = Icons.ac_unit_outlined,
-        required this.descriptionText,
-        this.imageUrl,
-
-  }
-  );
-
-  factory _Card.fromData(_CardData data) => _Card(
-    data.text,
-    descriptionText: data.descriptionText,
-    icon: data.icon,
-    imageUrl: data.imageUrl,
-  );
-
-  @override
-  State<_Card> createState() => _CardState();
-}
-
-class _CardState extends State<_Card> {
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      margin: const EdgeInsets.only(top: 15),
-      padding: const EdgeInsets.all(16),
-      decoration: BoxDecoration(
-        color: Colors.orangeAccent,
-        borderRadius: BorderRadius.circular(20),
-        border: Border.all(
-          color: Colors.grey,
-          width: 2,
-        )
-      ),
-      child: Row(
-        mainAxisAlignment: MainAxisAlignment.spaceBetween,
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: [
-          Flexible(
-              child: ClipRRect(
-                borderRadius: BorderRadius.circular(20),
-                child: SizedBox(
-                  height: 140,
-                  width: 100,
-                  child: Image.network(
-                      widget.imageUrl ?? '',
-                    fit: BoxFit.cover,
-                    errorBuilder: (_, __, ___) => const Placeholder(),
-                  ),
-                ),
-              )),
-          Flexible(
-            child: Padding(
-              padding: const EdgeInsets.all(8.0),
-              child: Column(
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  Text(
-                    widget.text,
-                    style:Theme.of(context).textTheme.headlineLarge,
-                  ),
-                  Text(
-                    widget.descriptionText,
-                    style:Theme.of(context).textTheme.bodyLarge,
-                  ),
-                ],
-              ),
-            ),
-          ),
-          const Spacer(),
-          Padding(
-            padding: const EdgeInsets.only(left: 8.0),
-            child: Icon(widget.icon),
-          ),
-        ],
-      ),
-    );
-  }
-}
   
diff --git a/lib/presentation/details_page/details_page.dart b/lib/presentation/details_page/details_page.dart
new file mode 100644
index 0000000..a91a3eb
--- /dev/null
+++ b/lib/presentation/details_page/details_page.dart
@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import '../../domain/models/card.dart';
+
+class DetailsPage extends StatelessWidget {
+  final CardData data;
+
+  const DetailsPage(this.data, {super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(backgroundColor: Colors.deepPurple,
+      ),
+      body: SingleChildScrollView(
+        child: Container(
+          constraints: BoxConstraints( minHeight: MediaQuery.sizeOf(context).height - 105),
+          decoration: const BoxDecoration(color: Colors.deepPurpleAccent,),
+          padding: const EdgeInsets.all(10),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Padding(
+                padding: const EdgeInsets.only(bottom: 16.0),
+                child: Image.network(data.imageUrl ?? ''),
+              ),
+              Padding(
+                padding: const EdgeInsets.only(bottom: 4.0),
+                child: Text(
+                  data.text,
+                  style: Theme.of(context).textTheme.headlineLarge,
+                ),
+              ),
+              Text(
+                data.gameDesc,
+                style: Theme.of(context).textTheme.bodyLarge,
+              )
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
\ No newline at end of file
diff --git a/lib/presentation/home_page/card.dart b/lib/presentation/home_page/card.dart
new file mode 100644
index 0000000..76182b2
--- /dev/null
+++ b/lib/presentation/home_page/card.dart
@@ -0,0 +1,163 @@
+part of 'home_page.dart';
+
+typedef OnLikeCallback = void Function(String title, bool isLiked); //сделано от дублирования кода
+
+
+class _Card extends StatefulWidget {
+  final String text;
+  final String descriptionText;
+  final IconData icon;
+  final String? imageUrl;
+  final OnLikeCallback? onLike;
+  final VoidCallback? onTap;
+
+  const _Card(
+      this.text, {
+        this.icon = Icons.ac_unit_outlined,
+        required this.descriptionText,
+        this.imageUrl,
+        this.onLike,
+        this.onTap,
+      }
+      );
+
+  factory _Card.fromData(
+      CardData data, {
+        OnLikeCallback? onLike,
+        VoidCallback? onTap,
+      }) =>
+      _Card(
+        data.text,
+        descriptionText: data.descriptionText,
+        imageUrl: data.imageUrl,
+        onLike: onLike,
+        onTap: onTap,
+      );
+
+  @override
+  State<_Card> createState() => _CardState();
+}
+
+
+class _CardState extends State<_Card> {
+  bool isLiked = false;
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: widget.onTap,
+      child: Padding(
+        padding: const EdgeInsets.symmetric(horizontal: 8.0),
+        child: Container(
+          margin: const EdgeInsets.all(16),
+          //padding: const EdgeInsets.all(16),
+          constraints: const BoxConstraints(minHeight: 140), //минимальный размер контейнера
+          decoration: BoxDecoration(
+              color: Colors.orangeAccent,
+              borderRadius: BorderRadius.circular(20),
+              border: Border.all(
+                color: Colors.grey,
+                width: 2,
+              )
+          ),
+          child: IntrinsicHeight(
+            child: Row(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                ClipRRect(
+                  borderRadius: const BorderRadius.only(
+                    bottomLeft: Radius.circular(20),
+                    topLeft: Radius.circular(20),
+                  ),
+                  child: SizedBox(
+                    height: 160,
+                    width: 160,
+                    child: Stack( // делаем поверх картинки надпись New
+                      children: [
+                        Positioned.fill(
+                          child: Image.network(
+                            widget.imageUrl ?? '',
+                            fit: BoxFit.cover,
+                            errorBuilder: (_, __, ___) => const Placeholder(),
+                          ),
+                        ),
+                        Align(
+                          alignment: Alignment.bottomLeft,
+                          child: Container(
+                            decoration: const BoxDecoration(
+                              color: Colors.purpleAccent,
+                              borderRadius: BorderRadius.only(
+                                bottomRight: Radius.circular(20),
+                              ),
+                            ),
+                            padding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
+                            child: Text(
+                              'New!!!!',
+                              style: Theme.of(context)
+                                  .textTheme
+                                  .bodyMedium
+                                  ?.copyWith(color: Colors.black),
+                            ),
+                          ),
+                        )
+                      ],
+                    ),
+                  ),
+                ),
+                Expanded(
+                  child: Padding(
+                    padding: const EdgeInsets.only(left: 16.0),
+                    child: Column(
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Text(
+                          widget.text,
+                          style:Theme.of(context).textTheme.headlineLarge,
+                        ),
+                        Text(
+                          widget.descriptionText,
+                          style:Theme.of(context).textTheme.bodyLarge,
+                        ),
+                      ],
+                    ),
+                  ),
+                ),
+                const Spacer(),
+                Align(
+                  alignment: Alignment.topRight,
+                  child: Padding(
+                    padding: const EdgeInsets.only(
+                      left: 8.0,
+                      right: 16,
+                      bottom: 16,
+                    ),
+                    child: GestureDetector(
+                      onTap: (){
+                        setState((){
+                          isLiked = !isLiked; // установка лайков
+                        });
+                        widget.onLike?.call(widget.text, isLiked);
+                      },
+                      child: AnimatedSwitcher( // Анимация для наших лайков
+                        duration: const Duration(milliseconds: 300),
+                        child: isLiked
+                            ? const Icon (
+                          Icons.favorite,
+                          color: Colors.redAccent,
+                          key: ValueKey<int>(0), // ключи для перерисовки
+                        )
+                            : const Icon(
+                          Icons.favorite_border,
+                          key: ValueKey<int>(1), // ключи для перерисовки
+                        ),
+                      ),
+                    ),
+                  ),
+                ),
+              ],
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
\ No newline at end of file
diff --git a/lib/presentation/home_page/home_page.dart b/lib/presentation/home_page/home_page.dart
new file mode 100644
index 0000000..973d73f
--- /dev/null
+++ b/lib/presentation/home_page/home_page.dart
@@ -0,0 +1,106 @@
+import 'package:flutter_test_app/main.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+import '../../domain/models/card.dart';
+import '../details_page/details_page.dart';
+
+
+part 'card.dart';
+
+class MyHomePage extends StatefulWidget {
+  const MyHomePage({super.key, required this.title});
+
+  final String title;
+
+  @override
+  State<MyHomePage> createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+  final Color _color = Colors.orangeAccent;
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        backgroundColor: _color,
+        title: Text(widget.title),
+      ),
+      body: const Body(),
+    );
+  }
+}
+
+class Body extends StatelessWidget {
+  const Body({super.key}); // ключи
+
+  @override
+  Widget build(BuildContext context) {
+    final data = [
+      CardData(
+          'Far Cry 1',
+          descriptionText: 'Так себе',
+          imageUrl: 'https://images.stopgame.ru/games/logos/8351/c1536x856/VdQJNv5ECykaiteg212k5g/far_cry_2-square_1.jpg',
+          gameDesc: 'Обретите сверхчеловеческую мощь космодесантника. Пустите в ход смертоносные навыки и разрушительное оружие, чтобы ' +
+              ' истребить безжалостных тиранидов. Защитите Империум в ярких одиночных боях или многопользовательских режимах с  ' +
+              'видом от третьего лица. В продолжении экшена 2011 года Space Marine вам предстоит сразиться с врагами человечества и' +
+              ' вновь доказать ему свою преданность в роли лейтенанта Деметрия Тита, который вернулся в ряды Ультрамаринов. Дайте отпор ' +
+              'ужасам Галактики в эпических боях сразу на нескольких планетах. Раскройте мрачные секреты и отбросьте тень вечной ночи.'), // создаётся объект ранее созданного контейнера
+      CardData(
+          'Far Cry 2',
+          descriptionText: 'Лучше',
+          imageUrl: 'https://images.stopgame.ru/games/logos/8351/c1536x856/VdQJNv5ECykaiteg212k5g/far_cry_2-square_1.jpg',
+    gameDesc: 'Обретите сверхчеловеческую мощь космодесантника. Пустите в ход смертоносные навыки и разрушительное оружие, чтобы ' +
+    ' истребить безжалостных тиранидов. Защитите Империум в ярких одиночных боях или многопользовательских режимах с  ' +
+    'видом от третьего лица. В продолжении экшена 2011 года Space Marine вам предстоит сразиться с врагами человечества и' +
+    ' вновь доказать ему свою преданность в роли лейтенанта Деметрия Тита, который вернулся в ряды Ультрамаринов. Дайте отпор ' +
+    'ужасам Галактики в эпических боях сразу на нескольких планетах. Раскройте мрачные секреты и отбросьте тень вечной ночи.'),
+      CardData(
+          'Far Cry 3',
+          descriptionText: 'Красавцы',
+          imageUrl: 'https://images.stopgame.ru/games/logos/8351/c1536x856/VdQJNv5ECykaiteg212k5g/far_cry_2-square_1.jpg',
+    gameDesc: 'Обретите сверхчеловеческую мощь космодесантника. Пустите в ход смертоносные навыки и разрушительное оружие, чтобы ' +
+    ' истребить безжалостных тиранидов. Защитите Империум в ярких одиночных боях или многопользовательских режимах с  ' +
+    'видом от третьего лица. В продолжении экшена 2011 года Space Marine вам предстоит сразиться с врагами человечества и' +
+    ' вновь доказать ему свою преданность в роли лейтенанта Деметрия Тита, который вернулся в ряды Ультрамаринов. Дайте отпор ' +
+    'ужасам Галактики в эпических боях сразу на нескольких планетах. Раскройте мрачные секреты и отбросьте тень вечной ночи.'
+
+      ),
+    ];
+    return Center(
+      child: SingleChildScrollView(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center, //расположение по главной оси
+          children: data.map((data) {
+            return _Card.fromData(
+              data,
+              onLike: (String title, bool isLiked) =>
+                  _showSnackBar(context, title, isLiked),
+              onTap: () => _navToDetails(context, data),
+            );
+          }).toList(),
+        ),
+      ),
+    );
+  }
+}
+void _navToDetails(BuildContext context, CardData data) {
+  Navigator.push(
+    context,
+    CupertinoPageRoute(builder: (context) => DetailsPage(data)),
+  );
+}
+void _showSnackBar(BuildContext context, String title, bool isLiked) { //выводить уведомление о поставке лайке с названием карточки
+  WidgetsBinding.instance.addPostFrameCallback((_) {
+    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+      content: Text(
+        'News $title ${isLiked ? 'liked' : 'disliked '}',
+        style: Theme.of(context).textTheme.bodyLarge,
+      ),
+      backgroundColor: Colors.deepPurpleAccent,
+      duration: const Duration(seconds: 1),
+    ));
+  });
+}
+