Flutter Learning Roadmap

10 min

10 min

Sahaj Rana

Published on Apr 2, 2024

Mastering Scrolling techniques in Flutter: How to implement basic and fancy Scrolling?

Introduction

Scrolling is a fundamental aspect of mobile app development, allowing users to navigate through content that exceeds the screen size. In Flutter, mastering scrolling techniques is essential for creating engaging and user-friendly applications. From basic scrolling functionalities to more advanced and fancy effects, understanding the various options available can significantly enhance the user experience.

Why Scrolling Matters

Scrolling enables users to access vast information within a confined space, making it a crucial component of app design. Without scrolling, users would be limited to the content visible on their device screens, hindering their ability to explore further. Developers can provide seamless navigation and enhance usability by implementing effective scrolling techniques.

Key Questions to Consider

  1. How can we implement basic scrolling in Flutter applications?

  2. What are the different types of scrolling techniques available in Flutter?

  3. How can we achieve fancy scrolling effects to create visually appealing apps?

  4. What role do specialized scrollable widgets play in Flutter development?

  5. How can we optimize scrolling performance for smooth user interaction?

As part of the Flutter Learning Roadmap series by Blup, we'll delve into these questions and provide practical insights for implementing basic and fancy scrolling effects in your applications. Let's embark on a journey to unlock the full potential of scrolling in Flutter development.

Basic Scrolling

Understanding basic scrolling behavior

Discover the ease of scrolling in Flutter with widgets like SingleChildScrollView, ListView, and GridView. Let Flutter handle the scrolling for you, effortlessly. Explore more scrolling widgets on the scrolling page of Flutter's Widget catalog.

Implementing infinite scrolling

Enable endless scrolling in Flutter with ListView.builder and GridView.builder. Load items as users scroll for smoother performance.

Learn how to implement this feature for improved user experience.


Exploring specialized scrollable widgets

Explore specialized scrolling widgets in Flutter, each offering unique scrolling behaviors. Learn about DraggableScrollableSheet with our instructional video.

Transform your scrollable area into a wheel using ListWheelScrollView!

Fancy Scrolling

Introduction to Fancy Scrolling Techniques

Are you looking to add dynamic scrolling effects like elastic scrolling or parallax scrolling to your Flutter app? Perhaps you need a scrolling header with unique behavior, such as shrinking or disappearing. In this guide, we'll explore how to implement these advanced scrolling techniques to enhance user experience and create visually captivating applications.

Level up your scrolling in Flutter with Sliver* classes. A sliver acts as a component of the scrollable area, offering detailed control. By integrating slivers into a CustomScrollView, you gain precise management over scrolling behavior.

Explore 'Using Slivers to achieve fancy scrolling' and learn about the Sliver classes for further details.

Nested scrolling widgets

What's the best strategy for nesting a scrolling widget within another scrolling widget to ensure optimal performance? Should you opt for the ShrinkWrap property or leverage slivers?

Discover the answer in the "ShrinkWrap vs Slivers" video.

Using Slivers for Fancy Scrolling

In Flutter, slivers are a powerful tool for achieving fancy scrolling effects. By utilizing slivers, you can customize the behavior of specific portions of a scrollable area, allowing for elastic scrolling and other dynamic effects.

Here's how you can use slivers to achieve fancy scrolling:

  1. Create a CustomScrollView: Start by setting up a CustomScrollView widget.

  2. Add a Floating App Bar with SliverAppBar: Incorporate a floating app bar using SliverAppBar.


  3. Incorporate a List with SliverList: Use SliverList to add a scrollable list of items.


Explore these concepts in action with an interactive example.

Resources

API docs

Refer to API docs for detailed information on available sliver APIs.

Floating App Bar

Improve User Experience with Floating App Bar

Easily hide the app bar as users scroll down your list of items. By moving the app bar into a CustomScrollView, you create a floating app bar that disappears as users scroll.

Follow these steps:

  1. Create a CustomScrollView.

  2. Add a floating app bar using SliverAppBar.

  3. Populate the list with SliverList.

Scaffold(
  body: CustomScrollView(
    slivers: [
      const SliverAppBar(
        title: Text(title),
        floating: true,
        flexibleSpace: Placeholder(),
        expandedHeight: 200,
      ),
      SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) => ListTile(title: Text('Item #$index')),
          childCount: 1000,
        ),
      ),
    ],
  ),
)

Enhance your app's usability with this simple recipe!

  1. Create a CustomScrollView

