{"id":319481,"date":"2021-03-12T15:01:18","date_gmt":"2021-03-12T15:01:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=319481"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=319481","title":{"rendered":"\u0422\u0440\u0435\u043f\u0435\u0449\u0443\u0449\u0438\u0439 Kivy. \u041e\u0431\u0437\u043e\u0440 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Kivy \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 KivyMD"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/i2\/8_\/qf\/i28_qff8276scfhag1p7wksllx4.png\"><\/div>\n<p>  <a href=\"https:\/\/github.com\/kivy\/kivy\" rel=\"nofollow noopener noreferrer\">Kivy<\/a> \u0438 <a href=\"https:\/\/github.com\/flutter\/flutter\" rel=\"nofollow noopener noreferrer\">Flutter<\/a> \u2014 \u0434\u0432\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u0434\u043b\u044f \u043a\u0440\u043e\u0441\u0441\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<h3><b>Flutter:<\/b><\/h3>\n<p>  <\/p>\n<ul>\n<li>\u0441\u043e\u0437\u0434\u0430\u043d \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0435\u0439 Google \u0438 \u0432\u044b\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 \u0432 2017 \u0433\u043e\u0434\u0443;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 Dart;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0440\u0438\u0441\u0443\u044f \u0432\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0434\u0432\u0438\u0436\u043a\u0430;<\/li>\n<\/ul>\n<p>  <\/p>\n<h3><b>Kivy:<\/b><\/h3>\n<p>  <\/p>\n<ul>\n<li>\u0441\u043e\u0437\u0434\u0430\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u043c Kivy \u0432 2010 \u0433\u043e\u0434\u0443;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 Python \u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u044f\u0437\u044b\u043a \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0438 UI \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u2014 KV Language;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0440\u0438\u0441\u0443\u044f \u0432\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e OpenGL ES 2.0 \u0438 SDL2;<\/li>\n<\/ul>\n<p>  \u041d\u0435\u0434\u0430\u0432\u043d\u043e \u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e\u0440\u0430\u0445 \u042e\u0442\u0443\u0431\u0430 \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044e Flutter \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u2014 <a href=\"https:\/\/www.youtube.com\/watch?v=Yix7Z9kFGQw&amp;list=WL&amp;index=2\" rel=\"nofollow noopener noreferrer\">Facebook Desktop Redesign built with Flutter Desktop<\/a>. \u041e\u0442\u043b\u0438\u0447\u043d\u043e\u0435 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u0441\u0442\u0438\u043b\u0435 material design! \u0418 \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044f \u043e\u0434\u0438\u043d \u0438\u0437 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/kivymd\/KivyMD\" rel=\"nofollow noopener noreferrer\">KivyMD<\/a> (\u043d\u0430\u0431\u043e\u0440 material \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Kivy) \u043c\u043d\u0435 \u0441\u0442\u0430\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u0440 \u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 <a href=\"https:\/\/github.com\/e200\/my-flutter-challenges\/tree\/master\/facebook_desktop\" rel=\"nofollow noopener noreferrer\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ek\/iv\/4e\/ekiv4eh5qtc8dmjceh3unan7yqg.png\"><\/div>\n<p>  <\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/zi\/zj\/br\/zizjbrwb_d12pvfmbmoh1ebnedu.png\"><\/div>\n<p>  \u041a\u0430\u043a \u0432\u044b \u0434\u0443\u043c\u0430\u0435\u0442\u0435, \u043a\u0430\u043a\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 \u0432\u044b\u0448\u0435\u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0445 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430\u0445 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Flutter \u0438 \u043a\u0430\u043a\u043e\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Kivy? \u041e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u0441\u0445\u043e\u0434\u0443 \u0442\u0440\u0443\u0434\u043d\u043e, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044f\u0440\u043a\u043e \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u043d\u044b\u0445 \u043e\u0442\u043b\u0438\u0447\u0438\u0439 \u043d\u0435\u0442. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435, \u0447\u0442\u043e \u0441\u0440\u0430\u0437\u0443 \u0431\u0440\u043e\u0441\u0430\u0435\u0442\u0441\u044f \u0432 \u0433\u043b\u0430\u0437\u0430 (\u043d\u0438\u0436\u043d\u0438\u0439 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442) \u2014 \u0432 Kivy \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0435\u0442 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0433\u043b\u0430\u0436\u0438\u0432\u0430\u043d\u0438\u044f. \u0418 \u044d\u0442\u043e \u0433\u0440\u0443\u0441\u0442\u043d\u043e, \u043d\u043e \u043d\u0435 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e. \u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0442\u044c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u0438\u0445 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0430 Dart (Flutter) \u0438 Python\/KV language (Kivy).<\/p>\n<p>  \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0442\u0435\u043f\u0435\u0440\u044c \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0438\u0437\u043d\u0443\u0442\u0440\u0438\u2026<\/p>\n<h3><b>StoryCard<\/b><\/h3>\n<p>  <\/p>\n<h3><b>Kivy<\/b><\/h3>\n<p>  \u0420\u0430\u0437\u043c\u0435\u0442\u043a\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043d\u0430 \u044f\u0437\u044b\u043a\u0435 KV-Language:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/zp\/1n\/8v\/zp1n8v8ujy3ei6n-7i_yywnnoki.png\"><\/div>\n<p>  \u0411\u0430\u0437\u043e\u0432\u044b\u0439 Python \u043a\u043b\u0430\u0441\u0441:<\/p>\n<pre><code class=\"python\">from kivy.properties import StringProperty  from kivymd.uix.relativelayout import MDRelativeLayout   class StoryCard(MDRelativeLayout):     avatar = StringProperty()     story = StringProperty()     name = StringProperty()      def on_parent(self, *args):         if not self.avatar:             self.remove_widget(self.ids.avatar) <\/code><\/pre>\n<p>  <\/p>\n<h3><b>Flutter:<\/b><\/h3>\n<p>  <\/p>\n<pre><code class=\"dart\">import 'package:flutter\/material.dart'; import 'package:flutter\/widgets.dart';  class Story extends StatefulWidget {   final String name;   final String avatar;   final String story;    const Story({     Key key,     this.name,     this.avatar,     this.story,   }) : super(key: key);    @override   _StoryState createState() =&gt; _StoryState(); }  class _StoryState extends State&lt;Story&gt; {   @override   Widget build(BuildContext context) {     return Container(       width: 150,       margin: const EdgeInsets.only(top: 30),       decoration: BoxDecoration(         borderRadius: BorderRadius.circular(30),         boxShadow: [           BoxShadow(             color: Colors.black.withOpacity(0.3),             blurRadius: 20,             offset: Offset(0, 10),           ),         ],       ),       child: Stack(         overflow: Overflow.visible,         fit: StackFit.expand,         children: [           ClipRRect(             borderRadius: BorderRadius.circular(30),             child: Image.network(               widget.story,               fit: BoxFit.cover,             ),           ),           if (widget.avatar != null)             Positioned.fill(               top: -30,               child: Align(                 alignment: Alignment.topCenter,                 child: Container(                   decoration: BoxDecoration(                     borderRadius: BorderRadius.circular(30),                     boxShadow: [                       BoxShadow(                         color: Colors.black.withOpacity(0.4),                         blurRadius: 5,                         offset: Offset(0, 3),                       ),                     ],                   ),                   child: ClipRRect(                     borderRadius: BorderRadius.circular(30),                     child: Image.network(                       widget.avatar,                       fit: BoxFit.cover,                       width: 60,                       height: 60,                     ),                   ),                 ),               ),             ),           if (widget.avatar != null)             Positioned.fill(               child: Align(                 alignment: Alignment.bottomCenter,                 child: Row(                   children: [                     Expanded(                       child: Container(                         padding: const EdgeInsets.all(15),                         decoration: BoxDecoration(                           borderRadius: BorderRadius.circular(30),                           gradient: LinearGradient(                             begin: Alignment.topCenter,                             end: Alignment.bottomCenter,                             colors: [                               Colors.transparent,                               Colors.black,                             ],                           ),                         ),                         child: widget.name != null ? Text(                           widget.name,                           textAlign: TextAlign.center,                           maxLines: 1,                           overflow: TextOverflow.ellipsis,                           style: TextStyle(                             color: Colors.white,                             fontWeight: FontWeight.bold,                           ),                         ) : SizedBox(),                       ),                     ),                   ],                 ),               ),             ),         ],       ),     );   } } <\/code><\/pre>\n<p>  \u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u043a\u043e\u0434 \u043d\u0430 Python \u0438 KV-Language \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0432\u0434\u0432\u043e\u0435 \u043a\u043e\u0440\u043e\u0447\u0435. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 Python\/Kivy, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435, \u0438\u043c\u0435\u0435\u0442 \u043e\u0431\u0449\u0438\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 31 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442. 3 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442\u0430 \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043c\u0430 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 Python \u043a\u043e\u0434, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u043e\u0435 \u2014 KV-Language. \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0430 Flutter \u2014 54 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442. \u0412\u043f\u0440\u043e\u0447\u0435\u043c, \u0437\u0434\u0435\u0441\u044c \u0443\u0434\u0438\u0432\u043b\u044f\u0442\u044c\u0441\u044f, \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u043d\u0435\u0447\u0435\u043c\u0443 \u2014 Python \u043e\u0434\u0438\u043d \u0438\u0445 \u0441\u0430\u043c\u044b\u0439 \u043b\u0430\u043a\u043e\u043d\u0438\u0447\u043d\u044b\u0445 \u044f\u0437\u044b\u043a\u043e\u0432 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043c\u0438\u0440\u0435.<\/p>\n<p>  \u041c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0441\u043f\u043e\u0440\u0438\u0442\u044c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043b\u0443\u0447\u0448\u0435: \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c UI \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 DSL \u044f\u0437\u044b\u043a\u043e\u0432 \u0438\u043b\u0438 \u043f\u0440\u044f\u043c\u043e \u0432 \u043a\u043e\u0434\u0435. \u0412 Kivy, \u043a\u0441\u0442\u0430\u0442\u0438, \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0432\u0438\u0434\u0436\u0435\u0442\u044b Python \u043a\u043e\u0434\u043e\u043c, \u043d\u043e \u044d\u0442\u043e \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0440\u043e\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435.<\/p>\n<h3><b>TopBar<\/b><\/h3>\n<p>  <b>Flutter:<\/b>  <\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/rx\/c6\/nc\/rxc6ncncdmtzgdquetlimpwizt8.gif\"><\/div>\n<p>  <b>Kivy:<\/b>  <\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/xb\/n-\/3e\/xbn-3eao8f4hhvkpjjg78g_ul6s.gif\"><\/div>\n<p>  \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0431\u0430\u0440\u0430, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u044e, \u043d\u0430 Python\/Kivy \u0437\u0430\u043d\u044f\u043b\u0430 \u0432\u0441\u0435\u0433\u043e 88 \u0441\u0442\u0440\u043e\u0447\u0435\u043a \u043a\u043e\u0434\u0430. \u041d\u0430 Dart\/Flutter \u2014 325 \u0441\u0442\u0440\u043e\u043a \u0438 9 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0437 \u0441\u0435\u0431\u044f \u044d\u0442\u043e\u0442 \u0432\u0438\u0434\u0436\u0435\u0442:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ah\/o4\/6k\/aho46kvy2qynasofoxsrwrvmy24.png\"><\/div>\n<p>  \u041b\u043e\u0433\u043e, \u0442\u0440\u0438 \u0442\u0430\u0431\u0430, \u0430\u0432\u0430\u0442\u0430\u0440, \u0442\u0440\u0438 \u0442\u0430\u0431\u0430 \u0438 \u043e\u0434\u0438\u043d \u0442\u0430\u0431 \u2014 \u043a\u043d\u043e\u043f\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0430\u0431\u0430 \u0441 \u0430\u043d\u0438\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c:<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ld\/88\/ak\/ld88akqhdwaykrsz5e1v5li8wyu.png\"><\/div>\n<p>  \u0410\u043d\u0438\u043c\u0430\u0446\u0438\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0438 \u0441\u043c\u0435\u043d\u0430 \u0442\u0438\u043f\u0430 \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u043c\u044b\u0448\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432 Python \u0444\u0430\u0439\u043b\u0435 \u0432 \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u043e\u043c \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u043e\u043c \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0438 \u043a\u043b\u0430\u0441\u0441\u0435:<\/p>\n<pre><code class=\"python\">from kivy.animation import Animation from kivy.properties import StringProperty, BooleanProperty from kivy.core.window import Window  from kivymd.uix.boxlayout import MDBoxLayout from kivymd.uix.behaviors import FocusBehavior   class Tab(FocusBehavior, MDBoxLayout):     icon = StringProperty()     active = BooleanProperty(False)      def on_enter(self):         Window.set_system_cursor(&quot;hand&quot;)      def on_leave(self):         Window.set_system_cursor(&quot;arrow&quot;)      def on_active(self, instance, value):         Animation(             opacity=value,             width=self.width if value else 0,             d=0.25,             t=&quot;in_sine&quot; if value else &quot;out_sine&quot;,         ).start(self.ids.separator) <\/code><\/pre>\n<p>  \u041c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0430\u043d\u0438\u043c\u0438\u0440\u0443\u0435\u043c \u0448\u0438\u0440\u0438\u043d\u0443 \u0438 opacity \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u0430 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043a\u043d\u043e\u043f\u043a\u0438 (active). \u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435 \u044d\u043a\u0440\u0430\u043d\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<pre><code class=\"python\">class FacebookDesktop(ThemableBehavior, MDScreen):     def set_active_tab(self, instance_tab):         for widget in self.ids.tab_box.children:             if issubclass(widget.__class__, MDBoxLayout):                 if widget == instance_tab:                     widget.active = True                 else:                     widget.active = False <\/code><\/pre>\n<p>  <b>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0430 Kivy:<\/b><\/p>\n<p>  <a href=\"https:\/\/habr.com\/ru\/post\/528154\/\">\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u0438\u0437\u0430\u0439\u043d. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0439 \u0432 Kivy<\/a><br \/>  <a href=\"https:\/\/habr.com\/ru\/post\/529364\/\">\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043d\u0430 Python. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0439 \u0432 Kivy. Part 2<\/a><\/p>\n<p>  <b>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430 Dart\/Flutter.<\/b><\/p>\n<p>  \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043a\u043e\u0434\u0430 \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e, \u044f \u0441\u043f\u0440\u044f\u0442\u0430\u043b \u0432\u0441\u0435 \u043f\u043e\u0434 \u0441\u043f\u043e\u0439\u043b\u0435\u0440\u044b:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">app_logo.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\">import 'package:flutter\/material.dart'; import 'package:flutter\/widgets.dart';  class AppLogo extends StatelessWidget {   @override   Widget build(BuildContext context) {     return Container(       decoration: BoxDecoration(         borderRadius: BorderRadius.circular(10),         boxShadow: [           BoxShadow(             color: Colors.blue.withOpacity(.6),             blurRadius: 5,             spreadRadius: 1,           ),         ],       ),       child: ClipRRect(         borderRadius: BorderRadius.circular(10),         child: Image.asset(           'assets\/images\/facebook_logo.jpg',           width: 30,           height: 30,         ),       ),     );   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">avatar.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\">import 'package:flutter\/material.dart'; import 'package:flutter\/rendering.dart'; import 'package:flutter\/widgets.dart';  class TopBarAvatar extends StatefulWidget {   @override   _TopBarAvatarState createState() =&gt; _TopBarAvatarState(); }  class _TopBarAvatarState extends State&lt;TopBarAvatar&gt;     with SingleTickerProviderStateMixin {   Animation&lt;Color&gt; _animation;   AnimationController _animationController;    @override   void initState() {     _animationController = AnimationController(       vsync: this,       duration: Duration(milliseconds: 150),     );      _animation = ColorTween(       begin: Colors.grey.withOpacity(.4),       end: Colors.blue.withOpacity(.6),     ).animate(_animationController);      _animation.addListener(() {       setState(() {});     });      super.initState();   }    @override   Widget build(BuildContext context) {     return MouseRegion(       onHover: (event) {         setState(() {           _animationController.forward();         });       },       onExit: (event) {         setState(() {           _animationController.reverse();         });       },       cursor: SystemMouseCursors.click,       child: Padding(         padding: const EdgeInsets.symmetric(horizontal: 15),         child: Container(           decoration: BoxDecoration(             borderRadius: BorderRadius.circular(15),             boxShadow: [               BoxShadow(                 color: _animation.value,                 blurRadius: 10,                 spreadRadius: 0,               ),             ],           ),           child: ClipRRect(             borderRadius: BorderRadius.circular(15),             child: Image.asset(               'assets\/images\/avatar.jpg',               width: 50,               height: 50,             ),           ),         ),       ),     );   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">button.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\">import 'package:flutter\/material.dart'; import 'package:flutter\/rendering.dart'; import 'package:flutter\/widgets.dart';  class TopBarButton extends StatefulWidget {   final IconData icon;   final bool isActive;   final Function onTap;    const TopBarButton({     Key key,     this.icon,     this.isActive = false,     this.onTap,   }) : super(key: key);    @override   _TopBarButtonState createState() =&gt; _TopBarButtonState(); }  class _TopBarButtonState extends State&lt;TopBarButton&gt;     with SingleTickerProviderStateMixin {   Animation&lt;Color&gt; _animation;   AnimationController _animationController;    @override   void initState() {     _animationController = AnimationController(       vsync: this,       duration: Duration(milliseconds: 150),     );      _animation = ColorTween(       begin: Colors.grey.withOpacity(.6),       end: Colors.blue.withOpacity(.6),     ).animate(_animationController);      _animation.addListener(() {       setState(() {});     });      super.initState();   }    @override   void didUpdateWidget(TopBarButton oldWidget) {     if (widget.isActive) {       _animationController.forward();     } else {       _animationController.reverse();     }      super.didUpdateWidget(oldWidget);   }    @override   Widget build(BuildContext context) {     return GestureDetector(       onTap: widget.onTap,       child: MouseRegion(         cursor: SystemMouseCursors.click,         child: Container(           height: 80,           child: Stack(             alignment: Alignment.center,             children: [               Padding(                 padding: const EdgeInsets.symmetric(horizontal: 30),                 child: Icon(                   widget.icon,                   color: _animation.value,                 ),               ),               Positioned(                 bottom: -1,                 child: Align(                   alignment: Alignment.bottomCenter,                   child: AnimatedContainer(                     duration: Duration(milliseconds: 50),                     curve: Curves.easeInOut,                     decoration: BoxDecoration(                       color: _animation.value,                       borderRadius: BorderRadius.circular(5),                       boxShadow: [                         BoxShadow(                           color: _animation.value,                           blurRadius: 5,                           offset: Offset(0, 2),                         ),                       ],                     ),                     width: widget.isActive ? 50 : 0,                     height: 4,                   ),                 ),               ),             ],           ),         ),       ),     );   }    @override   void dispose() {     _animationController.dispose();      super.dispose();   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">widget.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\">import 'package:facebook_desktop\/screens\/home\/components\/top_bar\/app_logo.dart'; import 'package:facebook_desktop\/screens\/home\/components\/top_bar\/avatar.dart'; import 'package:facebook_desktop\/screens\/home\/components\/top_bar\/button.dart'; import 'package:flutter\/material.dart'; import 'package:flutter\/widgets.dart'; import 'package:flutter_feather_icons\/flutter_feather_icons.dart';  class TopBar extends StatefulWidget {   @override   _TopBarState createState() =&gt; _TopBarState(); }  class _TopBarState extends State&lt;TopBar&gt; {   int _selectedPage = 0;    @override   Widget build(BuildContext context) {     return Container(       color: Colors.white,       padding: const EdgeInsets.symmetric(         horizontal: 30,       ),       child: Row(         children: [           Expanded(             flex: 1,             child: Align(               alignment: Alignment.centerLeft,               child: AppLogo(),             ),           ),           Expanded(             flex: 6,             child: Row(               mainAxisAlignment: MainAxisAlignment.center,               children: [                 TopBarButton(                   icon: FeatherIcons.home,                   isActive: _selectedPage == 0,                   onTap: () {                     setState(() {                       _selectedPage = 0;                     });                   },                 ),                 TopBarButton(                   icon: FeatherIcons.youtube,                   isActive: _selectedPage == 1,                   onTap: () {                     setState(() {                       _selectedPage = 1;                     });                   },                 ),                 TopBarButton(                   icon: FeatherIcons.grid,                   isActive: _selectedPage == 2,                   onTap: () {                     setState(() {                       _selectedPage = 2;                     });                   },                 ),                 TopBarAvatar(),                 TopBarButton(                   icon: FeatherIcons.users,                   isActive: _selectedPage == 3,                   onTap: () {                     setState(() {                       _selectedPage = 3;                     });                   },                 ),                 TopBarButton(                   icon: FeatherIcons.zap,                   isActive: _selectedPage == 4,                   onTap: () {                     setState(() {                       _selectedPage = 4;                     });                   },                 ),                 TopBarButton(                   icon: FeatherIcons.smile,                   isActive: _selectedPage == 5,                   onTap: () {                     setState(() {                       _selectedPage = 5;                     });                   },                 ),               ],             ),           ),           Expanded(             flex: 1,             child: Align(               alignment: Alignment.centerRight,               child: IconButton(                 color: Colors.grey.withOpacity(.6),                 icon: Icon(FeatherIcons.settings),                 onPressed: () {},               ),             ),           ),         ],       ),     );   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <b>ChatCard<\/b> (Kivy, Flutter)  <\/p>\n<div class=\"scrollable-table\">\n<table>\n<tbody>\n<tr>\n<td>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/os\/rk\/qs\/osrkqsc0xzrpru3qzf6qu3pgwqw.gif\"><\/div>\n<\/td>\n<td>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/jp\/1h\/1m\/jp1h1mk2yo5omgv_j1mp62pnue8.gif\"><\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>  \u0410\u043d\u0438\u043c\u0430\u0446\u0438\u044f \u0441\u0434\u0432\u0438\u0433\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u0438\u0434\u0436\u0435\u0442\u0430 (parent) \u043f\u0440\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0444\u043e\u043a\u0443\u0441\u0430 \u0438 \u0430\u043d\u0444\u043e\u043a\u0443\u0441\u0430 (on_enter, on_leave):<\/p>\n<pre><code class=\"json\">on_enter: Animation(x=root.parent.x + dp(12), d=0.4, t=&quot;out_cubic&quot;).start(root) on_leave: Animation(x=root.parent.x + dp(24), d=0.4, t=&quot;out_cubic&quot;).start(root) <\/code><\/pre>\n<p>  <\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/k3\/rt\/9s\/k3rt9ss7tfecdlmv4kannvesv0y.png\"><\/div>\n<p>  \u0418 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 Python:<\/p>\n<pre><code class=\"python\">from kivy.core.window import Window from kivy.properties import StringProperty  from FacebookDesktop.components.cards.fake_card import FakeCard   class ChatCard(FakeCard):     avatar = StringProperty()     text = StringProperty()     name = StringProperty()      def on_enter(self):         Window.set_system_cursor(&quot;hand&quot;)      def on_leave(self):         Window.set_system_cursor(&quot;arrow&quot;) <\/code><\/pre>\n<p>  \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Python\/Kivy \u2014 60 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430, \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Dart\/Flutter \u2014 182 \u0441\u0442\u0440\u043e\u043a\u0438 \u043a\u043e\u0434\u0430.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">chat_card.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\">import 'package:ezanimation\/ezanimation.dart'; import 'package:facebook_desktop\/components\/user_tile.dart'; import 'package:flutter\/material.dart'; import 'package:flutter\/rendering.dart'; import 'package:flutter_feather_icons\/flutter_feather_icons.dart';  class ChatCard extends StatefulWidget {   final String image;   final String name;   final String message;   final EdgeInsets padding;    const ChatCard({     Key key,     this.image,     this.name,     this.message,     this.padding,   }) : super(key: key);    @override   _ChatCardState createState() =&gt; _ChatCardState(); }  class _ChatCardState extends State&lt;ChatCard&gt; {   EzAnimation _animation;    @override   void initState() {     _animation = EzAnimation(       0.0,       -5.0,       Duration(milliseconds: 200),       curve: Curves.easeInOut,       context: context,     );      _animation.addListener(() {       setState(() {});     });      super.initState();   }    @override   Widget build(BuildContext context) {     return Transform.translate(       offset: Offset(_animation.value, 0),       child: MouseRegion(         cursor: SystemMouseCursors.click,         onEnter: (event) {           _animation.start();         },         onExit: (event) {           _animation.reverse();         },         child: Padding(           padding: widget.padding ?? const EdgeInsets.all(15),           child: Container(             width: 250,             padding: const EdgeInsets.all(15),             decoration: BoxDecoration(               color: Colors.white,               borderRadius: BorderRadius.circular(10),               boxShadow: [                 BoxShadow(                   color: Colors.black.withOpacity(.1),                   blurRadius: 15,                   offset: Offset(0, 8),                 ),               ],             ),             child: Column(               crossAxisAlignment: CrossAxisAlignment.start,               children: [                 UserTile(                   name: widget.name,                   image: widget.image,                   trailing: Icon(                     FeatherIcons.messageSquare,                     color: Colors.blue,                     size: 14,                   ),                 ),                 SizedBox(                   height: 10,                 ),                 Text(                   widget.message,                   style: TextStyle(color: Colors.grey, fontSize: 12),                   maxLines: 3,                   overflow: TextOverflow.ellipsis,                 ),               ],             ),           ),         ),       ),     );   }    @override   void dispose() {     _animation.dispose();      super.dispose();   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">user_tile.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\"> import 'package:facebook_desktop\/screens\/home\/components\/section.dart'; import 'package:flutter\/material.dart';  class UserTile extends StatelessWidget {   final String name;   final String image;   final Widget trailing;    const UserTile({     Key key,     this.name,     this.image,     this.trailing,   }) : super(key: key);    @override   Widget build(BuildContext context) {     return Row(       crossAxisAlignment: CrossAxisAlignment.start,       children: [         Container(           margin: const EdgeInsets.only(right: 10),           decoration: BoxDecoration(             color: Colors.white,             borderRadius: BorderRadius.circular(10),             boxShadow: [               BoxShadow(                 color: Colors.black.withOpacity(.1),                 blurRadius: 5,                 offset: Offset(0, 2),               ),             ],           ),           child: ClipRRect(             borderRadius: BorderRadius.circular(5),             child: Image(               image: NetworkImage(                 image,               ),               fit: BoxFit.cover,               height: 50,               width: 50,             ),           ),         ),         Column(           crossAxisAlignment: CrossAxisAlignment.start,           children: [             SectionTitle(               title: name,             ),             SizedBox(               height: 5,             ),             Text(               '12 min ago',               style: TextStyle(color: Colors.grey),             ),           ],         ),         if (trailing != null)         Expanded(           child: Align(             alignment: Alignment.topRight,             child: trailing           ),         ),       ],     );   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041d\u043e \u043d\u0435 \u0432\u0441\u0435 \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e, \u043a\u0430\u043a \u043a\u0430\u0436\u0435\u0442\u0441\u044f. \u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u044f \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b, \u0447\u0442\u043e \u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 KivyMD \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043a\u043d\u043e\u043f\u043a\u0438 \u0441 \u0442\u0438\u043f\u043e\u043c \u00abbadge\u00bb. \u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043d\u0430 Flutter, \u043a\u0441\u0442\u0430\u0442\u0438, \u0442\u043e\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043a\u043d\u043e\u043f\u043a\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/au\/s8\/mr\/aus8mrhdd1bjt_49_zgvbmotubk.png\"><\/div>\n<p>  \u0411\u0430\u0437\u043e\u0432\u044b\u0439 Python \u043a\u043b\u0430\u0441\u0441:<\/p>\n<pre><code class=\"python\">from kivy.properties import StringProperty  from kivymd.uix.relativelayout import MDRelativeLayout   class BadgeButton(MDRelativeLayout):     icon = StringProperty()     text = StringProperty() <\/code><\/pre>\n<p>  \u0418 \u0443\u0436\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043b\u0435\u0432\u0443\u044e \u043f\u0430\u043d\u0435\u043b\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/x7\/kp\/lo\/x7kploihaxbkvjimzr-ahhgrvau.png\"><\/p>\n<p>  \u0414\u0430\u0436\u0435 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f, \u0447\u0442\u043e \u043c\u043d\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0435 \u043a\u043d\u043e\u043f\u043a\u0438 \u0442\u0438\u043f\u0430 \u00abbadge\u00bb, \u043a\u043e\u0434 \u043b\u0435\u0432\u043e\u0439 \u043f\u0430\u043d\u0435\u043b\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0430 Python\/Kivy \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f \u043a\u043e\u0440\u043e\u0447\u0435 \u2014 58 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430, \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430 Dart\/Flutter \u2014 97 \u0441\u0442\u0440\u043e\u043a.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">button.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\">import 'package:flutter\/material.dart';  class LeftBarButton extends StatelessWidget {   final IconData icon;   final String badge;    const LeftBarButton({     Key key,     this.icon,     this.badge,   }) : super(key: key);    @override   Widget build(BuildContext context) {     return GestureDetector(       child: Stack(         children: [           Container(             padding: const EdgeInsets.all(10),             child: Icon(               icon,               color: Colors.grey.withOpacity(.6),             ),           ),           if (badge != null)             Positioned(               top: 5,               right: 2,               child: Container(                 padding: const EdgeInsets.all(3),                 decoration: BoxDecoration(                   borderRadius: BorderRadius.circular(100),                   color: Colors.blue,                 ),                 child: Text(                   badge,                   style: TextStyle(                     color: Colors.white,                     fontSize: 10,                   ),                 ),               ),             )         ],       ),     );   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">widget.dart<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"dart\">import 'package:facebook_desktop\/screens\/home\/left_bar\/button.dart'; import 'package:flutter\/material.dart'; import 'package:flutter\/widgets.dart'; import 'package:flutter_feather_icons\/flutter_feather_icons.dart';  class LeftBar extends StatelessWidget {   @override   Widget build(BuildContext context) {     return Container(       margin: const EdgeInsets.all(30),       padding: const EdgeInsets.all(5),       decoration: BoxDecoration(         color: Colors.white,         borderRadius: BorderRadius.circular(50),         boxShadow: [           BoxShadow(             color: Colors.grey.withOpacity(.1),             blurRadius: 2,             offset: Offset(0, 4),           )         ],       ),       child: Column(         mainAxisSize: MainAxisSize.min,         children: [           LeftBarButton(             icon: FeatherIcons.mail,             badge: '10',           ),           SizedBox(             height: 5,           ),           LeftBarButton(             icon: FeatherIcons.search,           ),           SizedBox(             height: 5,           ),           LeftBarButton(             icon: FeatherIcons.bell,             badge: '20',           ),         ],       ),     );   } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0411\u0435\u0437\u0443\u0441\u043b\u043e\u0432\u043d\u043e \u044f \u043d\u0435 \u0443\u043c\u0430\u043b\u044f\u044e \u0434\u043e\u0441\u0442\u043e\u0438\u043d\u0441\u0442\u0432 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Flutter. \u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439! \u042f \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u0445\u043e\u0442\u0435\u043b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c Python \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c, \u0447\u0442\u043e \u043e\u043d\u0438 \u043c\u043e\u0433\u0443\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0435 \u0436\u0435 \u0441\u0430\u043c\u044b\u0435 \u0432\u0435\u0449\u0438, \u0447\u0442\u043e \u0438 \u0432\u043e Flutter, \u043d\u043e \u043d\u0430 \u0438\u0445 \u043b\u044e\u0431\u0438\u043c\u043e\u043c \u044f\u0437\u044b\u043a\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Kivy \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 KivyMD. \u0427\u0442\u043e \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c, \u0442\u043e \u0437\u0434\u0435\u0441\u044c \u0441\u0442\u043e\u0438\u0442 \u043f\u0440\u0438\u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e Flutter \u043f\u0440\u0435\u0432\u043e\u0441\u0445\u043e\u0434\u0438\u0442 Kivy \u0432 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u0440\u0430\u0431\u043e\u0442\u044b. \u041d\u043e \u044d\u0442\u043e \u0443\u0436\u0435 \u0443\u0436\u0435 \u0434\u0440\u0443\u0433\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f\u2026 \u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 <a href=\"https:\/\/github.com\/HeaTTheatR\/Articles\/tree\/main\/FacebookDesktop\" rel=\"nofollow noopener noreferrer\">Facebook Desktop Redesign built with Flutter Desktop<\/a> \u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 Python\/Kivy\/KivyMD.<\/p>\n<div class=\"oembed\"><iframe allowfullscreen id=\"604b2fa495bc5d2dedbee1b1\" src=\"https:\/\/embedd.srv.habr.com\/iframe\/604b2fa495bc5d2dedbee1b1\"><\/iframe><\/div>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/546684\/\"> https:\/\/habr.com\/ru\/post\/546684\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/i2\/8_\/qf\/i28_qff8276scfhag1p7wksllx4.png\"><\/div>\n<p>  <a href=\"https:\/\/github.com\/kivy\/kivy\" rel=\"nofollow noopener noreferrer\">Kivy<\/a> \u0438 <a href=\"https:\/\/github.com\/flutter\/flutter\" rel=\"nofollow noopener noreferrer\">Flutter<\/a> \u2014 \u0434\u0432\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u0434\u043b\u044f \u043a\u0440\u043e\u0441\u0441\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<h3><b>Flutter:<\/b><\/h3>\n<p>  <\/p>\n<ul>\n<li>\u0441\u043e\u0437\u0434\u0430\u043d \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0435\u0439 Google \u0438 \u0432\u044b\u043f\u0443\u0449\u0435\u043d\u043d\u044b\u0439 \u0432 2017 \u0433\u043e\u0434\u0443;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 Dart;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0440\u0438\u0441\u0443\u044f \u0432\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0434\u0432\u0438\u0436\u043a\u0430;<\/li>\n<\/ul>\n<p>  <\/p>\n<h3><b>Kivy:<\/b><\/h3>\n<p>  <\/p>\n<ul>\n<li>\u0441\u043e\u0437\u0434\u0430\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e\u043c Kivy \u0432 2010 \u0433\u043e\u0434\u0443;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 Python \u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u044f\u0437\u044b\u043a \u0434\u043b\u044f \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0438 UI \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u2014 KV Language;<\/li>\n<\/ul>\n<p>  <\/p>\n<ul>\n<li>\u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b, \u0440\u0438\u0441\u0443\u044f \u0432\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e OpenGL ES 2.0 \u0438 SDL2;<\/li>\n<\/ul>\n<p>  \u041d\u0435\u0434\u0430\u0432\u043d\u043e \u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e\u0440\u0430\u0445 \u042e\u0442\u0443\u0431\u0430 \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 \u0432\u0438\u0434\u0435\u043e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u044e Flutter \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u2014 <a href=\"https:\/\/www.youtube.com\/watch?v=Yix7Z9kFGQw&amp;list=WL&amp;index=2\" rel=\"nofollow noopener noreferrer\">Facebook Desktop Redesign built with Flutter Desktop<\/a>. \u041e\u0442\u043b\u0438\u0447\u043d\u043e\u0435 \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u0441\u0442\u0438\u043b\u0435 material design! \u0418 \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044f \u043e\u0434\u0438\u043d \u0438\u0437 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"https:\/\/github.com\/kivymd\/KivyMD\" rel=\"nofollow noopener noreferrer\">KivyMD<\/a> (\u043d\u0430\u0431\u043e\u0440 material \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 Kivy) \u043c\u043d\u0435 \u0441\u0442\u0430\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441. \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u0440 \u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 <a href=\"https:\/\/github.com\/e200\/my-flutter-challenges\/tree\/master\/facebook_desktop\" rel=\"nofollow noopener noreferrer\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a>.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-319481","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/319481","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=319481"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/319481\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=319481"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=319481"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=319481"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}