diff --git a/lib/domain/models/card.dart b/lib/domain/models/card.dart new file mode 100644 index 0000000..4faa8f7 --- /dev/null +++ b/lib/domain/models/card.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +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, + }); +} diff --git a/lib/main.dart b/lib/main.dart index f1ff124..0446d80 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,7 +16,7 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), - home: const MyHomePage(title: 'Yackobchuk S.V.'), + home: const MyHomePage(title: 'The coolest hamsters on earth ^^'), ); } -} \ No newline at end of file +} diff --git a/lib/presentation/details_page/details_page.dart b/lib/presentation/details_page/details_page.dart new file mode 100644 index 0000000..5d780bb --- /dev/null +++ b/lib/presentation/details_page/details_page.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_labs/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(), + body: 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.descriptionText, + style: Theme.of(context).textTheme.bodyLarge, + ) + ], + ), + ); + } +} diff --git a/lib/presentation/home_page/card.dart b/lib/presentation/home_page/card.dart index a3a5afe..3221934 100644 --- a/lib/presentation/home_page/card.dart +++ b/lib/presentation/home_page/card.dart @@ -1,39 +1,159 @@ part of 'home_page.dart'; -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, - }); -} +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.text, { + this.icon = Icons.ac_unit_outlined, + required this.descriptionText, + this.imageUrl, + this.onLike, + this.onTap, + }); - factory _Card.fromData(_CardData data) => _Card( - data.text, - descriptionText: data.descriptionText, - icon: data.icon, - imageUrl: data.imageUrl, - ); + factory _Card.fromData( + CardData data, { + OnLikeCallback onLike, + VoidCallback? onTap, + }) => + _Card( + data.text, + descriptionText: data.descriptionText, + icon: data.icon, + imageUrl: data.imageUrl, + onLike: onLike, + onTap: onTap, + ); @override State<_Card> createState() => _CardState(); -} \ No newline at end of file +} + +class _CardState extends State<_Card> { + bool isLiked = false; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: widget.onTap, + child: Container( + margin: const EdgeInsets.all(16), + constraints: const BoxConstraints(minHeight: 140), + decoration: BoxDecoration( + color: Colors.white70, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(.5), + spreadRadius: 4, + offset: const Offset(0, 5), + blurRadius: 8, + ), + ], + ), + child: IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(20), + topLeft: Radius.circular(20), + ), + child: SizedBox( + height: double.infinity, + width: 120, + child: Stack( + 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.orangeAccent, + borderRadius: BorderRadius.only( + topRight: Radius.circular(20), + // + )), + padding: const EdgeInsets.fromLTRB(8, 2, 8, 2), + child: Text( + 'скидок нет', + 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, + ) + ], + ), + ), + ), + Align( + alignment: Alignment.bottomRight, + 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(microseconds: 200), + child: isLiked + ? const Icon( + Icons.favorite, + color: Colors.redAccent, + key: ValueKey(0), + ) + : const Icon( + Icons.favorite_border, + key: ValueKey(1), + ), + ), + ), + ), + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/home_page/home_page.dart b/lib/presentation/home_page/home_page.dart index 09f8fc8..8f471ae 100644 --- a/lib/presentation/home_page/home_page.dart +++ b/lib/presentation/home_page/home_page.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_labs/presentation/details_page/details_page.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_labs/domain/models/card.dart'; part 'card.dart'; @@ -32,130 +35,57 @@ class Body extends StatelessWidget { @override Widget build(BuildContext context) { final data = [ - _CardData( - 'mouse', + CardData('mouse', descriptionText: 'hahaha', icon: Icons.abc, imageUrl: - 'https://sun34-1.userapi.com/impg/_DLT-op0LbBdgh5h-ILvC7IMDY5kbLR349v7vA/tX7vtk6mNlA.jpg?size=736x736&quality=96&sign=47f2b0f63bf249c62f4498fb637695d5&type=album' - ), - _CardData( - 'mouse2', + 'https://sun34-1.userapi.com/impg/_DLT-op0LbBdgh5h-ILvC7IMDY5kbLR349v7vA/tX7vtk6mNlA.jpg?size=736x736&quality=96&sign=47f2b0f63bf249c62f4498fb637695d5&type=album'), + CardData('mouse2', descriptionText: 'ahahaha', icon: Icons.access_alarm_outlined, imageUrl: - 'https://sun165-1.userapi.com/impg/EVLbaLilqr8xw5tsqZLQQb6DSYrdKo7Q9sYSsw/H4FRwyMR6Ec.jpg?size=1280x960&quality=96&sign=f606e4ae3d1ccd27917cd1ffa6d91e58&type=album' - ), - _CardData( - 'mouse3', + 'https://sun165-1.userapi.com/impg/EVLbaLilqr8xw5tsqZLQQb6DSYrdKo7Q9sYSsw/H4FRwyMR6Ec.jpg?size=1280x960&quality=96&sign=f606e4ae3d1ccd27917cd1ffa6d91e58&type=album'), + CardData('mouse3', descriptionText: 'eeee', icon: Icons.access_alarm_rounded, imageUrl: - 'https://sun165-1.userapi.com/impg/Jxmp-zwq--f7KagZ6HuImLY7HrgQo-kyP1xhmw/JejdXP88_VE.jpg?size=736x981&quality=96&sign=5597f4d123422be17118ff4b1f570a9d&type=album' - ), + 'https://sun165-1.userapi.com/impg/Jxmp-zwq--f7KagZ6HuImLY7HrgQo-kyP1xhmw/JejdXP88_VE.jpg?size=736x981&quality=96&sign=5597f4d123422be17118ff4b1f570a9d&type=album'), ]; return Center( child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: data.map((e) => _Card.fromData(e)).toList(), - ) - ), - ); - } -} - -class _CardState extends State<_Card> { - bool isLiked = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(16), - constraints: const BoxConstraints(minHeight: 140), - decoration: BoxDecoration( - color: Colors.white70, - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(.5), - spreadRadius: 4, - offset: const Offset(0, 5), - blurRadius: 8, - ), - ], - ), - child: IntrinsicHeight( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ClipRRect( - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(20), - topLeft: Radius.circular(20), - ), - child: SizedBox( - height: double.infinity, - width: 120, - child: Image.network( - widget.imageUrl ?? '', - fit: BoxFit.cover, - errorBuilder: (_, __, ___) => const Placeholder(), - ), - ), - ), - 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, - ) - ], - ), - ), - ), - Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: const EdgeInsets.only( - left: 8.0, - right: 16, - bottom: 16, - ), - child: GestureDetector( - onTap: () { - setState(() { - isLiked = !isLiked; - }); - }, - child: AnimatedSwitcher( - duration: const Duration(microseconds: 200), - child: isLiked - ? const Icon( - Icons.favorite, - color: Colors.redAccent, - key: ValueKey(0), - ) - : const Icon( - Icons.favorite_border, - key: ValueKey(1), - ), - ), - ), - ), - ) - ], + 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(), ), ), ); } -} \ No newline at end of file + + 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( + 'Cute hamster $title ${isLiked ? 'liked!' : 'disliked :('}', + style: Theme.of(context).textTheme.bodyLarge, + ), + backgroundColor: Colors.orangeAccent, + duration: const Duration(seconds: 1), + )); + }); + } +}