diff --git a/lib/domain/models/card.dart b/lib/domain/models/card.dart new file mode 100644 index 0000000..b8b92cb --- /dev/null +++ b/lib/domain/models/card.dart @@ -0,0 +1,36 @@ +import 'package:first_project/presentation/home_page/home_page.dart'; +import 'package:flutter/material.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), + child: Image.network( + data.imageUrl ?? '', + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Text( + data.text, + style: Theme.of(context).textTheme.headlineLarge, + )), + Text( + data.description, + style: Theme.of(context).textTheme.bodyLarge, + ) + ], + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index be760f5..90b1b4b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:first_project/presentation/home_page/home_page.dart'; import 'package:flutter/material.dart'; void main() { @@ -21,143 +22,3 @@ class MyApp extends StatelessWidget { ); } } - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - @override - Widget build(BuildContext context) { - final data = [ - const _CardData( - "First", - description: "SomeText", - ), - const _CardData( - "Second", - icon: Icons.gamepad, - description: "ManyText", - imageUrl: "https://i.pinimg.com/originals/21/73/24/217324138d1bbc91663d4943ebe5de60.jpg", - ), - const _CardData( - "Third", - icon: Icons.offline_bolt_outlined, - description: "Wow >_<", - ), - ]; - - return Scaffold( - appBar: AppBar( - backgroundColor: Colors.purple, - title: Text(widget.title), - ), - body: Center( - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: data.map((e) => _Card.fromData(e)).toList(), - ), - ))); - } -} - -class _CardData { - final String text; - final String description; - final IconData icon; - final String imageUrl; - - const _CardData( - this.text, { - required this.description, - this.icon = Icons.add_call, - this.imageUrl = - "https://i.pinimg.com/736x/5f/14/b3/5f14b3f14fcd157bc4dffa39085396cc.jpg", - }); -} - -class _Card extends StatelessWidget { - final String text; - final String description; - final IconData icon; - final String imageUrl; - - const _Card( - this.text, { - required this.description, - this.icon = Icons.add_call, - required this.imageUrl, - }); - - factory _Card.fromData(_CardData data) => _Card( - data.text, - description: data.description, - icon: data.icon, - imageUrl: data.imageUrl, - ); - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(15), - padding: const EdgeInsets.all(15), - decoration: BoxDecoration( - color: Colors.purple, - borderRadius: BorderRadius.circular(20), - border: Border.all(color: Colors.green, width: 2), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(.4), - offset: const Offset(0, 5), - blurRadius: 6, - spreadRadius: 3 - ), - ] - ), - - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: SizedBox( - height: 140, - width: 100, - child: Image.network( - imageUrl ?? '', - fit: BoxFit.cover, - errorBuilder: (_, __, ___) => const Placeholder(), - ), - ), - ), - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - text * 10, - style: Theme.of(context).textTheme.headlineLarge, - ), - Text( - description, - style: Theme.of(context).textTheme.bodySmall, - ) - ], - ), - ), - Padding( - padding: const EdgeInsets.only(left: 8), - child: Icon(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..8b13789 --- /dev/null +++ b/lib/presentation/details_page/details_page.dart @@ -0,0 +1 @@ + diff --git a/lib/presentation/home_page/card.dart b/lib/presentation/home_page/card.dart new file mode 100644 index 0000000..c237563 --- /dev/null +++ b/lib/presentation/home_page/card.dart @@ -0,0 +1,164 @@ +part of 'home_page.dart'; + +typedef OnLikeCallback = void Function(String title, bool isLiked)?; + +class CardData { + final String text; + final String description; + final IconData icon; + final String imageUrl; + + const CardData( + this.text, { + required this.description, + this.icon = Icons.add_call, + this.imageUrl = + "https://i.pinimg.com/736x/5f/14/b3/5f14b3f14fcd157bc4dffa39085396cc.jpg", + }); +} + +class _Card extends StatefulWidget { + final String text; + final String description; + final String? imageUrl; + final OnLikeCallback onLike; + final VoidCallback? onTap; + + const _Card( + this.text, { + required this.description, + required this.imageUrl, + this.onLike, + this.onTap, + }); + + factory _Card.fromData( + CardData data, { + OnLikeCallback onLike, + VoidCallback? onTap, + }) => + _Card( + data.text, + description: data.description, + 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: Container( + margin: const EdgeInsets.all(10), + constraints: const BoxConstraints(minHeight: 140), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.green, width: 2), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(.4), + offset: const Offset(0, 5), + blurRadius: 6, + spreadRadius: 3), + ]), + child: IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack(children: [ + ClipRRect( + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(18), + topLeft: Radius.circular(18), + ), + child: SizedBox( + height: double.infinity, + width: 120, + child: Image.network( + widget.imageUrl ?? '', + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => const Placeholder(), + ), + ), + ), + Align( + alignment: Alignment.bottomLeft, + child: Container( + decoration: const BoxDecoration( + color: Colors.purple, + borderRadius: BorderRadius.only( + topRight: Radius.circular(20), + bottomLeft: Radius.circular(18))), + padding: const EdgeInsets.fromLTRB(8, 2, 8, 2), + child: Text('Новинка', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.white)), + ), + ), + ]), + Expanded( + child: Padding( + padding: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + widget.text, + style: Theme.of(context).textTheme.headlineLarge, + ), + Text( + widget.description, + style: Theme.of(context).textTheme.bodySmall, + ) + ], + ), + ), + ), + Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.only( + left: 8, + right: 8, + bottom: 8, + ), + 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.red, + 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 new file mode 100644 index 0000000..f10f892 --- /dev/null +++ b/lib/presentation/home_page/home_page.dart @@ -0,0 +1,104 @@ +import 'package:first_project/domain/models/card.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +part 'card.dart'; + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + 'Hello', + style: Theme.of(context).textTheme.bodyMedium, + ), + backgroundColor: Colors.purple, + duration: const Duration(seconds: 1), + )); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.purple, + title: Text(widget.title), + ), + body: const Body(), + ); + } +} + +class Body extends StatelessWidget { + const Body({super.key}); + + @override + Widget build(BuildContext context) { + final data = [ + const CardData( + "First", + description: "SomeText", + ), + const CardData( + "Second", + icon: Icons.gamepad, + description: "ManyText", + imageUrl: + "https://i.pinimg.com/originals/21/73/24/217324138d1bbc91663d4943ebe5de60.jpg", + ), + const CardData( + "Third", + icon: Icons.offline_bolt_outlined, + description: "Wow >_<", + ), + ]; + return Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: data.map( + (e) { + return _Card.fromData( + e, + onLike: (String title, bool isLiked) => + _showLiked(context, title, isLiked), + onTap: () => _navToDetails(context, e), + ); + }, + ).toList(), + ), + )); + } + + void _navToDetails(BuildContext context, CardData data) { + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => DetailsPage(data)), + ); + } + + void _showLiked(BuildContext context, String title, bool isLiked) { + WidgetsBinding.instance.addPostFrameCallback((_) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + 'Аниме $title ${isLiked ? 'лайкнуто!' : 'дизлайнуто'}', + style: Theme.of(context).textTheme.bodyMedium, + ), + backgroundColor: Colors.purple, + duration: const Duration(seconds: 1), + )); + }); + } +}