Building an eCommerce app with Flutter


In this tutorial, we will build a simple eCommerce app with Flutter. It won’t be a fully functional eCommerce app but if you are new to Flutter this is probably the best approach you should start with.

Prerequisites

  1. Flutter installed on your computer. I’m using Flutter 1.17.3 • you can find the stable version here https://github.com/flutter/flutter.git
  2. Know how to create a Flutter project and create a project named shopping_app

In case you haven’t set up a supported IDE and installed flutter on your computer, click here to get it done, using the official documentation. 

Before getting started with this article, I will recommend taking a sneak peek on the note-taking app build using Flutter.

Let’s go!

Overview of the eCommerce app we’ll be building in this tutorial.

The app has two screens: The home page and the main page. On the home page, we just have a landing page. On the main page we display three slides such as Trending Products, Best Selling, and Top Categories.

Building the eCommerce app

On the root folder create a path named assets and put the images into the folder. At the end of the tutorial, I’ll share all the resources.

root/assets

Then, open pubspec.yaml uncomment and edit these lines:

  assets:
     - assets/

It should look like this

eCommerce app
pubspec.yaml

Doing so you’re able to read all the images in the assets folder. Save it and will automatically update or you can do manually by running following command:

$ flutter pub get

Now, Inside of the lib folder create these folders:

folders

And then create these dart files:

files

Let´s build our models

categorie_model.dart

class CategorieModel{
  String categorieName; // name of the categorie
  String imgAssetPath; // url of the image
  String color1; 
  String color2;
  CategorieModel({this.categorieName,this.imgAssetPath,this.color1,this.color2});
}

product_model.dart

class ProductModel{
  int priceInDollars; // price 
  String productName; // name of the product
  int rating; // rating
  String imgUrl; // product image url
  int noOfRating; // number of rating
  ProductModel({this.productName,this.imgUrl,this.priceInDollars,this.rating,this.noOfRating});
}

trending_productmodel.dart

class TrendingProductModel{
  String productName;
  String storename;
  String imgUrl;
  int noOfRating;
  int priceInDollars;
  int rating;
  TrendingProductModel({this.imgUrl,this.noOfRating,this
  .priceInDollars,this.productName,this.storename, this.rating});
}

Now you already set up all the models. We don’t have a database right now so will simulate one.
We’ll create three different lists.

  1. A list to get all the Trending Products.
  2. A list to get all the Products.
  3. A list to get all the Categories

Open data.dart and paste this code below. This file covers all the products, categories, and trending.

data.dart

import 'package:shopping_app/models/categorie_model.dart';
import 'package:shopping_app/models/product_model.dart';
import 'package:shopping_app/models/trending_productmodel.dart';

