Flutter Tutorial: Understanding App State Management with E-Commerce App
Category: Skill Learning Tutorials
Larnr Education | 2542views

In this tutorial, you can be understanding app state-managed in the flutter framework. When We wanted to send data from one screen to another screen that time we are used to state management process in our application. Here I have created an e-commerce application project prototype sample given in the video. 

For the state management, we need a package for installation in our flutter project, for that, go to pub.dev website then search about provider package. Also, you can get package information by link.

Package installation command is 

flutter pub add provider

After then we start to code for state management.

main.dart

import 'package:appstate/models/user_model.dart';
import 'package:appstate/pages/home.dart';
import 'package:appstate/pages/login.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => ProductNotier()),
        ChangeNotifierProvider(create: (_) => UserNotifier())
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    bool isLogin = context.watch<UserNotifier>().isLogin;
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.yellow,
      ),
      home: isLogin ? HomePage() : LoginPage(),
    );
  }
}

models/user_modal.dart

import 'package:flutter/material.dart';

class UserNotifier with ChangeNotifier {
  String username = 'admin';
  String password = 'password';
  bool _check = false;

  bool auth(String user, String pass) {
    if (username == user && password == pass) {
      _check = true;
    }
    notifyListeners();
    return _check;
  }

  void logout() {
    _check = false;
    notifyListeners();
  }

  bool get isLogin => _check;
}

models/product_model.dart

import 'package:appstate/classes/item.dart';
import 'package:flutter/foundation.dart';

class ProductNotier with ChangeNotifier {
  List<Item> _items = [];
  int _counter = 0;

  List<Item> get items => _items;

  int get counter => _counter;

  double get totalAmt {
    double total = 0;
    _items.forEach((item) {
      total += item.amount!;
    });
    return total;
  }

  add(Item item) {
    _items.add(item);
    _counter = _items.length;
    notifyListeners();
  }

  remove(int index) {
    _items.removeAt(index);
    _counter = _items.length;
    notifyListeners();
  }

  removeAll() {
    _items.clear();
    _counter = _items.length;
    notifyListeners();
  }
}

classes/item.dart

class Item {
  String? details;
  double? amount;
  Item(this.details, this.amount);
}

classes/user.dart

class User {
  String? username;
  String? password;

  User(this.username, this.password);
}

Now Create Multiple Items. So now create-service directory for dummy data.

services/data_link.dart

import 'package:appstate/classes/item.dart';

List<Item> products = [
  Item('Shirt', 50),
  Item('Shree', 400),
  Item('T-Shirt', 100),
  Item('Watch', 800),
  Item('Mobile', 1000),
  Item('Wallet', 200),
  Item('Bag', 500),
  Item('Shoe', 1500),
  Item('Pant', 500),
  Item('Designer Pant', 1500),
];

Now we need to be created Pages or Screens

pages/login.dart

import 'package:appstate/models/user_model.dart';
import 'package:appstate/pages/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  String? username;
  String? password;
  GlobalKey<FormState> _key = GlobalKey<FormState>();
  bool recheck = false;

  @override
  Widget build(BuildContext context) {
    UserNotifier un = Provider.of<UserNotifier>(context);
    return Scaffold(
      body: Container(
        padding: EdgeInsets.all(8.0),
        child: Form(
          key: _key,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Login Page',
                style: TextStyle(fontSize: 24),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextFormField(
                  decoration: InputDecoration(
                    labelText: 'Input Username',
                    prefixIcon: Icon(
                      Icons.face_unlock_sharp,
                      color: Colors.blueAccent,
                    ),
                    labelStyle: TextStyle(color: Colors.blueAccent),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(color: Colors.teal),
                    ),
                  ),
                  initialValue: null,
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please input username';
                    }
                    return null;
                  },
                  onSaved: (value) {
                    username = value;
                  },
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextFormField(
                  decoration: InputDecoration(
                    labelText: 'Input Password',
                    prefixIcon: Icon(
                      Icons.lock,
                      color: Colors.blueAccent,
                    ),
                    labelStyle: TextStyle(color: Colors.blueAccent),
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                    ),
                  ),
                  initialValue: null,
                  validator: (value) {
                    if (value!.isEmpty) {
                      return 'Please input password';
                    }
                    return null;
                  },
                  onSaved: (value) {
                    password = value;
                  },
                ),
              ),
              SizedBox(
                height: 8.0,
              ),
              RaisedButton(
                child: Text('Login'),
                color: Colors.yellow,
                onPressed: () {
                  if (!_key.currentState!.validate()) return;
                  _key.currentState!.save();

                  setState(() {
                    recheck = un.auth(username!, password!);
                  });

                  // Show Message
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: Text(
                        (!recheck)
                            ? 'Username and Password do not match with our records'
                            : 'Welcome to my app',
                      ),
                    ),
                  );

                  // User Auth Check
                  if (!recheck) return;

                  // Navigate to Home page
                  Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(
                      builder: (context) =>
                          un.isLogin ? HomePage() : LoginPage(),
                    ),
                  );
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}

pages/home.dart 

