How to create a walkthrough in Flutter app? using nb_utils - PradeeptheDeveloper

A walkthrough screen is typically a series of introductory screens that guide a user through the features and functionality of an app. The purpose of a walkthrough screen is to provide an overview of the app's key features and to help new users understand how to navigate the app. A walkthrough screen is often displayed the first time an app is opened, and it may include instructional text, images, and interactive elements that help users understand the app's core features and functions.


This is a Flutter code for a walk-through screen for a demo blog application. The walk-through screen is used to guide the user through the main features of the application.

The code defines a WalkThroughScreen class that extends StatefulWidget and its state WalkThroughScreenState. initState() method is used to initialize a list of WalkThroughModelClass objects. build method returns a Scaffold widget that contains a Stack of widgets. The first widget in the stack is a PageView that displays a set of pages, each page contains an image, title and a subtitle.

The second widget in the stack is a DotIndicator that displays the current page number. The DotIndicator widget is positioned at the bottom of the screen and takes a PageController as input to keep track of the current page.

The third widget in the stack is an AppButton widget positioned at the bottom of the screen. This button is used to skip the walk-through screen and launch the SplashScreen. If the current page is not the last one, the 'Skip' button is displayed, otherwise, the button is hidden.

intro_screen.dart

import 'package:flutter/material.dart';
import 'package:nb_utils/nb_utils.dart';
import '../widgets/commonWidgets.dart';

class WalkThroughScreen extends StatefulWidget {
  @override
  WalkThroughScreenState createState() => WalkThroughScreenState();
}

class WalkThroughScreenState extends State<WalkThroughScreen> {
  PageController pageController = PageController();
  int currentPage = 0;

  List<WalkThroughModelClass> list = [];

  @override
  void initState() {
    super.initState();
    init();
  }

  Future<void> init() async {
    list.add(
      WalkThroughModelClass(
          title: 'Get coupled!',
          subTitle: 'Call & Chat with matches in a secure way',
          image:
              'https://img.freepik.com/free-photo/lovely-couple-have-warm-cuddle_273609-44073.jpg?w=1800&t=st=1675306795~exp=1675307395~hmac=78dba3db7d499468b2eb8a7fa3395ce3ea5407f96dd785bad487802571689d5d'),
    );
    list.add(
      WalkThroughModelClass(
          title: 'Meet new people!',
          subTitle: 'You can control who can see your information',
          image:
              'https://img.freepik.com/free-photo/portrait-young-family-european-couple-wear-red-clothes-pose-making-common-photo-have-good-relationships_273609-25081.jpg?w=1800&t=st=1675306814~exp=1675307414~hmac=5b0d9ebef98fb7eb2baaa702f84a963b7182678122ae9da72193926865a5b010'),
    );
    list.add(
      WalkThroughModelClass(
          title: '100% Free - No Hidden charges',
          subTitle:
              'Send your interest & actively respond to the profile you like',
          image:
              'https://img.freepik.com/free-photo/portrait-smiling-beautiful-girl-her-handsome-boyfriend-laughing_158538-4987.jpg?w=1480&t=st=1675306849~exp=1675307449~hmac=eb8a604462451769f3ef32ea0e5cbcf5be28f2c0e74a0df3fd9bc00a0d77c32e'),
    );
  }

