From 8a681b590a94513853207d467cbab3831d5084bf Mon Sep 17 00:00:00 2001 From: revengel66 Date: Fri, 25 Oct 2024 21:48:27 +0400 Subject: [PATCH] lab 2-4 ready --- lib/domain/models/card.dart | 11 ++ lib/main.dart | 148 +----------------- .../details_page/details_page.dart | 35 +++++ lib/presentation/home_page/card.dart | 132 ++++++++++++++++ lib/presentation/home_page/home_page.dart | 126 +++++++++++++++ 5 files changed, 305 insertions(+), 147 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..8a4a7cb --- /dev/null +++ b/lib/domain/models/card.dart @@ -0,0 +1,11 @@ +class CardDate { + final String text; + final String description; + final String? imgUrl; + + CardDate( + this.text, { + required this.description, + this.imgUrl, + }); +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index c6c27b4..84be076 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:pmd_lab/presentation/home_page/home_page.dart'; void main() { runApp(const MyApp()); @@ -19,150 +20,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 { - final Color _color = Colors.orangeAccent; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: _color, - title: Text(widget.title), - ), - body: const MyWidget(), - ); - } -} - -class MyWidget extends StatelessWidget { - const MyWidget({super.key}); - - @override - Widget build(BuildContext context) { - final data = [ - _CardDate( - 'Какая-то новость', - description: 'В данном блоке рекомендуем разместить краткую информацию', - imgUrl: - 'https://universal.revengel.ru/assets/cache_image/images/services/3_731x487_1cb.png', - ), - _CardDate( - 'Ещё какая-то новость', - description: 'В данном блоке рекомендуем разместить краткую информацию', - imgUrl: - "https://universal.revengel.ru/assets/cache_image/images/services/2_731x487_1cb.png", - ), - _CardDate( - 'Ещё одна новость', - description: 'В данном блоке рекомендуем разместить краткую информацию', - imgUrl: - "https://universal.revengel.ru/assets/cache_image/images/services/1_731x487_1cb.png", - ) - ]; - - return Center( - child: SingleChildScrollView( - padding: const EdgeInsets.only(left: 20, right: 20, top: 20), - child: Column( - children: data.map((e) => _Card.fromData(e)).toList(), - ), - ), - ); - } -} - -class _CardDate { - final String text; - final String description; - final String? imgUrl; - - _CardDate( - this.text, { - required this.description, - this.imgUrl, - }); -} - -class _Card extends StatelessWidget { - final String text; - final String description; - final String? imgUrl; - - const _Card( - this.text, { - required this.description, - this.imgUrl, - }); - - factory _Card.fromData(_CardDate data) => _Card( - data.text, - description: data.description, - imgUrl: data.imgUrl, - ); - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.only(bottom: 20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.1), - spreadRadius: 1, - offset: const Offset(0, 0), - blurRadius: 15, - ) - ]), - child: Column(children: [ - ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(20), topRight: Radius.circular(20)), - child: Image.network( - imgUrl ?? '', - errorBuilder: (_, __, ___) => const Placeholder(), - fit: BoxFit.fitWidth, - ), - ), - Padding( - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom:10), - child: Text( - text, - style: const TextStyle( - color: Color(0xff4c4c4c), - fontSize: 30, - fontWeight: FontWeight.bold, - ), - ), - ), - Text( - description, - style: const TextStyle( - color: Color(0xff9c9c9c), - fontSize: 20, - fontWeight: FontWeight.normal, - ), - ), - ], - ), - ), - ]), - ); - } -} diff --git a/lib/presentation/details_page/details_page.dart b/lib/presentation/details_page/details_page.dart new file mode 100644 index 0000000..51f4fad --- /dev/null +++ b/lib/presentation/details_page/details_page.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:pmd_lab/domain/models/card.dart'; + +class DetailsPage extends StatelessWidget { + final CardDate 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.imgUrl ?? '',), + ), + 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/presentation/home_page/card.dart b/lib/presentation/home_page/card.dart new file mode 100644 index 0000000..82eec8f --- /dev/null +++ b/lib/presentation/home_page/card.dart @@ -0,0 +1,132 @@ +part of 'home_page.dart'; + +typedef OnLikeCallBack = void Function(String title, bool isLiked)?; + +class _Card extends StatefulWidget { + final String text; + final String description; + final String? imgUrl; + final OnLikeCallBack onLike; + final VoidCallback? onTap; + + const _Card( + this.text, { + required this.description, + this.imgUrl, + this.onLike, + this.onTap, + }); + + factory _Card.fromData(CardDate data, {OnLikeCallBack onLike, VoidCallback? onTap}) => _Card( + data.text, + description: data.description, + imgUrl: data.imgUrl, + 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.only(bottom: 20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + spreadRadius: 1, + offset: const Offset(0, 0), + blurRadius: 15, + ) + ]), + child: Column( + children: [ + Stack( + children: [ + ClipRRect( + borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)), + child: Image.network( + widget.imgUrl ?? '', + errorBuilder: (_, __, ___) => const Placeholder(), + fit: BoxFit.fitWidth, + ), + ), + Align( + alignment: Alignment.topRight, + child: Container( + decoration: const BoxDecoration( + color: Colors.redAccent, + borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20),bottomRight: Radius.circular(0), bottomLeft: Radius.circular(20)), + ), + padding: const EdgeInsets.fromLTRB(20,8,20,8), + child: const Text( + 'NEW', + style: TextStyle( + color: Color(0xff191919), + fontSize: 15, + ), + ) + , + ) + ) + ] + ), + Padding( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom:10), + child: Text( + widget.text, + style: const TextStyle( + color: Color(0xff4c4c4c), + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), + ), + Text( + widget.description, + style: const TextStyle( + color: Color(0xff9c9c9c), + fontSize: 20, + fontWeight: FontWeight.normal, + ), + ), + Padding( + padding: const EdgeInsets.only(top:10), + 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(0),) + : const Icon(Icons.favorite_border, key:ValueKey(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..4049c1e --- /dev/null +++ b/lib/presentation/home_page/home_page.dart @@ -0,0 +1,126 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:pmd_lab/domain/models/card.dart'; +import 'package:pmd_lab/presentation/details_page/details_page.dart'; +part 'card.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // colorScheme: ColorScheme.fromSeed(seedColor: Colors.black), colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const MyHomePage(title: 'Flutter rocks!'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final Color _color = Colors.orangeAccent; + + @override + void initState(){ + WidgetsBinding.instance.addPostFrameCallback( (_) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Hello', style: Theme + .of(context) + .textTheme + .bodyLarge,), + backgroundColor: Colors.orangeAccent, + duration: const Duration(seconds: 1), + )); + }); + super.initState(); + } + + @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 = [ + CardDate( + 'Какая-то новость', + description: 'В данном блоке рекомендуем разместить краткую информацию', + imgUrl: + 'https://universal.revengel.ru/assets/cache_image/images/services/3_731x487_1cb.png', + ), + CardDate( + 'Ещё какая-то новость', + description: 'В данном блоке рекомендуем разместить краткую информацию', + imgUrl: + "https://universal.revengel.ru/assets/cache_image/images/services/2_731x487_1cb.png", + ), + CardDate( + 'Ещё одна новость', + description: 'В данном блоке рекомендуем разместить краткую информацию', + imgUrl: + "https://universal.revengel.ru/assets/cache_image/images/services/1_731x487_1cb.png", + ) + ]; + + return Center( + child: SingleChildScrollView( + padding: const EdgeInsets.only(left: 20, right: 20, top: 20), + child: Column( + children: data.map((data) => _Card.fromData( + data, + onLike: (text, isLiked) => _showSnackBar(context, text, isLiked), + onTap: () => _navToDetails(context, data), + )).toList(), + ), + ), + ); + } + + void _showSnackBar(BuildContext context, String title, bool isLiked){ + WidgetsBinding.instance.addPostFrameCallback( (_) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text( + 'Article $title ${isLiked ? 'liked!' : 'disliked :('}', + style: Theme.of(context).textTheme.bodyLarge,), + backgroundColor: Colors.orangeAccent, + duration: const Duration(seconds: 1), + )); + }); + } + + void _navToDetails(BuildContext context, CardDate data){ + Navigator.push( + context, + CupertinoPageRoute(builder: (context) => DetailsPage(data)), + ); + } +} + +