How to Build an Onboarding Screen using PageView In Flutter
Tags: flutter, dart, programming
Published: 2020-05-02 14:04:49



Prerequisites

Tools required to follow the article.

- Flutter SDK.
- Android Studio or VS code.
- Emulator or an Android device
- Background knowledge of Flutter and Dart

Although there is a packaged already built to do this, there is an advantage getting thing done yourself, one of which customizing the design pattern to suit you. I will take you through achieving this without an external help of a provider.


Setup

Create a new project with the command below


    flutter create your_app_name


Open the folder on your preferred text editor and open the `main.dart` which is in `lib` folder and replace the the code with:


    import 'package:flutter/material.dart';
    void main() => runApp(MyApp());
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      }
    }
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(),
        );
      }
    }
    
    

To achieve the goal at hand flutter already have a widget called `PageView` Widget, so my job is to show you how this can be used.


PageView

A `PageView` allows the user to swipe between different screens in your app. All you need to set it up are a `PageViewController` which is a required property of the PageView. Each or widget to be swiped is wrapped inside a list of Widget as children.
 
 Time to See how the magic works

 Initializing PageController 

 Inside the stateful widget create `controller` variable whose type is of `PageViewController` and another `int` variable to hold the current index of the page

      PageController _pageController;
      int currentIndex = 0;

Initialize the state, add the following code after declaring of the page controller’s variable


    @override
      void initState() {
        super.initState();
        _pageController = PageController();
      }

Also the controller needs to be disposed, to do this add the following code after the last previous one


    @override
      void dispose() {
        _pageController.dispose();
        super.dispose();
      }


Building Screens

Update the body of your scaffold with the following code



    PageView(
        controller: _pageController,
        children: <Widget>[
          Container(
            child: Text("First Screen")
          ),
          Container(
            child: Text("Second Screen")
          ),
          Container(
            child: Text("Third Screen")
          )
          
        ],
      ),


Adding Indicators

It is common in different application with onboarding screens to see indicators to which screen is currently viewed. The `PageView` has provision for function which listen to the change in screen and return the index of the current screen. You will create a custom stateless widget for the indicator, and place it in the view. Create the Indicator widget with the code below outside the `MyHomePage`


    class Indicator extends StatelessWidget {
      final int positionIndex, currentIndex;
      const Indicator({this.currentIndex, this.positionIndex});
      @override
      Widget build(BuildContext context) {
        return Container(
          height: 12,
          width: 12,
          decoration: BoxDecoration(
              border: Border.all(color: Colors.blue),
              color: positionIndex == currentIndex
                  ? Colors.blue
                  : Colors.transparent,
              borderRadius: BorderRadius.circular(100)),
        );
      }
    }

Now you have wrap the `PageView` widget in `Stack` widgets so you can position another thing on the screen and where ever you wish to place it. 

    body: Stack(
            children: <Widget>[
              PageView(
                controller: _pageController,
                children: <Widget>[
                  Container(child: Text("First Screen")),
                  Container(child: Text("Second Screen")),
                  Container(child: Text("Third Screen"))
                ],
              ),
            ],
          ),

For the Indicator, it is going to be placed at the bottom of the screen in a row. add the following code as the second widget in the stack


    Positioned(
      bottom: 60,
      left: 150,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          Indicator(
            positionIndex: 0,
            currentIndex: currentIndex,
          ),
          SizedBox(
            width: 10,
          ),
          Indicator(
            positionIndex: 1,
            currentIndex: currentIndex,
          ),
          SizedBox(
            width: 10,
          ),
          Indicator(
            positionIndex: 2,
            currentIndex: currentIndex,
          ),
        ],
      ),
    ), 

Now you will create a method to update the index of the page once it is changed


    onChangedFunction(int index) {
        setState(() {
          currentIndex = index;
        });
      }

As earlier mentioned the `PageView` has a property called `onPageChanged` which takes a method and return the index of the current page. go ahead and add the method above to the  `PageView` widget


    onPageChanged: onChangedFunction,


Next and Previous Button

Lastly, you can also add a next or previous button to your page. You can do this by acessing the `nextPage` and `previousPage` which are both methods built into the `PageController` and takes two parameter `duration` which takes the duration for the page to switch, and `curve`  type of transition animation you want to apply while changing. create two static for `duration` and the `curve` after `_pageController`


    static const _kDuration = const Duration(milliseconds: 300);
    static const _kCurve = Curves.ease;

Also Create two methods to handle both functions respectively


     nextFunction() {
        _pageController.nextPage(duration: _kDuration, curve: _kCurve);
      }
     previousFunction() {
        _pageController.previousPage(duration: _kDuration, curve: _kCurve);
      }

Update your view by adding this as the last widget in the stack

    Positioned(
      bottom: 30,
      left: 130,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20.0),
        child: Container(
          child: Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              InkWell(
                  onTap: () => previousFunction(),
                  child: Text("Previous")),
              SizedBox(
                width: 50,
              ),
              InkWell(onTap: () => nextFunction(), child: Text("Next"))
            ],
          ),
        ),
      ),
    )