{"id":1177,"date":"2021-06-18T18:28:05","date_gmt":"2021-06-18T18:28:05","guid":{"rendered":"https:\/\/flutter4fun.com\/?p=1177"},"modified":"2021-06-18T18:29:36","modified_gmt":"2021-06-18T18:29:36","slug":"ui-challenge-5","status":"publish","type":"post","link":"https:\/\/flutter4fun.com\/ui-challenge-5\/","title":{"rendered":"UI Challenge 5 \u2013 Parallax Effect by Marcus Engstrup Dresler"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"594\" src=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/UI-Challenge-5-1.jpg\" alt=\"\" class=\"wp-image-1182\" srcset=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/UI-Challenge-5-1.jpg 1024w, https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/UI-Challenge-5-1-300x174.jpg 300w, https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/UI-Challenge-5-1-768x446.jpg 768w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Hi there. I&#8217;m back again.<\/p>\n\n\n\n<p>Today I decided to write a blog post here. After a little search in the Dribbble I found this amazing design <a rel=\"noreferrer noopener\" href=\"https:\/\/dribbble.com\/shots\/14544247-Parallax-Effect\" target=\"_blank\">here<\/a> by <a rel=\"noreferrer noopener\" href=\"https:\/\/dribbble.com\/marcdres\" target=\"_blank\">Marcus Engstrup Dresler<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-rich is-provider-embed-handler wp-block-embed-embed-handler\"><div class=\"wp-block-embed__wrapper\">\n<div style=\"width: 640px;\" class=\"wp-video\"><!--[if lt IE 9]><script>document.createElement('video');<\/script><![endif]-->\n<video class=\"wp-video-shortcode\" id=\"video-1177-1\" width=\"640\" height=\"360\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/cdn.dribbble.com\/users\/941062\/screenshots\/14544247\/media\/3a1557d2fc6862cdc4c8b11644a172a8.mp4?_=1\" \/><a href=\"https:\/\/cdn.dribbble.com\/users\/941062\/screenshots\/14544247\/media\/3a1557d2fc6862cdc4c8b11644a172a8.mp4\">https:\/\/cdn.dribbble.com\/users\/941062\/screenshots\/14544247\/media\/3a1557d2fc6862cdc4c8b11644a172a8.mp4<\/a><\/video><\/div>\n<\/div><\/figure>\n\n\n\n<p>Okay, let&#8217;s break it into some small challenges to solve. First of all, we need to have a single image which supports parallax effect, then we need to put list of images inside a horizontal list and make them parallax based on their offset in the list.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. SlidingImage<\/h3>\n\n\n\n<p>For achieving parallax effect on an image, we need to show part of our image. We  can use Alignment in the <a rel=\"noreferrer noopener\" href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/Image-class.html\" target=\"_blank\">Image<\/a> widget to achieve that.<br>Alignment takes x and y which should be between -1.0 and +1.0. In our case, we need to use only X value (-1.0 means left, +1.0 means right, and 0.0 is center)<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class ParallaxImage extends StatelessWidget {\n  final String url;\n  final double horizontalSlide;\n\n  const ParallaxImage({\n    Key? key,\n    required this.url,\n    required this.horizontalSlide,\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Image.network(\n      imageUrl,\n      alignment: Alignment(horizontalSlide, 1),\n      fit: BoxFit.cover,\n    );\n  }\n}<\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"597\" height=\"841\" src=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/ezgif-2-22c2f0c1f8de.gif\" alt=\"\" class=\"wp-image-1190\"\/><figcaption>Try it in <a href=\"https:\/\/dartpad.dev\/daea278769122bb55c881d34a238f88d?null_safety=true\" target=\"_blank\" rel=\"noreferrer noopener\">DartPad<\/a><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\"> 2. Horizontal Scroll List<\/h3>\n\n\n\n<p>Now let&#8217;s focus on making a horizontal list with a simple widget (like a colored box). There is a widget called <a rel=\"noreferrer noopener\" href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageView-class.html\" target=\"_blank\">PageView<\/a> that can use it for implementing a horizontal list (with snapping effect).<\/p>\n\n\n\n<p>Below is a simple example of PageView<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@override\nWidget build(BuildContext context) {\n  return Scaffold(\n    body: Center(\n      child: AspectRatio(\n        aspectRatio: 2.0,\n        child: PageView(\n          controller: PageController(\n            initialPage: 1,\n            viewportFraction: 0.6,\n          ),\n          children: [\n            ColoredBox(color: Colors.red),\n            ColoredBox(color: Colors.green),\n            ColoredBox(color: Colors.blue),\n          ],\n        ),\n      ),\n    ),\n  );\n}<\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"802\" src=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/1.gif\" alt=\"\" class=\"wp-image-1199\"\/><figcaption>Try it in <a href=\"https:\/\/dartpad.dev\/ac18a4c8816be384a4adfe3f8c56c135?null_safety=true\" target=\"_blank\" rel=\"noreferrer noopener\">DartPad<\/a><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Now we need to calculate the offset of each box and let them now their offset value.<\/p>\n\n\n\n<p>We can get the <code>page<\/code> value using <a rel=\"noreferrer noopener\" href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageController-class.html\" target=\"_blank\">PageController<\/a> which contains a value that shows the offset of our <a rel=\"noreferrer noopener\" href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageView-class.html\" target=\"_blank\">PageView<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">late PageController _pageController;\ndouble page = 0.0;\n\n@override\nvoid initState() {\n  _pageController = PageController(\n    initialPage: 1,\n    viewportFraction: 0.6,\n  );\n  _pageController.addListener(() {\n    setState(() {\n      page = _pageController.page!;\n    });\n  });\n  super.initState();\n}\n\n@override\nWidget build(BuildContext context) {\n  return Scaffold(\n    body: Center(\n      child: AspectRatio(\n        aspectRatio: 2.0,\n        child: PageView(\n          controller: _pageController,\n          children: [\n            ColoredBox(color: Colors.red),\n            ColoredBox(color: Colors.green),\n            ColoredBox(color: Colors.blue),\n          ],\n        ),\n      ),\n    ),\n  );\n}<\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"798\" src=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/2.gif\" alt=\"\" class=\"wp-image-1204\"\/><figcaption>Try it in <a href=\"https:\/\/dartpad.dev\/95a5cb882846d9c29d1bdc9b15479a69?null_safety=true\" target=\"_blank\" rel=\"noreferrer noopener\">DartPad<\/a><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Now let&#8217;s use <a rel=\"noreferrer noopener\" href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PageView\/PageView.builder.html\" target=\"_blank\">PageView.builder<\/a> to access the <code>index<\/code> of each item (also it improves the performance)<br>Then we can calculate the offset of each item using <code>page - index<\/code> formula. Then we clamp the result between -1 and +1:<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">PageView.builder(\n  controller: _pageController,\n  itemBuilder: (context, index) {\n    return ColoredBox(\n      color: colors[index],\n      offset: (index - page).clamp(-1, 1).toDouble(),\n    );\n  },\n  itemCount: colors.length,\n),<\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/3-5.gif\" alt=\"\" class=\"wp-image-1265\" width=\"600\" height=\"821\"\/><figcaption>Try it in <a href=\"https:\/\/dartpad.dev\/36a636ea420b29e44d22f518d62d2fd0?null_safety=true\" target=\"_blank\" rel=\"noreferrer noopener\">DartPad<\/a><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Now we can replace our colored boxes with our <code>ParallaxImage<\/code> widget and use the <code>horizontalSlide<\/code> value to slide each image.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">PageView.builder(\n  controller: _pageController,\n  itemBuilder: (context, index) {\n    return Padding(\n      padding: const EdgeInsets.all(16.0),\n      child: ParallaxImage(\n        url: images[index],\n        horizontalSlide: (index - page).clamp(-1, 1).toDouble(),\n      ),\n    );\n  },\n  itemCount: images.length,\n)<\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"821\" src=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/4.gif\" alt=\"\" class=\"wp-image-1243\"\/><figcaption>Try it in <a href=\"https:\/\/dartpad.dev\/b97e6a46154869e402e0a9f8a1c8c859?null_safety=true\" target=\"_blank\" rel=\"noreferrer noopener\">DartPad<\/a><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>Okay, now let&#8217;s change the width and height of each image based on their offset. Also, we need to make images rounded by wrapping them in a <a rel=\"noreferrer noopener\" href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/ClipRRect-class.html\" target=\"_blank\">ClipRRect<\/a> widget.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:66.66%\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class ParallaxImage extends StatelessWidget {\n  final String url;\n  final double horizontalSlide;\n\n  const ParallaxImage({\n    Key? key,\n    required this.url,\n    required this.horizontalSlide,\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    final scale = 1 - horizontalSlide.abs();\n    final size = MediaQuery.of(context).size;\n    return Container(\n      child: Center(\n        child: SizedBox(\n          width: size.width * ((scale * 0.8) + 0.8),\n          height: size.height * ((scale * 0.2) + 0.2),\n          child: ClipRRect(\n            borderRadius: BorderRadius.all(Radius.circular(48)),\n            child: Image.network(\n              url,\n              alignment: Alignment(horizontalSlide, 1),\n              fit: BoxFit.cover,\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}<\/pre>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:33.33%\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"965\" src=\"https:\/\/flutter4fun.com\/wp-content\/uploads\/2021\/06\/5.gif\" alt=\"\" class=\"wp-image-1248\"\/><figcaption>Try it in <a href=\"https:\/\/dartpad.dev\/6b8d1d82e2a4cfc4b58e9b18920efc89?null_safety=true\" data-type=\"URL\" data-id=\"https:\/\/dartpad.dev\/6b8d1d82e2a4cfc4b58e9b18920efc89?null_safety=true\" target=\"_blank\" rel=\"noreferrer noopener\">DartPad<\/a><\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<p>I think there is nothing left except replacing images. Unfortunately, I couldn&#8217;t find images that have been used in our reference video. If you found them you can replace them easily.<\/p>\n\n\n\n<p>You can find the source code <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/imaNNeoFighT\/UI-Challenge-5\" target=\"_blank\">here<\/a> in my Github.<\/p>\n\n\n\n<p>Don&#8217;t forget to comment and write your feedback.<\/p>\n\n\n\n<p>Thanks for reading!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi there. I&#8217;m back again. Today I decided to write a blog post here. After a little search in the Dribbble I found this amazing design here by Marcus Engstrup Dresler. Okay, let&#8217;s break it into some small challenges to solve. First of all, we need to have a single image which supports parallax effect, [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":1179,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"wp_social_preview_title":"","wp_social_preview_description":"","wp_social_preview_image":0,"footnotes":""},"categories":[32],"tags":[],"class_list":["post-1177","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ui-challenge"],"_links":{"self":[{"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/posts\/1177","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/comments?post=1177"}],"version-history":[{"count":33,"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/posts\/1177\/revisions"}],"predecessor-version":[{"id":1805,"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/posts\/1177\/revisions\/1805"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/media\/1179"}],"wp:attachment":[{"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/media?parent=1177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/categories?post=1177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/flutter4fun.com\/wp-json\/wp\/v2\/tags?post=1177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}