List<TrendingProductModel> getTrendingProducts() {
  List<TrendingProductModel> trendingProducts = new List<TrendingProductModel>();
  TrendingProductModel productModel = new TrendingProductModel();
  //1
  productModel.storename = "Store Name";
  productModel.productName = "Product";
  productModel.noOfRating = 1;
  productModel.rating = 4;
  productModel.priceInDollars = 75;
  trendingProducts.add(productModel);
  productModel = new TrendingProductModel();
  //2
  productModel.storename = "Store Name";
  productModel.productName = "Product";
  productModel.noOfRating = 3;
  productModel.rating = 4;
  productModel.priceInDollars = 30;
  trendingProducts.add(productModel);
  productModel = new TrendingProductModel();
  //3
  productModel.storename = "Store Name";
  productModel.productName = "Product";
  productModel.noOfRating = 3;
  productModel.rating = 4;
  productModel.priceInDollars = 30;
  trendingProducts.add(productModel);
  productModel = new TrendingProductModel();
  //4
  productModel.storename = "Store Name";
  productModel.productName = "Product";
  productModel.noOfRating = 301;
  productModel.rating = 4;
  productModel.priceInDollars = 30;
  trendingProducts.add(productModel);
  productModel = new TrendingProductModel();

  return trendingProducts;
}
List<ProductModel> getProducts(){
  List<ProductModel> products = new List();
  ProductModel productModel = new ProductModel();
  //1
  productModel.productName = "Special  gift card";
  productModel.noOfRating = 4;
  productModel.imgUrl = "";
  productModel.rating = 4;
  productModel.priceInDollars = 20;
  products.add(productModel);
  productModel = new ProductModel();
  //1
  productModel.productName = "Special  gift card";
  productModel.noOfRating = 4;
  productModel.imgUrl = "";
  productModel.rating = 4;
  productModel.priceInDollars = 20;
  products.add(productModel);
  productModel = new ProductModel();
  //1
  productModel.productName = "Special  gift card";
  productModel.noOfRating = 4;
  productModel.imgUrl = "";
  productModel.rating = 4;
  productModel.priceInDollars = 20;
  products.add(productModel);
  productModel = new ProductModel();
  //1
  productModel.productName = "Special  gift card";
  productModel.noOfRating = 4;
  productModel.imgUrl = "";
  productModel.rating = 4;
  productModel.priceInDollars = 20;
  products.add(productModel);
  productModel = new ProductModel();
  //1
  productModel.productName = "Special  gift card";
  productModel.noOfRating = 4;
  productModel.imgUrl = "";
  productModel.rating = 4;
  productModel.priceInDollars = 20;
  products.add(productModel);
  productModel = new ProductModel();
  //1
  productModel.productName = "Special  gift card";
  productModel.noOfRating = 4;
  productModel.imgUrl = "";
  productModel.rating = 4;
  productModel.priceInDollars = 57;
  products.add(productModel);
  productModel = new ProductModel();
  return products;
}
List<CategorieModel> getCategories(){
  List<CategorieModel> categories = new List();
  CategorieModel categorieModel = new CategorieModel();
  //1
  categorieModel.categorieName = "Regular Gift";
  categorieModel.color1 = "0xff8EA2FF";
  categorieModel.color2 = "0xff557AC7";
  categorieModel.imgAssetPath = "assets/categorie.png";
  categories.add(categorieModel);
  categorieModel = new CategorieModel();
  //2
  categorieModel.categorieName = "Box Gift";
  categorieModel.color1 = "0xff50F9B4";
  categorieModel.color2 = "0xff38CAE9";
  categorieModel.imgAssetPath = "assets/boxgift.png";
  categories.add(categorieModel);
  categorieModel = new CategorieModel();
  //3
  categorieModel.categorieName = "Chocolate";
  categorieModel.color1 = "0xffFFB397";
  categorieModel.color2 = "0xffF46AA0";
  categorieModel.imgAssetPath = "assets/choclate.png";
  categories.add(categorieModel);
  categorieModel = new CategorieModel();
  //4
  categorieModel.categorieName = "Gift with card";
  categorieModel.color1 = "0xff8EA2FF";
  categorieModel.color2 = "0xff557AC7";
  categorieModel.imgAssetPath = "assets/categorie.png";
  categories.add(categorieModel);
  categorieModel = new CategorieModel();
  //5
  categorieModel.categorieName = "Regular Gift";
  categorieModel.color1 = "0xff8EA2FF";
  categorieModel.color2 = "0xff557AC7";
  categorieModel.imgAssetPath = "assets/categorie.png";
  categories.add(categorieModel);
  categorieModel = new CategorieModel();
  return categories;
}

Let´s set up the colors. It’s good practice to have a file that covers all the colors of the App.

colors.dart

import 'package:flutter/material.dart';
Color textGrey = Color(0xff9B9B9B);

Let’s build our main page. On this page we’ll have a search bar and three slides with horizontal scroll.

Open main.dart and start with this:


import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:shopping_app/data/data.dart';
import 'package:shopping_app/models/categorie_model.dart';
import 'package:shopping_app/models/product_model.dart';
import 'package:shopping_app/models/trending_productmodel.dart';
import 'package:shopping_app/resources/colors.dart';
import 'package:shopping_app/views/home.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',
      debugShowCheckedModeBanner: false,
      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: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            SizedBox(height: 50,),
            // here we will add our containers
          ],
        ),
      )
    );
  }
}

Above you have MyApp class and MyHomePage class that extends the StatefulWidget.