To implement a floating app bar, nest it within a CustomScrollView along with your list of items. This setup ensures synchronized scrolling between the app bar and the list. The CustomScrollView acts like a ListView, allowing various scrollable elements called slivers.

These slivers, like  SliverListSliverGrid, and SliverAppBar, define the scrollable components.

For this example, construct a CustomScrollView containing a SliverAppBar and a SliverList, removing any existing app bars from the Scaffold widget.

Scaffold(
  // No appBar property provided, only the body.
  body: CustomScrollView(
  // Add the app bar and list of items as slivers in the next steps.
      slivers: <Widget>[]),
);
  1. Adding a Floating App Bar with SliverAppBar

Integrate an app bar into the CustomScrollView using SliverAppBar. Similar to AppBar, SliverAppBar offers diverse features such as floating behavior and shrinkability.

Create a floating effect by setting the floating property to true, allow users to reveal the app bar by scrolling up.

Customize the appearance with a flexibleSpace widget for expandedHeight.

CustomScrollView(
  slivers: [
  // Add the app bar to the CustomScrollView.
    const SliverAppBar(
  // Provide a standard title.
      title: Text(title),
   // Allows the user to reveal the app bar if they begin scrolling
  // Back up the list of items.
      floating: true,
   // Display a placeholder widget to visualize the shrinking size.
      flexibleSpace: Placeholder(),
  // Make the initial height of the SliverAppBar larger than normal.
      expandedHeight: 200,
    ),
  ],
)
  1. Adding a List of Items with SliverList

After setting up the app bar, include a list of items within the CustomScrollView. Choose between SliverList or SliverGrid based on your layout needs. Utilize the SliverChildDelegate to provide widgets for the list. Use SliverChildBuilderDelegate for lazy loading, similar to ListView.builder widget.

// Next, create a SliverList
SliverList(
  // Use a delegate to build items as they're scrolled on screen.
  delegate: SliverChildBuilderDelegate(
    // The builder function returns a ListTile with a title that
    // displays the index of the current item.
    (context, index) => ListTile(title: Text('Item #$index')),
    // Builds 1000 ListTiles
    childCount: 1000,
  ),
)

Scrolling Parallax Effect

Creating a Scrolling Parallax Effect

Scrolling parallax effects add depth and visual interest to a list of items by making images appear to move at a different speed than the rest of the content as the user scrolls. Let's break down how to implement this effect in Flutter:

  1. Create a list to hold the parallax items:

    Begin by creating a new stateless widget, let's call it ParallaxRecipe. Inside this widget, set up  SingleChildScrollView with a Column. This structure forms the foundation of your list.

    class ParallaxRecipe extends StatelessWidget {
      const ParallaxRecipe({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const SingleChildScrollView(
          child: Column(
            children: [],
          ),
        );
      }
    }

    Each item in the list will be represented by a card containing an image and some text. These items will be stacked vertically within the SingleChildScrollView.


  2. Display items with text and a static image:

    Define a new stateless widget called LocationListItem. This widget will represent each item in the list.

    @immutable
    class LocationListItem extends StatelessWidget {
      const LocationListItem({
        super.key,
        required this.imageUrl,
        required this.name,
        required this.country,
      });
    
      final String imageUrl;
      final String name;
      final String country;
    
      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
          child: AspectRatio(
            aspectRatio: 16 / 9,
            child: ClipRRect(
              borderRadius: BorderRadius.circular(16),
              child: Stack(
                children: [
                  _buildParallaxBackground(context),
                  _buildGradient(),
                  _buildTitleAndSubtitle(),
                ],
              ),
            ),
          ),
        );
      }
    
      Widget _buildParallaxBackground(BuildContext context) {
        return Positioned.fill(
          child: Image.network(
            imageUrl,
            fit: BoxFit.cover,
          ),
        );
      }
    
      Widget _buildGradient() {
        return Positioned.fill(
          child: DecoratedBox(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.transparent, Colors.black.withOpacity(0.7)],
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter,
                stops: const [0.6, 0.95],
              ),
            ),
          ),
        );
      }
    
      Widget _buildTitleAndSubtitle() {
        return Positioned(
          left: 20,
          bottom: 20,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                name,
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                ),
              ),
              Text(
                country,
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 14,
                ),
              ),
            ],
          ),
        );
      }
    }
    • Each LocationListItem will display a rounded-rectangle background image, representing a location, overlaid with the name and country of that location.

    • Initially, use a static Image widget for the background. Later, this will be replaced with a dynamic parallax version.

      class ParallaxRecipe extends StatelessWidget {
        const ParallaxRecipe({super.key});
      
        @override
        Widget build(BuildContext context) {
          return SingleChildScrollView(
            child: Column(
              children: [
                for (final location in locations)
                  LocationListItem(
                    imageUrl: location.imageUrl,
                    name: location.name,
                    country: location.place,
                  ),
              ],
            ),
          );
        }
      }


  3. Implement the parallax effect:

A parallax scrolling effect adds depth and visual interest to a list by moving background images at a different speed than the rest of the content as the user scrolls. Here's how to implement it in Flutter:

  1. Understanding Parallax Effect:

    • Parallax effect involves moving background images opposite to the scroll direction. As list items move up, background images move down, creating a visually appealing effect.

    • Achieving this effect requires knowing the position of list items within the Scrollable widget. However, this information isn't available until after the layout phase. Flutter's Flow widget allows us to control widget transformations before painting, solving this problem.

  2. Wrap Background Image with Flow:

    • Wrap the background Image widget with a Flow widget. This provides control over the transformation of the background image before painting.

      Widget _buildParallaxBackground(BuildContext context) {
        return Flow(
          children: [
            Image.network(
              imageUrl,
              fit: BoxFit.cover,
            ),
          ],
        );
      }


  3. Introduce ParallaxFlowDelegate:

    Create a new class called ParallaxFlowDelegate, which extends FlowDelegate. This delegate will handle the layout and painting of the background image.

    class ParallaxFlowDelegate extends FlowDelegate {
      ParallaxFlowDelegate();
    
      @override
      BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {
        // To be implemented.
      }
      @override
      void paintChildren(FlowPaintingContext context) {
        // To be implemented.
      }
      @override
      bool shouldRepaint(covariant FlowDelegate oldDelegate) {
        // To be implemented.
        return true;
      }
    }


  4. Control Child Sizing:

    In the ParallaxFlowDelegate, specify tight width constraints for the background image child. This ensures the image is sized exactly as wide as the Flow widget.

    @override
    BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {
      return BoxConstraints.tightFor(
        width: constraints.maxWidth,
      );
    }



To create a parallax scrolling effect, you need to calculate the vertical position of each background image based on its scroll position and paint it accordingly. Here's how to achieve this in Flutter:

  1. Get Necessary Information:

    You need three critical pieces of information: the bounds of the ancestor Scrollable, the bounds of the individual list item, and the size of the background image after scaling.


  2. Wrap Background Image with Flow:

    Wrap the background Image widget with a Flow widget to control its transformation before painting.

    Widget _buildParallaxBackground(BuildContext context) {
      return Flow(
        delegate: ParallaxFlowDelegate(),
        children: [
          Image.network(
            imageUrl,
            fit: BoxFit.cover,
          ),
        ],
      );
    }


  3. Introduce ParallaxFlowDelegate:

    Create a ParallaxFlowDelegate class that extends FlowDelegate to handle layout and painting of the background image.

    class ParallaxFlowDelegate extends FlowDelegate {
      ParallaxFlowDelegate({
        required this.scrollable,
        required this.listItemContext,
        required this.backgroundImageKey,
      });
    
      final ScrollableState scrollable;
      final BuildContext listItemContext;
      final GlobalKey backgroundImageKey;
    }


  4. Implement shouldRepaint Method:

    Implement the shouldRepaint method to repaint the delegate when necessary information changes.

    @override
    bool shouldRepaint(ParallaxFlowDelegate oldDelegate) {
      return scrollable != oldDelegate.scrollable ||
          listItemContext != oldDelegate.listItemContext ||
          backgroundImageKey != oldDelegate.backgroundImageKey;
    }


  5. Calculate Layout:

    Calculate the pixel position of the list item within its ancestor Scrollable and the percentage position within the scrollable area.

    @override
    void paintChildren(FlowPaintingContext context) {
      final scrollableBox = scrollable.context.findRenderObject() as RenderBox;
      final listItemBox = listItemContext.findRenderObject() as RenderBox;
      final listItemOffset = listItemBox.localToGlobal(
          listItemBox.size.centerLeft(Offset.zero),
          ancestor: scrollableBox);
    
      final viewportDimension = scrollable.position.viewportDimension;
      final scrollFraction =
          (listItemOffset.dy / viewportDimension).clamp(0.0, 1.0);
    }


  6. Calculate Alignment:

    Use the scroll percentage to calculate the vertical alignment of the background image.

    final verticalAlignment = Alignment(0.0, scrollFraction * 2 - 1);


  7. Calculate Child Rectangle:

    Use the vertical alignment, list item size, and background image size to produce a rectangle for positioning the background image.

    final backgroundSize =
          (backgroundImageKey.currentContext!.findRenderObject() as RenderBox)
              .size;
    final listItemSize = context.size;
    final childRect =
          verticalAlignment.inscribe(backgroundSize, Offset.zero & listItemSize);


  8. Paint Background Image:

    Paint the background image with the desired translation transformation to achieve the parallax effect.

    context.paintChild(
      0,
      transform:
          Transform.translate(offset: Offset(0.0, childRect.top)).transform,
    );


  9. Ensure Repainting:

    Pass the ScrollableState's ScrollPosition to the FlowDelegate superclass to ensure repainting when the scroll position changes.

    ParallaxFlowDelegate({
        required this.scrollable,
        required this.listItemContext,
        required this.backgroundImageKey,
      }) : super(repaint: scrollable.position);


    You've successfully implemented parallax scrolling background images for your list of cards.

Download Our Flutter-based App Builder "Blup"

Blup Tool is a powerful platform tailored for shipping industry app development, offering a user-friendly interface and robust capabilities.

Start Building with Blup - Blup's Role in App Development

User-Friendly Interface: Blup's intuitive interface makes it easy for users of all experience levels to create functional and visually appealing apps.

Built on Flutter: As a Flutter-based tool, Blup offers exceptional flexibility, allowing developers to create apps that work seamlessly across both iOS and Android platforms using a single codebase.

Speedy Prototyping: Blup supports rapid prototyping, allowing businesses to quickly develop, test, and refine their app ideas in real-world scenarios. This is particularly valuable in the fast-paced shipping industry, where market demands can shift rapidly.

Flutter Learning Resources

Explore Flutter development with Blup's comprehensive Flutter Learning Roadmap Series. Whether you're new or experienced, Blup provides valuable resources to enhance your skills.

Previous Blogs: Check out "Mastering Gestures in Flutter: Taps, Drags, multi-touch gestures, Gesture Detector and More" and other blogs in the series.

Here are some reference links for the keywords and widgets used:

Scrollable Widgets:

  • Scrollable.of(context) - Retrieves the closest Scrollable ancestor's state in the widget tree, providing access to its properties.

  • scrollable.position - Refers to the position of the scrollable widget, which contains information about the current scrolling state.

Flow Delegate and Scroll Effects:

  • Flow - A widget that arranges its children in a flow pattern, and is used here to create the parallax background effect.

  • FlowDelegate - A delegate for managing flow layouts; in this context, it manages the parallax scrolling effect.

  • ParallaxFlowDelegate - A custom flow delegate for implementing parallax effects while scrolling.

  • Parallax - Custom widget for creating parallax effects, often used with scrollable widgets to create dynamic visuals during scrolling.

Scrollable State and Context:

  • ScrollableState - Represents the state of a Scrollable widget, providing access to the scroll position and other properties.

  • listItemContext - The context of the list item, used for calculating positions and offsets about the scrollable widget.

For more information, refer to the "Retrieve the value of a text field" recipe.

Resources:

  • Blup — Flutter Learning Roadmap: Follow Blup's learning roadmap.

  • Flutter Community: Join the community for support.

  • Stack Overflow: Find and answer Flutter questions.

  • r/FlutterDev on Reddit: Participate in Flutter discussions.

  • Flutter Discord Channel: Join for real-time conversations.

  • GitHub: Explore the Flutter GitHub repository.

  • Medium: Read Flutter-related articles and tutorials.

Conclusion

In this comprehensive guide, you've learned how to implement a captivating scrolling parallax effect in your Flutter app. By combining Flutter's powerful widgets like SingleChildScrollView, Column, and Flow, you've achieved a dynamic UI with depth and visual interest. Whether you're building a travel app showcasing exotic locations or an e-commerce platform highlighting products, the parallax effect adds a touch of elegance and sophistication. Now, armed with this knowledge, unleash your creativity and take your Flutter apps to new heights!

Flutter Learning Resource

Explore our Flutter Learning Series for more in-depth tutorials, tips, and tricks to master Flutter app development. Dive into a world of endless possibilities and take your skills to the next level. Start your journey today and unlock the full potential of Flutter!

Top Blogs

Follow us on

Follow us on