Flutter Parallax Scroll Effect with PageView

Flutter Design Android IOS page parallax image parallax image parallax effect

Parallax effects are awesome. Having elements move at different speeds during scrolling can easily provide a unique feeling for the application and they can make the user think that your app is well-polished. In this post, I will try to achieve a parallax effect using PageView, Transforms, Alignments, and some basic math. 


Parallax effect —

parallax scrolling is a website technique where we make our background move at a little slower pace than the foreground we make. This is a 3D effect as the user scrolls down the site, putting in a sense of depth and forming a more mesmeric experience while browsing.

Our eyes recognize objects close to us comparatively larger than those far away from our eyes. We perceive distant objects as if they are moving very slowly. Parallax bounds the same idea as human eyes which is a so-called optical illusion.

flutter makes it very easy to implement the parallax effect in our apps by using a few widgets, such as Stack, Positioned, etc. We’re going to use the Stack and Positioned widget in the demo project below. With the help of flutter widgets here we quickly can implement parallax scrolling.

What is a Stack widget?

It is a widget in Flutter SDK that allows us to make layers of widgets by putting one over the other.

This class is significant if you want to overlap several children simply. It is like putting objects in a single bucket one after the other(First goes in, last comes out). Similarly, if we assemble the same example pattern in a flutter, we could imagine having a few texts and an image, overlaid with a gradient and an elevated button attached at the bottom.

It gets mixed very smoothly and makes the UI much more interactive than before.

Constructor of Stack Class:

Stack(
{Key key,
AlignmentGeometry alignment: AlignmentDirectional.topStart,
TextDirection textDirection,
StackFit fit: StackFit.loose,
Overflow overflow: Overflow.clip,
Clip clipBehavior: Clip.hardEdge,
List<Widget> children: const <Widget>[]}
)

Here’s how our main.dart file would look like-


import 'package:flutter/material.dart';
import 'package:google_signin/pages/login_page.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Google SignIn',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomeScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}
Here’s the code for our Home screen-

import 'package:flutter/material.dart';

import 'package:parallaxeffect/widgets/SlidingCardView.dart';

import 'package:parallaxeffect/widgets/header.dart';


class HomeScreen extends StatefulWidget {

  const HomeScreen({super.key});


  @override

  State<HomeScreen> createState() => _HomeScreenState();

}


class _HomeScreenState extends State<HomeScreen> {

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      backgroundColor: Colors.white,

      body: Stack(

        children: [

          SafeArea(

            child: Column(

              crossAxisAlignment: CrossAxisAlignment.start,

              children: [

                SizedBox(height: 8),

                Header(),

                SizedBox(height: 20),

                SlidingCardView(),

              ],

            ),

          )

        ],

      ),

    );

  }

}


Create a Widgets folder and create a header.dart & SlidingCardView.dart files and add code :

header.dart 

import 'package:flutter/material.dart';


class Header extends StatelessWidget {

  const Header({super.key});


  @override

  Widget build(BuildContext context) {

    return Padding(

      padding: EdgeInsets.symmetric(horizontal: 32),

      child: Text(

        "Parallax Effect",

        style: TextStyle(fontSize: 22, fontWeight: FontWeight.w600),

      ),

    );

  }

}


SlidingCardView.dart

import 'package:flutter/material.dart';

import 'package:parallaxeffect/models/carddata.dart';

import 'dart:math' as math;


import 'package:parallaxeffect/screens/detail.dart';


class SlidingCardView extends StatefulWidget {

  const SlidingCardView({super.key});


  @override

  State<SlidingCardView> createState() => _SlidingCardViewState();

}


class _SlidingCardViewState extends State<SlidingCardView> {

  late PageController pageController;


  late List<CardData> card = [

    CardData("Water pic", "assets/images/0.jpeg"),

    CardData("Color pic", "assets/images/1.jpeg"),

    CardData("Space pic", "assets/images/2.jpeg"),

    CardData("Wave pic", "assets/images/3.jpeg"),

    CardData("light pic", "assets/images/4.jpeg"),

    CardData("zoom pic", "assets/images/5.jpeg")

  ];


  @override

  void initState() {

    // TODO: implement initState

    super.initState();

    pageController = PageController(viewportFraction: 0.8);

  }


  @override

  void dispose() {

    // TODO: implement dispose

    pageController.dispose();

    super.dispose();

  }


  @override

  Widget build(BuildContext context) {

    return SizedBox(

      height: MediaQuery.of(context).size.height * 0.55,

      child: PageView.builder(

        clipBehavior: Clip.none,

        controller: pageController,

        itemCount: card.length, //dynamic list show

        itemBuilder: (context, index) {

          return AnimatedBuilder(

            animation: pageController,

            builder: (context, child) {

              double pageOffset = 0;

              if (pageController.position.haveDimensions) {

                pageOffset = pageController.page! - index;

              }

              double gauss =

                  math.exp(-(math.pow((pageOffset.abs() - 0.5), 2) / 0.08));

              return Transform.translate(

                offset: Offset(-32 * gauss * pageOffset.sign, 0),

                child: Container(

                  clipBehavior: Clip.none,

                  margin: EdgeInsets.only(left: 8, right: 8, bottom: 24),

                  decoration: BoxDecoration(

                      color: Colors.white,

                      borderRadius: BorderRadius.circular(32),

                      boxShadow: [

                        BoxShadow(

                            color: Colors.black.withOpacity(0.1),

                            offset: Offset(8, 20),

                            blurRadius: 24)

                      ]),

                  child: Column(

                    children: [

                      ClipRRect(

                        borderRadius:

                            BorderRadius.vertical(top: Radius.circular(32)),

                        child: Image.asset(

                          card[index].url,

                          height: MediaQuery.of(context).size.height * 0.4,

                          alignment: Alignment(-pageOffset.abs(), 0),

                          fit: BoxFit.none,

                        ),

                      ),

                      Expanded(child: child!)

                    ],

                  ),

                ),

              );

            },

            child: Column(

              children: [

                SizedBox(

                  height: 8,

                ),

                Expanded(

                  child: Padding(

                    padding: EdgeInsets.all(16),

                    child: Row(

                      crossAxisAlignment: CrossAxisAlignment.start,

                      mainAxisAlignment: MainAxisAlignment.spaceBetween,

                      children: [

                        Padding(

                          padding: const EdgeInsets.all(8.0),

                          child: Text(card[index].title,

                              style: TextStyle(fontSize: 20)),

                        ),

                        Spacer(),

                        Row(

                          children: [

                            ElevatedButton(

                              onPressed: () {

                                // Navigator.push(

                                //     context,

                                //     MaterialPageRoute(

                                //         builder: (context) => DetaiScreen()));

                              },

                              child: Text(

                                "Detail",

                                style: TextStyle(color: Colors.white),

                              ),

                              style: ElevatedButton.styleFrom(

                                  backgroundColor: Colors.deepPurple,

                                  textStyle: TextStyle(color: Colors.white),

                                  shape: RoundedRectangleBorder(

                                      borderRadius: BorderRadius.circular(32))),

                            ),

                            SizedBox(

                              width: 16,

                            )

                          ],

                        )

                      ],

                    ),

                  ),

                )

              ],

            ),

          );

        },

      ),

    );

  }

}

Create a Model folder in lib and create a file for demo data 

CardData.dart

class CardData {

  final String title;

  final String url;


  CardData(this.title, this.url);

}

Output:


Flutter Design Android IOS page parallax image parallax image parallax effect
Comments

AdBlock Detected!

Our website is made possible by displaying ads to our visitors. Please supporting us by whitelisting our website.