Inside of _MyHomePageState we will initialize all the lists using the initState () method because the framework will call this method exactly once for each State object it creates.

  List<TrendingProductModel> trendingProducts = new List();
  List<ProductModel> products = new List();
  List<CategorieModel> categories = new List();
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    trendingProducts = getTrendingProducts();
    products = getProducts();
    categories = getCategories();
  }

Now, we need to create our cards to display the trending products, the best selling products and finally the top categories. The classes that we are going to create you need to put them inside of main.dart like this:

Trending card

class TrendingTile extends StatelessWidget {
      final String productName;
      final String storename;
      final String imgUrl;
      final int noOfRating;
      final int priceInDollars;
      final int rating;
      TrendingTile({this.productName,this.storename,this.imgUrl,this.rating,this.noOfRating,this.priceInDollars});
      @override
      Widget build(BuildContext context) {
        return Container(
          width: MediaQuery.of(context).size.width - 70,
          margin: EdgeInsets.only(right: 13),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(4),
            boxShadow: <BoxShadow>[
              BoxShadow(
                offset: Offset(1.0, 1.0),
                blurRadius: 15.0,
                color: Colors.black87.withOpacity(0.05),
              ),
            ],
          ),
          child: Row(
            children: <Widget>[
              Container(
                child: Stack(
                  children: <Widget>[
                    Image.asset("assets/image.png",height: 150,fit: BoxFit.cover,) ,
                    Container(
                      height: 25,
                      width: 50,
                      margin: EdgeInsets.only(left: 5,top: 5),
                      alignment: Alignment.center,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(6) ,
                        gradient: LinearGradient(
                          colors: [const Color(0xff8EA2FF).withOpacity(0.5), const Color(0xff557AC7).withOpacity(0.5)]
                        )
                      ),
                      child: Text("\$$priceInDollars",style: TextStyle(
                          color: Colors.white
                      ),
                      ),
                    ),
                  ],
                ),
              ),
              Container(
                padding: EdgeInsets.symmetric(horizontal: 14,vertical: 12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text(productName, style: TextStyle(
                      color: Colors.black87,
                      fontSize: 19
                    ),),
                    Text(storename, style: TextStyle(
                      color: textGrey
                    ),),
                   SizedBox(height: 8,),
                   Row(
                     children: <Widget>[
                       StarRating(rating: rating,),
                       SizedBox(width: 3,),
                       Text("($noOfRating)", style: TextStyle(
                         color: textGrey,
                         fontSize: 12
                       ),)
                     ],
                   ),
                    SizedBox(height: 13,),
                    Container(
                      height: 30,
                      padding: EdgeInsets.symmetric(horizontal: 12),alignment: Alignment.center,
                      decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(4) ,
                          gradient: LinearGradient(
                              colors: [const Color(0xff8EA2FF), const Color(0xff557AC7)]
                          )
                      ),
                      child: Text(
                        "Add to cart", style: TextStyle(
                        color: Colors.white
                      ),
                      ),
                    )
                  ],
                ),
              )
            ],
          ),
        );
      }
    }

We’ll need this additional class to complete our trending card.

StarRating class

class StarRating extends StatelessWidget {
  final int rating;
  StarRating({this.rating});
  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        Image.asset(rating >= 1? "assets/star.png" : "assets/stargrey.png",width: 13,height: 13,),
        SizedBox(width: 3,),
        Image.asset(rating >= 2? "assets/star.png" : "assets/stargrey.png",width: 13,height: 13,),
        SizedBox(width: 3,),
        Image.asset(rating >= 3? "assets/star.png" : "assets/stargrey.png",width: 13,height: 13,),
        SizedBox(width: 3,),
        Image.asset(rating >= 4? "assets/star.png" : "assets/stargrey.png",width: 13,height: 13,),
        SizedBox(width: 3,),
        Image.asset(rating >= 5? "assets/star.png" : "assets/stargrey.png",width: 13,height: 13,),
      ],
    );
  }
}

Best Selling card

