{"id":336722,"date":"2022-08-08T15:00:28","date_gmt":"2022-08-08T15:00:28","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=336722"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=336722","title":{"rendered":"<span>\u0410\u043d\u0438\u043c\u0430\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043a\u0430\u0440\u0443\u0441\u0435\u043b\u0438 \u0432\u043e Flutter<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u042f \u2014 \u0422\u0438\u043c, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 \u0413\u0443\u0434\u0438\u0442\u0432\u043e\u0440\u043a\u0441. \u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435-\u0433\u0438\u0434 \u043f\u043e \u0440\u0435\u0441\u0442\u043e\u0440\u0430\u043d\u0430\u043c, \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0430\u043d\u0438\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0440\u0443\u0441\u0435\u043b\u044c \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a. \u041d\u0430 \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432\u043e Flutter \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u043a\u0430\u0440\u0443\u0441\u0435\u043b\u044c. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u2014 \u043d\u0438\u0436\u0435:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/257\/dbe\/158\/257dbe158fd43ad4703c2a8c482f7da4.gif\" alt=\"\u041f\u0440\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u0438\u043b\u043a\u0438-\u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0438.\" title=\"\u041f\u0440\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u0438\u043b\u043a\u0438-\u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0438.\" width=\"688\" height=\"516\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/257\/dbe\/158\/257dbe158fd43ad4703c2a8c482f7da4.gif\"\/><figcaption>\u041f\u0440\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u0438\u043b\u043a\u0438-\u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0438.<\/figcaption><\/figure>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043f\u043e\u043b\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0430.<\/p>\n<p>\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u0430\u0440\u0443\u0441\u0435\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0441\u043a\u0440\u043e\u043b\u043b\u0430: \u043f\u0440\u0438 \u0435\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/AnimatedBuilder\/builder.html\" rel=\"noopener noreferrer nofollow\">build-\u0444\u0443\u043d\u043a\u0446\u0438\u044f<\/a> \u0432\u0441\u0435\u0433\u043e \u0432\u0438\u0434\u0436\u0435\u0442\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u0430 \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0437\u0430\u0442\u0440\u0430\u0442\u043d\u043e\u043c\u0443 \u0440\u0435\u0440\u0435\u043d\u0434\u0435\u0440\u0443. \u0427\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043b\u0438\u0448\u043d\u0438\u0445 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043e\u043a, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0436\u0435\u0442 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/AnimatedBuilder-class.html\" rel=\"noopener noreferrer nofollow\">AnimatedBuilder<\/a> \u2014 \u043e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0430\u0441\u0447\u0451\u0442\u044b \u043d\u0435 \u0432 \u043a\u043e\u0440\u043d\u0435 \u0432\u0438\u0434\u0436\u0435\u0442\u0430, \u0430 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0431\u043b\u0438\u0437\u043a\u043e \u043a \u0442\u043e\u043c\u0443 \u0432\u0438\u0434\u0436\u0435\u0442\u0443 \u0432 \u0434\u0435\u0440\u0435\u0432\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u044d\u0442\u0438\u0445 \u0440\u0430\u0441\u0447\u0451\u0442\u043e\u0432.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043b\u044f \u0441\u043a\u0440\u043e\u043b\u043b\u044f\u0449\u0435\u0433\u043e\u0441\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageView-class.html\" rel=\"noopener noreferrer nofollow\">PageView<\/a> \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageController-class.html\" rel=\"noopener noreferrer nofollow\">PageController<\/a>. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageController\/viewportFraction.html\" rel=\"noopener noreferrer nofollow\">viewportFraction<\/a> \u0431\u0443\u0434\u0435\u0442 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a.<\/p>\n<pre><code class=\"dart\">\/\/ main.dart  class _HomeState extends State&lt;Home> {   static const imageHeight = 278.0;   final _controller = PageController(viewportFraction: 0.68);   ... }<\/code><\/pre>\n<p>\u0422\u0435\u043a\u0443\u0449\u0430\u044f \u043f\u043e\u0437\u0438\u0446\u0438\u044f \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0435 \u043a\u043b\u0430\u0441\u0441\u0430 <a href=\"https:\/\/api.flutter.dev\/flutter\/foundation\/ValueNotifier-class.html\" rel=\"noopener noreferrer nofollow\">ValueNotifier<\/a> (\u043e\u043d \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043a\u043b\u0430\u0441\u0441\u0443 <a href=\"https:\/\/api.flutter.dev\/flutter\/foundation\/ChangeNotifier-class.html\" rel=\"noopener noreferrer nofollow\">ChangeNotifier<\/a>, \u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442 \u0437\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f).<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart   class _CoverCardState extends State&lt;CoverCard> {   ...     final ValueNotifier&lt;double> _scrollPosition = ValueNotifier&lt;double>(0.0);     void _onScrollPositionChanged() {     setState(() => _scrollPosition.value = widget.pageController.page ?? 0.0);   }     ... }<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f, \u043e\u043d \u043e\u043f\u043e\u0432\u0435\u0441\u0442\u0438\u0442 \u0441\u0432\u043e\u0438\u0445 \u043b\u0438\u0441\u0442\u0435\u043d\u0435\u0440\u043e\u0432.<\/p>\n<p>\u041f\u043e\u0434\u0446\u0435\u043f\u0438\u043c \u043a \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443 \u043b\u0438\u0441\u0442\u0435\u043d\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0432 <code>_scrollPosition<\/code> (\u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043e\u0442\u0446\u0435\u043f\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u044b\u043b\u043e \u0443\u0442\u0435\u0447\u0435\u043a \u043f\u0430\u043c\u044f\u0442\u0438).<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  @override void initState() {   super.initState();   widget.pageController.addListener(_onScrollPositionChanged); }  @override void dispose() {   super.dispose();   widget.pageController.removeListener(_onScrollPositionChanged); }<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u0430 build-\u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>AnimatedBuilder<\/code>, \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c <code>_scrollPosition<\/code> \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>animation<\/code>. <\/p>\n<p>\u041e\u0434\u0438\u043d <code>AnimatedBuilder<\/code> \u0440\u0438\u0441\u0443\u0435\u0442 \u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0443:<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  @override Widget build(BuildContext context) {   ...    return Stack(     ...     children: [       Positioned(         ...         ),       ),       AnimatedBuilder(         animation: _scrollPosition,         builder: (BuildContext context, Widget? child) {           return Positioned(             top: arrowPadding - 33,             left: widthCard + _getArrowOffset(),             child: Transform.rotate(               angle: pi \/ 2.0,               child: const ImageIcon(                 AssetImage(                   'assets\/icons\/arrow_insider.png',                 ),                 color: Color(0xfff9b9ad),                 size: 62,               ),             ),           );         },       ),       ...     ],   ); }<\/code><\/pre>\n<p>\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0435\u043b\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u0440\u0430\u0441\u0447\u0435\u0442\u044b \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>_getArrowOffset<\/code>. \u0415\u0441\u043b\u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0430 \u043d\u0435 \u0432 \u0444\u043e\u043a\u0443\u0441\u0435 \u0438 \u043d\u0435 \u0441\u0431\u043e\u043a\u0443 \u043e\u0442 \u0444\u043e\u043a\u0443\u0441\u0430, \u0442\u043e \u0441\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u043e\u0439 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442.<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  class _CoverCardState extends State&lt;CoverCard> {   static const double _defaultArrowOffset = .0;   static const double _focusArrowOffset = 40;   static const cardPadding = 111.0;     final ValueNotifier&lt;double> _scrollPosition = ValueNotifier&lt;double>(0.0);     void _onScrollPositionChanged() {     setState(() => _scrollPosition.value = widget.pageController.page ?? 0.0);   }     double _getArrowOffset() {     final scrollPosition = _scrollPosition.value;     final currentPosition = scrollPosition.floor();       final delta = scrollPosition - currentPosition;       final forwardAnimationOffest =         Curves.ease.transform(1 - delta) * _focusArrowOffset;     final backwardAnimationOffest =         Curves.ease.transform(delta) * _focusArrowOffset;       var animatedArrowOffset = _defaultArrowOffset;       if (widget.index == currentPosition) {       \/\/\/ Closest to focus       animatedArrowOffset = forwardAnimationOffest;     } else if (widget.index == currentPosition + 1) {       \/\/\/ Left or right sided from central card       animatedArrowOffset = backwardAnimationOffest;     }       return animatedArrowOffset;   } }<\/code><\/pre>\n<p>\u0414\u0440\u0443\u0433\u043e\u0439 <code>AnimatedBuilder<\/code> \u0440\u0438\u0441\u0443\u0435\u0442 \u0442\u0435\u043a\u0441\u0442. \u0415\u043c\u0443 \u0442\u0430\u043a \u0436\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>animation<\/code>. <\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  @override Widget build(BuildContext context) {   final deviceWidth = MediaQuery.of(context).size.width;   final width = deviceWidth * 0.68;   final widthCard = deviceWidth * 0.48;   final heightCard = widthCard * 1.4;    final arrowPadding =       widget.imageHeight \/ 2 + (heightCard + cardPadding) \/ 2;    return Stack(     ...     children: [       Positioned(         ...       ),       ...       AnimatedBuilder(         animation: _scrollPosition,         builder: (BuildContext context, Widget? child) {           return Positioned(             width: width,             top: heightCard + 85,             child: Opacity(               opacity: getTextOpacity(),               child: Column(                 children: [                   Text(                     \"Jane Doe\",                     textAlign: TextAlign.center,                     style: _titleTextStyle,                     maxLines: 2,                     overflow: TextOverflow.ellipsis,                   ),                   const SizedBox(height: 12),                   Text(                     \"Lorem ipsum dolor sit amet\",                     textAlign: TextAlign.center,                     style: _subtitleTextStyle,                     maxLines: 4,                     overflow: TextOverflow.ellipsis,                   ),                 ],               ),             ),           );         },       ),     ],   ); }<\/code><\/pre>\n<p>\u0422\u0435\u043a\u0441\u0442 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0432 \u0432\u0438\u0434\u0436\u0435\u0442 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Opacity-class.html\" rel=\"noopener noreferrer nofollow\">Opacity<\/a>: \u043e\u043d \u043c\u0435\u043d\u044f\u0435\u0442 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u0451\u0440\u043d\u0443\u0442\u044b\u0445 \u0432 \u043d\u0435\u0433\u043e \u0432\u0438\u0434\u0436\u0435\u0442\u043e\u0432 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438. \u042d\u0442\u043e \u0437\u0430\u0442\u0440\u043e\u043d\u0435\u0442 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0443 \u0432 \u0444\u043e\u043a\u0443\u0441\u0435 \u0438 \u0442\u0435, \u0447\u0442\u043e \u043e\u0442 \u043d\u0435\u0435 \u043f\u043e \u0431\u043e\u043a\u0430\u043c:<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  double getTextOpacity() {   final scrollPosition = _scrollPosition.value;   final currentPosition = scrollPosition.floor();   final delta = scrollPosition - currentPosition;    if (widget.index == currentPosition) return 1 - delta;    if (currentPosition + 1 == widget.index) return delta;    return 0; }<\/code><\/pre>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430, \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0438, \u0438 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0430.<\/p>\n<p>\u041a\u043e\u0434 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u2014 \u0432 <a href=\"https:\/\/github.com\/gooditcollective\/flutter-animations-example\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 GitHub<\/a>. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u0438\u0441\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u2014 \u0441 \u0443\u0434\u043e\u0432\u043e\u043b\u044c\u0441\u0442\u0432\u0438\u0435\u043c \u043e\u0442\u0432\u0435\u0447\u0443.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \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\/681430\/\"> https:\/\/habr.com\/ru\/post\/681430\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u042f \u2014 \u0422\u0438\u043c, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432 \u0413\u0443\u0434\u0438\u0442\u0432\u043e\u0440\u043a\u0441. \u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435-\u0433\u0438\u0434 \u043f\u043e \u0440\u0435\u0441\u0442\u043e\u0440\u0430\u043d\u0430\u043c, \u043c\u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0430\u043d\u0438\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0440\u0443\u0441\u0435\u043b\u044c \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a. \u041d\u0430 \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u044f \u043f\u043e\u043a\u0430\u0436\u0443, \u043a\u0430\u043a \u0432\u043e Flutter \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u043a\u0430\u0440\u0443\u0441\u0435\u043b\u044c. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u2014 \u043d\u0438\u0436\u0435:<\/p>\n<figure class=\"full-width\"><figcaption>\u041f\u0440\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u0438\u043b\u043a\u0438-\u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0438.<\/figcaption><\/figure>\n<p>\u0412 \u043a\u043e\u043d\u0446\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043f\u043e\u043b\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0430.<\/p>\n<p>\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u0430\u0440\u0443\u0441\u0435\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0441\u043a\u0440\u043e\u043b\u043b\u0430: \u043f\u0440\u0438 \u0435\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/AnimatedBuilder\/builder.html\" rel=\"noopener noreferrer nofollow\">build-\u0444\u0443\u043d\u043a\u0446\u0438\u044f<\/a> \u0432\u0441\u0435\u0433\u043e \u0432\u0438\u0434\u0436\u0435\u0442\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u0430 \u044d\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0437\u0430\u0442\u0440\u0430\u0442\u043d\u043e\u043c\u0443 \u0440\u0435\u0440\u0435\u043d\u0434\u0435\u0440\u0443. \u0427\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043b\u0438\u0448\u043d\u0438\u0445 \u043f\u0435\u0440\u0435\u0440\u0438\u0441\u043e\u0432\u043e\u043a, \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0438\u0434\u0436\u0435\u0442 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/AnimatedBuilder-class.html\" rel=\"noopener noreferrer nofollow\">AnimatedBuilder<\/a> \u2014 \u043e\u043d \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0430\u0441\u0447\u0451\u0442\u044b \u043d\u0435 \u0432 \u043a\u043e\u0440\u043d\u0435 \u0432\u0438\u0434\u0436\u0435\u0442\u0430, \u0430 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0431\u043b\u0438\u0437\u043a\u043e \u043a \u0442\u043e\u043c\u0443 \u0432\u0438\u0434\u0436\u0435\u0442\u0443 \u0432 \u0434\u0435\u0440\u0435\u0432\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u044d\u0442\u0438\u0445 \u0440\u0430\u0441\u0447\u0451\u0442\u043e\u0432.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043b\u044f \u0441\u043a\u0440\u043e\u043b\u043b\u044f\u0449\u0435\u0433\u043e\u0441\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageView-class.html\" rel=\"noopener noreferrer nofollow\">PageView<\/a> \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageController-class.html\" rel=\"noopener noreferrer nofollow\">PageController<\/a>. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageController\/viewportFraction.html\" rel=\"noopener noreferrer nofollow\">viewportFraction<\/a> \u0431\u0443\u0434\u0435\u0442 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a.<\/p>\n<pre><code class=\"dart\">\/\/ main.dart  class _HomeState extends State&lt;Home> {   static const imageHeight = 278.0;   final _controller = PageController(viewportFraction: 0.68);   ... }<\/code><\/pre>\n<p>\u0422\u0435\u043a\u0443\u0449\u0430\u044f \u043f\u043e\u0437\u0438\u0446\u0438\u044f \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0432 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0435 \u043a\u043b\u0430\u0441\u0441\u0430 <a href=\"https:\/\/api.flutter.dev\/flutter\/foundation\/ValueNotifier-class.html\" rel=\"noopener noreferrer nofollow\">ValueNotifier<\/a> (\u043e\u043d \u043d\u0430\u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043a\u043b\u0430\u0441\u0441\u0443 <a href=\"https:\/\/api.flutter.dev\/flutter\/foundation\/ChangeNotifier-class.html\" rel=\"noopener noreferrer nofollow\">ChangeNotifier<\/a>, \u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442 \u0437\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f).<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart   class _CoverCardState extends State&lt;CoverCard> {   ...     final ValueNotifier&lt;double> _scrollPosition = ValueNotifier&lt;double>(0.0);     void _onScrollPositionChanged() {     setState(() => _scrollPosition.value = widget.pageController.page ?? 0.0);   }     ... }<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f, \u043e\u043d \u043e\u043f\u043e\u0432\u0435\u0441\u0442\u0438\u0442 \u0441\u0432\u043e\u0438\u0445 \u043b\u0438\u0441\u0442\u0435\u043d\u0435\u0440\u043e\u0432.<\/p>\n<p>\u041f\u043e\u0434\u0446\u0435\u043f\u0438\u043c \u043a \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443 \u043b\u0438\u0441\u0442\u0435\u043d\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0432 <code>_scrollPosition<\/code> (\u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043e\u0442\u0446\u0435\u043f\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u044b\u043b\u043e \u0443\u0442\u0435\u0447\u0435\u043a \u043f\u0430\u043c\u044f\u0442\u0438).<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  @override void initState() {   super.initState();   widget.pageController.addListener(_onScrollPositionChanged); }  @override void dispose() {   super.dispose();   widget.pageController.removeListener(_onScrollPositionChanged); }<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043b\u0430 build-\u0444\u0443\u043d\u043a\u0446\u0438\u044f <code>AnimatedBuilder<\/code>, \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c <code>_scrollPosition<\/code> \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>animation<\/code>. <\/p>\n<p>\u041e\u0434\u0438\u043d <code>AnimatedBuilder<\/code> \u0440\u0438\u0441\u0443\u0435\u0442 \u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0443:<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  @override Widget build(BuildContext context) {   ...    return Stack(     ...     children: [       Positioned(         ...         ),       ),       AnimatedBuilder(         animation: _scrollPosition,         builder: (BuildContext context, Widget? child) {           return Positioned(             top: arrowPadding - 33,             left: widthCard + _getArrowOffset(),             child: Transform.rotate(               angle: pi \/ 2.0,               child: const ImageIcon(                 AssetImage(                   'assets\/icons\/arrow_insider.png',                 ),                 color: Color(0xfff9b9ad),                 size: 62,               ),             ),           );         },       ),       ...     ],   ); }<\/code><\/pre>\n<p>\u041f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0435\u043b\u043a\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u0440\u0430\u0441\u0447\u0435\u0442\u044b \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0442 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>_getArrowOffset<\/code>. \u0415\u0441\u043b\u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0430 \u043d\u0435 \u0432 \u0444\u043e\u043a\u0443\u0441\u0435 \u0438 \u043d\u0435 \u0441\u0431\u043e\u043a\u0443 \u043e\u0442 \u0444\u043e\u043a\u0443\u0441\u0430, \u0442\u043e \u0441\u043e \u0441\u0442\u0440\u0435\u043b\u043a\u043e\u0439 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442.<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  class _CoverCardState extends State&lt;CoverCard> {   static const double _defaultArrowOffset = .0;   static const double _focusArrowOffset = 40;   static const cardPadding = 111.0;     final ValueNotifier&lt;double> _scrollPosition = ValueNotifier&lt;double>(0.0);     void _onScrollPositionChanged() {     setState(() => _scrollPosition.value = widget.pageController.page ?? 0.0);   }     double _getArrowOffset() {     final scrollPosition = _scrollPosition.value;     final currentPosition = scrollPosition.floor();       final delta = scrollPosition - currentPosition;       final forwardAnimationOffest =         Curves.ease.transform(1 - delta) * _focusArrowOffset;     final backwardAnimationOffest =         Curves.ease.transform(delta) * _focusArrowOffset;       var animatedArrowOffset = _defaultArrowOffset;       if (widget.index == currentPosition) {       \/\/\/ Closest to focus       animatedArrowOffset = forwardAnimationOffest;     } else if (widget.index == currentPosition + 1) {       \/\/\/ Left or right sided from central card       animatedArrowOffset = backwardAnimationOffest;     }       return animatedArrowOffset;   } }<\/code><\/pre>\n<p>\u0414\u0440\u0443\u0433\u043e\u0439 <code>AnimatedBuilder<\/code> \u0440\u0438\u0441\u0443\u0435\u0442 \u0442\u0435\u043a\u0441\u0442. \u0415\u043c\u0443 \u0442\u0430\u043a \u0436\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 <code>animation<\/code>. <\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  @override Widget build(BuildContext context) {   final deviceWidth = MediaQuery.of(context).size.width;   final width = deviceWidth * 0.68;   final widthCard = deviceWidth * 0.48;   final heightCard = widthCard * 1.4;    final arrowPadding =       widget.imageHeight \/ 2 + (heightCard + cardPadding) \/ 2;    return Stack(     ...     children: [       Positioned(         ...       ),       ...       AnimatedBuilder(         animation: _scrollPosition,         builder: (BuildContext context, Widget? child) {           return Positioned(             width: width,             top: heightCard + 85,             child: Opacity(               opacity: getTextOpacity(),               child: Column(                 children: [                   Text(                     \"Jane Doe\",                     textAlign: TextAlign.center,                     style: _titleTextStyle,                     maxLines: 2,                     overflow: TextOverflow.ellipsis,                   ),                   const SizedBox(height: 12),                   Text(                     \"Lorem ipsum dolor sit amet\",                     textAlign: TextAlign.center,                     style: _subtitleTextStyle,                     maxLines: 4,                     overflow: TextOverflow.ellipsis,                   ),                 ],               ),             ),           );         },       ),     ],   ); }<\/code><\/pre>\n<p>\u0422\u0435\u043a\u0441\u0442 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0432 \u0432\u0438\u0434\u0436\u0435\u0442 <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Opacity-class.html\" rel=\"noopener noreferrer nofollow\">Opacity<\/a>: \u043e\u043d \u043c\u0435\u043d\u044f\u0435\u0442 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u0451\u0440\u043d\u0443\u0442\u044b\u0445 \u0432 \u043d\u0435\u0433\u043e \u0432\u0438\u0434\u0436\u0435\u0442\u043e\u0432 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438. \u042d\u0442\u043e \u0437\u0430\u0442\u0440\u043e\u043d\u0435\u0442 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0443 \u0432 \u0444\u043e\u043a\u0443\u0441\u0435 \u0438 \u0442\u0435, \u0447\u0442\u043e \u043e\u0442 \u043d\u0435\u0435 \u043f\u043e \u0431\u043e\u043a\u0430\u043c:<\/p>\n<pre><code class=\"dart\">\/\/ card\/cover_card.dart  double getTextOpacity() {   final scrollPosition = _scrollPosition.value;   final currentPosition = scrollPosition.floor();   final delta = scrollPosition - currentPosition;    if (widget.index == currentPosition) return 1 - delta;    if (currentPosition + 1 == widget.index) return delta;    return 0; }<\/code><\/pre>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0430, \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0435\u043b\u043e\u0447\u043a\u0438, \u0438 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0430.<\/p>\n<p>\u041a\u043e\u0434 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u2014 \u0432 <a href=\"https:\/\/github.com\/gooditcollective\/flutter-animations-example\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 GitHub<\/a>. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043e\u0441\u0442\u0430\u043b\u0438\u0441\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u2014 \u0441 \u0443\u0434\u043e\u0432\u043e\u043b\u044c\u0441\u0442\u0432\u0438\u0435\u043c \u043e\u0442\u0432\u0435\u0447\u0443.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/div>\n<p> <!----> <!----><br \/> \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\/681430\/\"> https:\/\/habr.com\/ru\/post\/681430\/<\/a><br \/><\/br><\/br><\/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-336722","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/336722","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=336722"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/336722\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=336722"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=336722"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=336722"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}