import 'package:appstate/models/product_model.dart';
import 'package:appstate/models/user_model.dart';
import 'package:appstate/pages/cart.dart';
import 'package:appstate/pages/login.dart';
import 'package:appstate/services/data_link.dart';
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    UserNotifier un = Provider.of<UserNotifier>(context);
    ProductNotier pn = Provider.of<ProductNotier>(context);
    return Scaffold(
      appBar: AppBar(
        leading: Icon(Icons.integration_instructions),
        title: Text('My App'),
        actions: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: IconButton(
              icon: Badge(
                badgeContent: Text(pn.counter.toString()),
                child: Icon(Icons.shopping_cart),
              ),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => CartPage('Cart Page'),
                  ),
                );
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: IconButton(
              icon: Icon(Icons.logout),
              onPressed: () {
                un.logout();
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => LoginPage(),
                  ),
                );
              },
            ),
          )
        ],
      ),
      body: Container(
        height: double.infinity,
        child: ListView(
          children: [
            for (var item in products)
              ListTile(
                leading: Icon(Icons.supervised_user_circle_outlined),
                title: Text(
                  item.details!,
                ),
                subtitle: Text(
                  'Price: ₹' + item.amount.toString(),
                ),
                trailing: ElevatedButton(
                  onPressed: () {
                    pn.add(item);
                  },
                  child: Text('Add'),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

pages/cart.dart

import 'package:appstate/models/product_model.dart';
import 'package:appstate/models/user_model.dart';
import 'package:appstate/pages/login.dart';
import "package:flutter/material.dart";
import 'package:provider/provider.dart';

class CartPage extends StatefulWidget {
  String title;

  CartPage(this.title);

  @override
  _CartPageState createState() => _CartPageState();
}

class _CartPageState extends State<CartPage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    UserNotifier un = Provider.of<UserNotifier>(context);
    ProductNotier pn = Provider.of<ProductNotier>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        actions: [
          Center(
            widthFactor: 1.2,
            child: IconButton(
              icon: Icon(Icons.remove_shopping_cart),
              onPressed: () => {
                pn.removeAll(),
              },
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: IconButton(
              icon: Icon(Icons.logout),
              onPressed: () {
                un.logout();
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => LoginPage(),
                  ),
                );
              },
            ),
          )
        ],
      ),
      body: Container(
        child: ListView(
          children: [
            Padding(
              padding: const EdgeInsets.only(top: 8, left: 8),
              child: Text(
                'Products Details',
                style: TextStyle(fontSize: 20),
              ),
            ),
            MyTable(
              children: [
                tebleHeader(title: 'Product Details'),
                for (var i = 0; i < pn.items.length; i++)
                  tableBody(
                    pn.items[i].details!,
                    pn.items[i].amount!,
                    index: i,
                  ),
                tableBody('Total', pn.totalAmt)
              ],
            ),
            tableFooter(context)
          ],
        ),
      ),
    );
  }

  ButtonBar tableFooter(BuildContext context) {
    return ButtonBar(
      children: [
        RaisedButton(
          color: Theme.of(context).primaryColor,
          onPressed: () {
            context.read<ProductNotier>().removeAll();
          },
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text('Clear Cart'),
          ),
        ),
        RaisedButton(
          color: Theme.of(context).primaryColor,
          onPressed: () {},
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text('Check Out'),
          ),
        ),
      ],
    );
  }

  TableRow tableBody(String details, double amount, {int? index}) {
    return TableRow(children: [
      TableCell(
          child: Padding(
        padding: const EdgeInsets.all(5.0),
        child: Text(details),
      )),
      // TableCell(child: Center(child: Text(unit.toString()))),
      TableCell(child: Center(child: Text('₹$amount'))),
      TableCell(
          child: Center(
        child: (index != null)
            ? RaisedButton(
                color: Theme.of(context).primaryColor,
                child: Text('X'),
                onPressed: () {
                  context.read<ProductNotier>().remove(index);
                },
              )
            : null,
      )),
    ]);
  }

  TableRow tebleHeader({String? title, String? units, String? price}) {
    return TableRow(children: [
      TableCell(
        verticalAlignment: TableCellVerticalAlignment.middle,
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Text(title ?? 'Product Title'),
        ),
      ),
      TableCell(
        child: Center(child: Text(price ?? 'Price')),
      ),
      TableCell(
        child: Center(child: Text(units ?? 'Delete?')),
      ),
    ]);
  }
}

class MyTable extends StatelessWidget {
  final List<TableRow>? children;
  const MyTable({
    Key? key,
    @required this.children,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Table(
        defaultVerticalAlignment: TableCellVerticalAlignment.middle,
        defaultColumnWidth: FlexColumnWidth(1.0),
        border: TableBorder.all(
          color: Colors.black,
          style: BorderStyle.solid,
          width: 1,
        ),
        columnWidths: {
          0: FlexColumnWidth(3),
          1: FlexColumnWidth(1),
        },
        children: children!,
      ),
    );
  }
}

This is all codes. I hope you will understand the whole concept of app state management. If you wanted to learn more step by step then you purchase a complete course on flutter mobile application development. You may understand every flutter application concept with full source code. 

Thank you for learning. You can express yourself with like shares and comments.