diff --git a/wiki_dog_breeds/README.md b/wiki_dog_breeds/README.md index 569dfa6..06f4f38 100644 --- a/wiki_dog_breeds/README.md +++ b/wiki_dog_breeds/README.md @@ -3,7 +3,7 @@ A new Flutter project. ## Getting Started - + This project is a starting point for a Flutter application. A few resources to get you started if this is your first Flutter project: diff --git a/wiki_dog_breeds/lib/domain/models/card.dart b/wiki_dog_breeds/lib/domain/models/card.dart new file mode 100644 index 0000000..8aefc2b --- /dev/null +++ b/wiki_dog_breeds/lib/domain/models/card.dart @@ -0,0 +1,15 @@ +class CardData { + final String text; + final String desc; + final List tags; + final bool isTop; + final String imageUrl; + + CardData({ + required this.text, + required this.desc, + required this.tags, + required this.isTop, + required this.imageUrl, + }); +} \ No newline at end of file diff --git a/wiki_dog_breeds/lib/main.dart b/wiki_dog_breeds/lib/main.dart index 60c649a..e543e1c 100644 --- a/wiki_dog_breeds/lib/main.dart +++ b/wiki_dog_breeds/lib/main.dart @@ -1,6 +1,5 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; +import 'package:wiki_dog_breeds/presentation/home_page/home_page.dart'; void main() { runApp(const MyApp()); @@ -23,159 +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) { - return Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), - ), - body: const MyWidget(), - ); - } -} - -class MyWidget extends StatelessWidget { - const MyWidget({super.key}); - - @override - Widget build(BuildContext context) { - - final data = [ - _CardData(text:'Немецкая овчарка', desc: 'Отличные охранники и бойцы, уверенные, выносливые', tages:['крупные', 'служебные'] , imageUrl: 'https://i.pinimg.com/originals/37/a2/9a/37a29a34b248ff86d15c5d1aca9a13e0.jpg'), - _CardData(text: 'Хаски', desc: 'Очень дружелюбные и ласковые, отличные компаньоны', tages:['крупные', 'пастушьи', 'компаньоны'] , imageUrl: 'https://i.pinimg.com/736x/56/c4/f0/56c4f08522549ed68e6782a4461810fc.jpg'), - _CardData(text: 'Доберман', desc: 'Умные, преданные, с молниеносной реакцией и чувством собственного достоинства', tages:['крупные', 'бойцовые'] , imageUrl: 'https://i.pinimg.com/736x/46/05/4f/46054f5a232d2be0704e5bf2c92c073a.jpg'), - _CardData(text: 'Колли', desc: 'Общительные, интеллигентные, неагрессивные, сильно привязываются', tages:['крупные', 'компаньоны'] , imageUrl: 'https://i.pinimg.com/originals/b4/9a/59/b49a59cd65b1315bf207896e7022ff96.jpg'), - _CardData(text: 'Корги', desc: 'Жизнерадостные, активные, социальные, обучаемые, ', tages:['маленькие', 'декоративные'] , imageUrl: 'https://avatars.mds.yandex.net/i?id=0d1565e44373e7be60d235ebfe642396_l-4776319-images-thumbs&n=13'), - ]; - - return Center( - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: data.map((e) => _Card.fromData(e)).toList(), - ), - ), - ); - } -} - -class _Card extends StatelessWidget { - final String text; - final String desc; - final List tages; - final String imageUrl; - - _Card(this.text, this.desc, this.tages, this.imageUrl); - - factory _Card.fromData(_CardData data) => _Card( - data.text, - data.desc, - data.tages, - data.imageUrl - ); - - @override - Widget build(BuildContext context) { - return Container( - height: 150, - margin: const EdgeInsets.only(top: 16), - decoration: BoxDecoration( - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black26.withOpacity(0.5), - offset: const Offset(0, 5), - blurRadius: 8, - spreadRadius: 4 - ) - ] - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: ClipRRect( - child: SizedBox( - height: 150, - width: 400, - child: Image.network( - imageUrl ?? '', - fit: BoxFit.cover, - errorBuilder: (_, __, ___) => const Placeholder(), - ), - ), - ) - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - text, - style: Theme.of(context).textTheme.titleMedium - ), - Text ( - desc, - style: TextStyle( - fontSize: 12.0, - height: 0.99 - ), - //style: Theme.of(context).textTheme.bodySmall, - ), - Wrap( - spacing: 2.0, - runSpacing: 3.0, - children: tages.map((e) => _buildTag(e)).toList(), - ) - ], - ), - ), - ), - ], - ), - ); - } - - Widget _buildTag(String tag) { - return Container( - margin: const EdgeInsets.only(right: 5.0), - padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 2.0), - decoration: BoxDecoration( - color: Colors.orange, - borderRadius: BorderRadius.circular(16.0), - ), - child: Text( - tag, - style: TextStyle( - color: Colors.white, - fontSize: 10.0, - ), - ), - ); - } -} - -class _CardData { - final String text; - final String desc; - final List tages; - final String imageUrl; - - _CardData({required this.text, required this.desc, required this.tages, required this.imageUrl}); -} - diff --git a/wiki_dog_breeds/lib/presentation/details_page/details_page.dart b/wiki_dog_breeds/lib/presentation/details_page/details_page.dart new file mode 100644 index 0000000..fb3bcc5 --- /dev/null +++ b/wiki_dog_breeds/lib/presentation/details_page/details_page.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:wiki_dog_breeds/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: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: Image.network(data.imageUrl), + ), + Padding( + padding: const EdgeInsets.only(left: 18, right: 18, top: 0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 5.0), + child: Text( + data.text, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 14.0), + child: Text( + data.desc, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + Wrap( + spacing: 2.0, + runSpacing: 3.0, + children: data.tags.map((e) => _buildTag(e)).toList(), + ) + ], + ), + ), + ], + ), + ), + ); + } + + Widget _buildTag(String tag) { + return Container( + margin: const EdgeInsets.only(right: 5.0), + padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 2.0), + decoration: BoxDecoration( + color: Colors.orange, + borderRadius: BorderRadius.circular(16.0), + ), + child: Text( + tag, + style: TextStyle( + color: Colors.white, + fontSize: 15.0, + ), + ), + ); + } +} + diff --git a/wiki_dog_breeds/lib/presentation/home_page/card.dart b/wiki_dog_breeds/lib/presentation/home_page/card.dart new file mode 100644 index 0000000..fe3b3b5 --- /dev/null +++ b/wiki_dog_breeds/lib/presentation/home_page/card.dart @@ -0,0 +1,184 @@ +part of 'home_page.dart'; + +typedef OnLikeCallback = void Function(String title, bool isLiked)?; + +class _Card extends StatefulWidget { + final String text; + final String desc; + final List tages; + final bool isTop; + final String imageUrl; + final OnLikeCallback onLike; + final VoidCallback? onTap; + + const _Card(this.text, this.desc, this.tages, this.isTop, this.imageUrl, {this.onLike, this.onTap}); + + factory _Card.fromData( + CardData data, { + OnLikeCallback onLike, + VoidCallback? onTap, + }) => + _Card( + data.text, + data.desc, + data.tags, + data.isTop, + 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: IntrinsicHeight( + child: Container( + height: 150, + margin: const EdgeInsets.only(top: 16), + constraints: const BoxConstraints(minHeight: 150), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black26.withOpacity(0.5), + offset: const Offset(0, 5), + blurRadius: 8, + spreadRadius: 4 + ) + ] + ), + child: IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: ClipRRect( + child: SizedBox( + height: double.infinity, + width: 400, + child: Stack( + children: [ + Positioned.fill( + child: Image.network( + widget.imageUrl, + fit: BoxFit.cover, + errorBuilder: (_, __, ___) => const Placeholder(), + ), + ), + Align( + alignment: Alignment.bottomLeft, + child: widget.isTop + ? Container( + decoration: const BoxDecoration( + color: Colors.deepOrange, + borderRadius: BorderRadius.only( + topRight: Radius.elliptical(20, 60) + ) + ), + padding: const EdgeInsets.fromLTRB(8,2,8,2), + child: Text( + 'ТОП', + style: Theme.of(context) + .textTheme.bodyLarge + ?.copyWith(color: Colors.white), + ), + ) + : null, + ) + ] + ), + ), + ) + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + // заголовок + widget.text, + style: Theme.of(context).textTheme.titleSmall + ), + ), + Flexible( + child: GestureDetector( + onTap: () { + setState(() => isLiked = !isLiked); + widget.onLike?.call(widget.text, isLiked); + }, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: isLiked + ? const Icon( + Icons.favorite, + color: Colors.deepOrangeAccent, + key: ValueKey(0), + ) + : const Icon( + Icons.favorite_border, + key: ValueKey(1), + ) + ), + ), + ), + ], + ), + Text ( + // описание + widget.desc, + style: TextStyle( + fontSize: 12.0, + height: 0.99 + ), + //style: Theme.of(context).textTheme.bodySmall, + ), + Wrap( + spacing: 2.0, + runSpacing: 3.0, + children: widget.tages.map((e) => _buildTag(e)).toList(), + ) + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } + + Widget _buildTag(String tag) { + return Container( + margin: const EdgeInsets.only(right: 5.0), + padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 2.0), + decoration: BoxDecoration( + color: Colors.orange, + borderRadius: BorderRadius.circular(16.0), + ), + child: Text( + tag, + style: TextStyle( + color: Colors.white, + fontSize: 10.0, + ), + ), + ); + } +} \ No newline at end of file diff --git a/wiki_dog_breeds/lib/presentation/home_page/home_page.dart b/wiki_dog_breeds/lib/presentation/home_page/home_page.dart new file mode 100644 index 0000000..fdef5b4 --- /dev/null +++ b/wiki_dog_breeds/lib/presentation/home_page/home_page.dart @@ -0,0 +1,85 @@ +import "package:flutter/cupertino.dart"; +import "package:flutter/material.dart"; +import "package:wiki_dog_breeds/domain/models/card.dart"; +import "package:wiki_dog_breeds/presentation/details_page/details_page.dart"; +part 'card.dart'; + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class Body extends StatelessWidget { + const Body({super.key}); + + @override + Widget build(BuildContext context) { + + final data = [ + CardData(text:'Немецкая овчарка', desc: 'Отличные охранники и бойцы, уверенные, выносливые', tags:['крупные', 'служебные'] , isTop: true, imageUrl: 'https://i.pinimg.com/originals/37/a2/9a/37a29a34b248ff86d15c5d1aca9a13e0.jpg'), + CardData(text: 'Хаски', desc: 'Очень дружелюбные и ласковые, отличные компаньоны', tags:['крупные', 'пастушьи', 'компаньоны'] , isTop: false, imageUrl: 'https://i.pinimg.com/736x/56/c4/f0/56c4f08522549ed68e6782a4461810fc.jpg'), + CardData(text: 'Доберман', desc: 'Умные, преданные, с молниеносной реакцией и чувством собственного достоинства', tags:['крупные', 'бойцовые'] , isTop: true, imageUrl: 'https://i.pinimg.com/736x/46/05/4f/46054f5a232d2be0704e5bf2c92c073a.jpg'), + CardData(text: 'Колли', desc: 'Общительные, интеллигентные, неагрессивные, сильно привязываются', tags:['крупные', 'компаньоны'] , isTop: false, imageUrl: 'https://i.pinimg.com/originals/b4/9a/59/b49a59cd65b1315bf207896e7022ff96.jpg'), + CardData(text: 'Корги', desc: 'Жизнерадостные, активные, социальные, обучаемые, ', tags:['маленькие', 'декоративные'] , isTop: true, imageUrl: 'https://avatars.mds.yandex.net/i?id=0d1565e44373e7be60d235ebfe642396_l-4776319-images-thumbs&n=13'), + ]; + + return Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: data.map((e) { + return _Card.fromData( + e, + onLike: (String title, bool isLiked) => _showSnackBar(context, e.text, isLiked), + onTap: () => _navToDetails(context, e), + ); + }) + .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 ( + 'Запись $title ${isLiked ? 'лайкнута' : 'дизлайкнута :c'}', + style: Theme.of(context).textTheme.labelLarge, + ), + backgroundColor: Colors.amber, + duration: const Duration(seconds: 1), + )); + }); + } +} + +class _MyHomePageState extends State { + +@override void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text(widget.title), + ), + body: const Body(), + ); + } +} +