class ProductTile extends StatelessWidget {
  final int priceInDollars;
  final String productName;
  final int rating;
  final String imgUrl;
  final int noOfRating;
  ProductTile({this.priceInDollars, this.imgUrl, this.rating,this.productName, this.noOfRating});
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(right: 16),
      child: Column(
        children: <Widget>[
          Container(
            child: Stack(
              children: <Widget>[
                Image.asset("assets/productImage.png",height: 150,fit: BoxFit.cover,) ,
                Container(
                  height: 25,
                  width: 45,
                  margin: EdgeInsets.only(left: 8,top: 8),
                  alignment: Alignment.center,
                  decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(6) ,
                      gradient: LinearGradient(
                          colors: [const Color(0xff8EA2FF).withOpacity(0.5), const Color(0xff557AC7).withOpacity(0.5)]
                      )
                  ),
                  child: Text("\$$priceInDollars",style: TextStyle(
                      color: Colors.white
                  ),
                  ),
                ),
              ],
            ),
          ),
          Text(productName),
          SizedBox(height: 8,),
          Row(
            children: <Widget>[
              StarRating(rating: rating,),
              SizedBox(width: 10,),
              Text("($noOfRating)", style: TextStyle(
                  color: textGrey,
                  fontSize: 12
              ),)
            ],
          ),
        ],
      ),
    );
  }
}

Categories card

class CategorieTile extends StatelessWidget {
  final String categorieName;
  final String imgAssetPath;
  final String color1;
  final String color2;
  CategorieTile({this.imgAssetPath,this.color2,this.color1,this.categorieName});
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            height: 65,
            width: 110,
            margin: EdgeInsets.only(right: 16),
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Color(int.parse(color1)),Color(int.parse(color2))]
              ),
              borderRadius: BorderRadius.circular(8)
            ),
            padding: const EdgeInsets.symmetric(vertical: 14,horizontal: 8),
            child: Container(
                child: Image.asset(imgAssetPath,)),
          ),
          SizedBox(height: 8,),
          Text(categorieName),
        ],
      ),
    );
  }
}

Let’s put it all together.  But first make sure your main.dart looks like this:

eCommerce app

Go to build method in main.dart

Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            SizedBox(height: 50,),
            // here we will add our containers
          ],
        ),
      )
    );
  }

Inside the children we’ll add our cards to be displayed.


Add the search bar

                 /// Search Bar
                  Container(
                    margin: EdgeInsets.symmetric(horizontal: 12),
                    padding: EdgeInsets.symmetric(horizontal: 16, vertical: 14),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(8),
                      boxShadow: <BoxShadow>[
                        BoxShadow(
                          offset: Offset(5.0, 5.0),
                          blurRadius: 5.0,
                          color: Colors.black87.withOpacity(0.05),
                        ),
                      ],
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: <Widget>[
                        Padding(
                          padding: const EdgeInsets.only(left: 9),
                          child: Text("Search", style: TextStyle(
                            color: Color(0xff9B9B9B),
                            fontSize: 17
                          ),),
                        ),
                        Spacer(),
                        Icon(Icons.search),
                      ],
                    ),
                  ),
 

Trending products slide

             SizedBox(height: 30,),
              /// Trending
              Container(
                padding: EdgeInsets.symmetric(horizontal: 22),
                child: Row(
                 crossAxisAlignment: CrossAxisAlignment.end,
                  children: <Widget>[
                    Text("Today Trending", style: TextStyle(
                      color: Colors.black87,
                      fontSize: 22
                    ),),
                    SizedBox(width: 12,),
                    Text("30 June")
                  ],
                ),
              ),
              SizedBox(height: 20,),
              Container(
                padding: EdgeInsets.only(left: 22),
                height: 150,
                child: ListView.builder(
                itemCount: trendingProducts.length,
                    shrinkWrap: true,
                    scrollDirection: Axis.horizontal,
                    itemBuilder: (context, index){
                  return TrendingTile(
                    priceInDollars: trendingProducts[index].priceInDollars,
                    productName: trendingProducts[index].productName,
                    storename: trendingProducts[index].storename,
                    imgUrl: trendingProducts[index].imgUrl,
                    noOfRating: trendingProducts[index].noOfRating,
                    rating: trendingProducts[index].rating,
                  );
                    }),
              ),

The Best Selling slide

             SizedBox(height: 40,),
              /// Best Selling
              Container(
                padding: EdgeInsets.symmetric(horizontal: 22),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: <Widget>[
                    Text("Best Selling", style: TextStyle(
                        color: Colors.black87,
                        fontSize: 22
                    ),),
                    SizedBox(width: 12,),
                    Text("This week")
                  ],
                ),
              ),
              SizedBox(height: 20,),
              Container(
                height: 240,
                padding: EdgeInsets.only(left: 22),
                child: ListView.builder(
                  itemCount: products.length,
                    shrinkWrap: true,
                    scrollDirection: Axis.horizontal,
                    itemBuilder: (context, index){
                    return ProductTile(
                      priceInDollars: products[index].priceInDollars,
                      productName: products[index].productName,
                      rating: products[index].rating,
                      imgUrl: products[index].imgUrl,
                      noOfRating: products[index].noOfRating,
                    );
                    }),
              ),

Top categories slide

             /// Top categorie
              Container(
                padding: EdgeInsets.symmetric(horizontal: 22),
                child: Text("Top categories", style: TextStyle(
                    color: Colors.black87,
                    fontSize: 22
                ),),
              ),
              SizedBox(height: 20,),
              Container(
                height: 240,
                padding: EdgeInsets.only(left: 22),
                child: ListView.builder(
                    itemCount: categories.length,
                    shrinkWrap: true,
                    scrollDirection: Axis.horizontal,
                    itemBuilder: (context, index){
                      return CategorieTile(
                        categorieName: categories[index].categorieName,
                        imgAssetPath: categories[index].imgAssetPath,
                        color1: categories[index].color1,
                        color2: categories[index].color2,
                      );
                    }),
              )

Run the app

$ flutter run
eCommerce app

To finish let’s build our home page. Open home.dart. It will be a StateLessWidget.

home.dart


import 'package:flutter/material.dart';
import 'package:shopping_app/main.dart';

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
              child: Container(
          padding: EdgeInsets.symmetric(horizontal: 24),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              SizedBox(height: 60,),
              SizedBox(height: 4,),
              Text("Shopping App\nBuy everthing", style: TextStyle(
                color: Colors.black87.withOpacity(0.7),
                fontWeight: FontWeight.w500,
                fontSize: 28),),
              SizedBox(height: 40,),
              Image.asset("assets/illstration.png"),
              SizedBox(height: 40,),
              Container(
                width: MediaQuery.of(context).size.width,
                child: Column(
                  children: <Widget>[
                    GestureDetector(
                      onTap: (){
                        Navigator.push(context, MaterialPageRoute(
                          builder: (context) => MyHomePage()
                        ));
                      },
                      child: Container(
                        width: MediaQuery.of(context).size.width - 70,
                        alignment: Alignment.center,
                        padding: EdgeInsets.symmetric(horizontal: 24,vertical: 17),
                        decoration: BoxDecoration(
                            gradient: LinearGradient(
                                colors: [const Color(0xff8EA2FF), const Color(0xff557AC7)]
                            ),
                            boxShadow: <BoxShadow>[
                              BoxShadow(
                                offset: Offset(5.0, 5.0),
                                blurRadius: 15.0,
                                color: Color(0xff8EA2FF).withOpacity(0.5),
                              ),
                            ],
                            borderRadius: BorderRadius.circular(6)
                        ),
                        child: Text("Explore Shop".toUpperCase(), style: TextStyle(
                            color: Colors.white
                        ),),
                      ),
                    ),
                    SizedBox(height: 30,),
                    Text("Learn More", style: TextStyle(
                      color: Color(0xff8EA2FF),
                      fontSize: 17
                    ),)
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

To link this go back in main.dart  on MyApp class find this line

home: MyHomePage(),

And change to

home: Home(),

Now you can run the app and enjoy the Newly built eCommerce app using Flutter! This is just a little bit of what you can do with Flutter. The next step is to connect the application to the database and manipulate the products in real-time.  Soon we will write a tutorial to show how this is done.

Here’s the next article I will suggest you read on Creating Neumorphic Design with Flutter.

The full source code of this article can be found here.


Share on social media

//