From f4573584d0d9a74e63875240bd81a4f3ee32bd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=9F=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D0=BF=D0=BE=D0=B2?= Date: Tue, 26 Nov 2024 16:43:55 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=20=D0=BB=D0=B0=D0=B9=D0=BA=20=D0=B8=20=D0=B4=D0=B5=D1=82?= =?UTF-8?q?=D0=B0=D0=BB=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/domain/card.dart | 8 + .../detail_pages/card_detail_page.dart | 43 +++++ lib/presentation/home_page/card.dart | 166 ++++++++++++++++++ lib/presentation/home_page/home_page.dart | 164 +++++------------ 4 files changed, 258 insertions(+), 123 deletions(-) create mode 100644 lib/domain/card.dart diff --git a/lib/domain/card.dart b/lib/domain/card.dart new file mode 100644 index 0000000..3a96a32 --- /dev/null +++ b/lib/domain/card.dart @@ -0,0 +1,8 @@ +class CardPostData { + final String name; + final String description; + final String? imageUrl; + final bool isLiked; + + const CardPostData(this.name, this.description, this.imageUrl, this.isLiked); +} diff --git a/lib/presentation/detail_pages/card_detail_page.dart b/lib/presentation/detail_pages/card_detail_page.dart index e69de29..ebd244e 100644 --- a/lib/presentation/detail_pages/card_detail_page.dart +++ b/lib/presentation/detail_pages/card_detail_page.dart @@ -0,0 +1,43 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:pmu/domain/card.dart'; + +class DetailPage extends StatelessWidget { + final CardPostData data; + + const DetailPage(this.data, {super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: Image.network( + data.imageUrl ?? '', + fit: BoxFit.cover, + width: double.infinity, + ), + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Text( + data.name, + style: const TextStyle(color: Colors.black, fontSize: 30), + ), + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Text( + data.description, + style: const TextStyle(color: Colors.black, fontSize: 20), + ), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/presentation/home_page/card.dart b/lib/presentation/home_page/card.dart index e69de29..a7d9be7 100644 --- a/lib/presentation/home_page/card.dart +++ b/lib/presentation/home_page/card.dart @@ -0,0 +1,166 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:pmu/domain/card.dart'; + +typedef OnLikeCallback = void Function(String title, bool isLiked)?; + +const double NORMAL_ICON_SCALE = 2.0; +const double SCALED_ICON_SCALE = 2.5; + + +class CardPost extends StatefulWidget { + final String description; + final String? imageUrl; + final bool isLiked; + final String name; + + final OnLikeCallback onLike; + final VoidCallback? onTap; + + const CardPost(this.name, this.description, this.imageUrl, this.isLiked, + this.onLike, this.onTap); + + factory CardPost.fromData(CardPostData data, {OnLikeCallback onLike, + VoidCallback? onTap}) => + CardPost(data.name, data.description, data.imageUrl, data.isLiked, + onLike, onTap); + + @override + State createState() => _CardPostState(); +} + +class _CardPostState extends State { + bool isLiked = false; + + double iconScale = NORMAL_ICON_SCALE; + + void _onLikeTap() { + setState(() { + isLiked = !isLiked; + iconScale = SCALED_ICON_SCALE; // Increase scale temporarily + }); + + // Reset the scale back to normal after 250 milliseconds + Timer(const Duration(milliseconds: 110), () { + setState(() { + iconScale = NORMAL_ICON_SCALE; + }); + }); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: widget.onTap, + child: Container( + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.blue, + border: Border.all( + color: Colors.blue, + width: 0.5, + ), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + offset: Offset( + 0.0, + 5.0, + ), + blurRadius: 4.0, + spreadRadius: 1.0, + ), + ], + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: Text( + widget.name, + style: const TextStyle( + color: Colors.white, + fontSize: 30, + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.only(top: 6.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: Text( + widget.description, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontSize: 24, + ), + ), + ), + ], + ), + ), + if (widget.imageUrl != null) + Padding( + padding: const EdgeInsets.all(8.0), + child: Image.network(widget.imageUrl!, + height: 500, + width: double.infinity, + fit: BoxFit.fitWidth), + ), + Padding( + padding: const EdgeInsets.all(4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () => + { + setState(() { + isLiked = !isLiked; + iconScale = SCALED_ICON_SCALE; + + Timer(const Duration(milliseconds: 110), () { + setState(() { + iconScale = NORMAL_ICON_SCALE; + }); + }); + widget.onLike?.call(widget.name, isLiked); + }) + }, + child: AnimatedScale(scale: iconScale, + duration: const Duration(milliseconds: 250), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + child: isLiked + ? const Icon( + Icons.favorite, color: Colors.red, + key: ValueKey( + 1),) + : const Icon( + Icons.favorite_border, color: Colors.black, + key: ValueKey(1) + ) + ) + ), + ), + + ], + ), + ) + ], + ), + ) + ); + } + +} diff --git a/lib/presentation/home_page/home_page.dart b/lib/presentation/home_page/home_page.dart index 2d7d18a..26cfdcb 100644 --- a/lib/presentation/home_page/home_page.dart +++ b/lib/presentation/home_page/home_page.dart @@ -1,4 +1,8 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:pmu/domain/card.dart'; +import 'package:pmu/presentation/detail_pages/card_detail_page.dart'; +import 'package:pmu/presentation/home_page/card.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); @@ -17,152 +21,66 @@ class _MyHomePageState extends State { backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), - body: const MyWidget(), + body: const Body(), ); } } -class MyWidget extends StatelessWidget { - const MyWidget({super.key}); +class Body extends StatelessWidget { + const Body({super.key}); + + void _navToDetails(BuildContext context, CardPostData data) { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => DetailPage(data)), + ); + } + + void _showSnackBar(BuildContext context, String title, bool isLiked) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + '${isLiked ? 'Вы поставили лайк: ' : 'Вы убрали лайк: '} $title', + style: const TextStyle( + color: Colors.black45, + fontSize: 20, + fontWeight: FontWeight.bold + ), + ), + backgroundColor: isLiked ? Colors.green : Colors.red, + duration: const Duration(seconds: 2), + )); + }); + } @override Widget build(BuildContext context) { final data = [ - const _CardPostData( + const CardPostData( "Евгения", "Люблю программирование и кататься на скейте", "https://avatar.iran.liara.run/public/girl", true), - const _CardPostData("Алекс", "Junior Flutter Engineer", + const CardPostData("Алекс", "Junior Flutter Engineer", "https://avatar.iran.liara.run/public/36", false), - const _CardPostData( + const CardPostData( "Станислав", "Нет ничего приятнее прогулки на природе!", "https://avatar.iran.liara.run/public/boy", true), - const _CardPostData("Вероника", "В свободное время хожу на уроки вокала", + const CardPostData("Вероника", "В свободное время хожу на уроки вокала", "https://avatar.iran.liara.run/public/93", false), ]; return Center( child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: data.map((e) => _CardPost.fromData(e)).toList(), + children: data.map((data) { + return CardPost.fromData(data, + onLike: (String title, bool isLiked) => + _showSnackBar(context, title, isLiked), + onTap: () => _navToDetails(context, data)); + }).toList(), ))); } } - -class _CardPost extends StatelessWidget { - final String description; - final String? imageUrl; - final bool isLiked; - final String name; - - const _CardPost(this.name, this.description, this.imageUrl, this.isLiked); - - factory _CardPost.fromData(_CardPostData data) => - _CardPost(data.name, data.description, data.imageUrl, data.isLiked); - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.blue, - border: Border.all( - color: Colors.blue, - width: 0.5, - ), - boxShadow: const [ - BoxShadow( - color: Colors.black12, - offset: Offset( - 0.0, - 5.0, - ), - blurRadius: 4.0, - spreadRadius: 1.0, - ), - ], - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: Text( - name, - style: const TextStyle( - color: Colors.white, - fontSize: 30, - ), - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.only(top: 6.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: Text( - description, - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.white, - fontSize: 24, - ), - ), - ), - ], - ), - ), - if (imageUrl != null) - Padding( - padding: const EdgeInsets.all(8.0), - child: Image.network(imageUrl!, - height: 500, width: double.infinity, fit: BoxFit.fitWidth), - ), - if (isLiked) - const Padding( - padding: EdgeInsets.all(4.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.favorite, color: Colors.red, size: 80), - ], - ), - ) - else - const Padding( - padding: EdgeInsets.all(4.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.favorite_border, color: Colors.red, size: 80), - ], - ), - ) - ], - ), - ); - } -} - -class _CardPostData extends StatelessWidget { - final String name; - final String description; - final String? imageUrl; - final bool isLiked; - - const _CardPostData(this.name, this.description, this.imageUrl, this.isLiked); - - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -}