In the second part of this series, we discussed building a single-page scrollable website (SPSW) using a
ListView whose sections are built lazily (on-demand) and have equal height. In this article, the sample app will be very similar to the previous app with one difference: In the
ColorSections widget, instead of
ListView , we will use
If you haven’t read the previous articles, I would strongly suggest starting from the first part of this series since the implementation details of the common widgets will not be mentioned in this part.
- Part 1: Introduction
- Part 2: Scroll to Position
- Part 3: Scroll to Page
- Part 4: Ensure Visible
- Part 5: Scroll to Index
- Part 6: Navigation
- Part 7: Query Params
PageView widget is a scrollable list whose children have the same size which is equal to the viewport size by default. Each item in the list is called a page.
PageController extends the
ScrollController . It allows setting which page is visible in a
previousPage methods. We can set the page size relative to the viewport size using its
We can consider the
PageView widget as a
ListView but more tailored for items of equal size. We will build a
PageView in a vertical scroll direction lazily using its
builder constructor. By default,
PageView widget locks the viewport to a certain page when a scrolling gesture ends. We will disable this feature by setting its
pageSnapping property to
Listen to Color Code Update
We start listening for the
colorCodeNotifier updates as soon as the
ColorSections widget is inserted into the tree. At this moment,
initState method is called, and the
PageController doesn’t have any clients because the
PageView is not yet laid out.
When we receive an update, if the
PageController is attached to the
PageView, and the source of the update is not scrolling, we provide the page index number to the
animateToPage method of the
PageController which scrolls to the destination index programmatically.
Notify Color Code
While the user is scrolling, we want to update the
colorCodeNotifier listeners as the first visible color code section (
Resizing Browser Window
In our use case, we set each page’s height equal to the viewport height. When resizing the browser window we want to make sure that we should be able to show the page content even in the smallest height that browser allows. We will use
viewportFraction property of the
PageController to set a minimum height for pages.
viewportFraction property is declared as
PageController so, we can’t dynamically set its value. We will wrap the
PageView with a
LayoutBuilder class to be able create new
PageController for the
PageView according to the new size constraints.
… the framework calls the builder function at layout time and provides the parent widget’s constraints. This is useful when the parent constrains the child’s size and doesn’t depend on the child’s intrinsic size. The LayoutBuilder’s final size will match its child’s size.
We will calculate the
viewportFraction for the available size according to this formulation:
- We first decide on the minimum height that is enough to show our content in a page. In this sample, we set it to 800 px.
- The builder function of LayoutBuilder will be called the first time the widget is laid out and when the parent widget passes different layout constraints. We need to construct a new
PageControllereach time this method is called.
- If the available height that the
PageViewcan be laid out is greater than the
_minPageHeight, then the
viewportFractionequals to 1 which makes the page height equal to the viewport height.
- If the available height is less than the
_minPageHeight, we calculate the
_minPageHeightby the available height. For example, if the viewport height equals to 400 px, we need to set the
viewportFractionto 2 so that the page height will be 800 px.
Here is the full code for the
Issues with Resizing
We actually don’t have any problem when the
viewportFraction is set to less than or equal to 1. However, as of Flutter stable version 2.2.0, I experience issues using
viewportFraction when it is greater than 1.
- Clipped First and Last Pages
PageView widget is obsessed with snapping the content to the center of the viewport. When the
viewportFraction that is greater than 1, it calculates
_initalPageOffset value to be added to the
minScrollContent and subtracted from the
maxScrollContent to snap the content to the center. This decision leads to clipping the first and last pages.
- Updating page index after the offset
To get the current page index from the
PageController , we access the
page property and floor it to an integer. For some reason, the page does not update until some offset value.
In the recording below, you can notice that the page index is correctly displayed when the
viewportFraction is 1. As the browser height gets smaller, we set the
viewportFraction to greater than 1. In the recording, the current page index is less than 1, although the
PageView is actually displaying the 1st index.
Note that these two issues might be expected behavior or bugs. Please write in comments if you have solutions, or I am missing something.
In this article, we explored lazily building the sections of a single page scrollable website using a
PageView widget. In the fourth part of this series, we will learn how to live with building all the sections at once in a
You can run this sample app by right clicking on the
main_003.02.dart file in the project source code. If you liked this article, please press the clap button, and star the Github repository of the sample apps.