  @override
  void setState(fn) {
    if (mounted) super.setState(fn);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          PageView(
            controller: pageController,
            children: list.map((e) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  commonCachedNetworkImage(
                    e.image,
                    fit: BoxFit.cover,
                    height: 250,
                    width: 250,
                  ).cornerRadiusWithClipRRect(20),
                  22.height,
                  Text(e.title!,
                      style: boldTextStyle(size: 22),
                      textAlign: TextAlign.center),
                  8.height,
                  Text(
                    e.subTitle!,
                    style: TextStyle(fontSize: 16),
                    textAlign: TextAlign.center,
                  ),
                ],
              );
            }).toList(),
          ),
          Positioned(
            left: 0,
            right: 0,
            bottom: 100,
            child: DotIndicator(
              indicatorColor: Colors.orange,
              pageController: pageController,
              pages: list,
              unselectedIndicatorColor: grey,
              onPageChanged: (index) {
                setState(
                  () {
                    currentPage = index;
                  },
                );
              },
            ),
          ),
          Positioned(
            bottom: 20,
            right: 0,
            left: 0,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                AppButton(
                  padding: EdgeInsets.all(12),
                  text: 'Skip',
                  color: context.cardColor,
                  textStyle: primaryTextStyle(),
                  onTap: () {
                    finish(context);
                    SplashScreen().launch(context);
                  },
                ).visible(currentPage != 2),
                16.width,
                AppButton(
                  padding: EdgeInsets.all(12),
                  color: Colors.orange,
                  text: currentPage != 2 ? 'Next' : 'Create an account',
                  textStyle: primaryTextStyle(color: white),
                  onTap: () {
                    if (currentPage == 2) {
                      finish(context);
                      SplashScreen().launch(context);
                    } else {
                      pageController.animateToPage(
                        currentPage + 1,
                        duration: Duration(milliseconds: 300),
                        curve: Curves.linear,
                      );
                    }
                  },
                ).expand()
              ],
            ).paddingOnly(left: 16, right: 16),
          ),
        ],
      ),
    );
  }
}

commonWidgets.dart

This code is a Flutter widget that displays an image either from a URL or a local asset. If the URL is empty, it returns a placeholder widget with a specified height, width, fit, and alignment. The placeholder widget has a grey color and a circular border with a specified radius. If the URL starts with "http", it displays the image using CachedNetworkImage and if the image fails to load, it shows the placeholder widget. If the URL does not start with "http", it assumes the URL is a local asset and displays the image using Image.asset. The color, height, width, fit, alignment, and radius can be specified as optional parameters.

import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';

const double defaultRadius = 8.0;

Widget placeHolderWidget({
  double? height,
  double? width,
  BoxFit? fit,
  Alignment? alignment,
  double? radius,
}) {
  return Container(
    height: height,
    width: width,
    decoration: BoxDecoration(
      color: Colors.grey[200],
      shape: BoxShape.rectangle,
      borderRadius: BorderRadius.circular(radius ?? defaultRadius),
    ),
    alignment: alignment ?? Alignment.center,
  );
}

Widget commonCachedNetworkImage(
  String? url, {
  double? height,
  double? width,
  BoxFit? fit,
  Alignment? alignment,
  bool usePlaceholderIfUrlEmpty = true,
  double? radius,
  Color? color,
}) {
  if (url == null || url.isEmpty) {
    return placeHolderWidget(
        height: height,
        width: width,
        fit: fit,
        alignment: alignment,
        radius: radius);
  } else if (url.startsWith('http')) {
    return CachedNetworkImage(
      imageUrl: url,
      height: height,
      width: width,
      fit: fit,
      color: color,
      alignment: alignment ?? Alignment.center,
      errorWidget: (_, s, d) {
        return placeHolderWidget(
            height: height,
            width: width,
            fit: fit,
            alignment: alignment,
            radius: radius);
      },
      placeholder: (_, s) {
        if (!usePlaceholderIfUrlEmpty) return SizedBox();
        return placeHolderWidget(
            height: height,
            width: width,
            fit: fit,
            alignment: alignment,
            radius: radius);
      },
    );
  } else {
    return Container(
      height: height,
      width: width,
      decoration: BoxDecoration(
        shape: BoxShape.rectangle,
        borderRadius: BorderRadius.circular(radius ?? defaultRadius),
      ),
      alignment: alignment ?? Alignment.center,
      child: Image.asset(
        url,
        fit: fit,
      ),
    );
  }
}


The code uses the following dependencies:

  • package:flutter/material.dart: Flutter's material library for building beautiful, responsive, and flexible user interfaces.
  • package:nb_utils/nb_utils.dart: A collection of utility functions to simplify and streamline Flutter development.
  • package:demo_blog/widgets/commonWidgets.dart: Custom widgets that are used throughout the application.
  • package:cached_network_image/cached_network_image.dart: Cached image network functions to simplify and streamline Flutter development.

Comments

Popular posts from this blog

Error Handling in Flutter - Gradle issue

How to Make a Dynamic and Trending ListView with Flutter Widgets?

Understanding API integration with Getx State management