Flutter Redux: Shopping Cart App From Scratch

in #utopian-io7 years ago (edited)


Github Repository: 

What will I learn?


  • You learn how to make shopping cart app using flutter redux.
  • You learn redux 


Requirements


System Requirements:


OS Support for Flutter:

  • Windows 7 SP1 or later (64-bit)
  • mac OS (64-bit)
  • Linux (64-bit)

Required Knowledge

  • A little understanding of key/value stores
  • A fair understanding of Mobile development and Imperative or Object Oriented Programming
  • Some understanding of real time databases


Resources for Flutter and this Project:


Difficulty


  • Intermediate


What is Flutter?


Flutter is an SDK owned by Google to create applications for Android and iOS using a single codebase. Flutter uses the Dart programming language (also owned by Google). Flutter was in beta when I released this video, so things might still change a bit. But the SDK is already mature enough to write some cool production apps!


What is Redux?

Redux is an application design, made initially for JavaScript and now utilized as a part of uses worked with receptive systems, (for example, React Native or Flutter). Redux is rearranged rendition of Flux engineering, made by Facebook. Yet, what's about with Redux? Fundamentally, you have to know three things: 

  • there's a solitary wellspring of truth — your entire application state is kept just in one place (called store) 
  • the state is perused only — to change the application state you have to dispatch an activity, and after that new state is made 
  • changes are made with unadulterated functions — a unadulterated capacity (to improve, it's a capacity without symptoms) takes the past state and an activity, and returns new state


Sounds cool, however what are the upsides of that arrangement? 

  • we have control over the state — it implies that we precisely comprehend what caused state change, we don't have copied state and we can without much of a stretch take after information stream 
  • unadulterated reducer capacities are anything but difficult to test  we can pass state, activity and test if result is right 
  • application is unmistakably structured — we have diverse layers for activities, models, business rationale, etc. — so you precisely know where to put another new element 
  • it's extraordinary design for more convoluted apps — you don't have to pass state down the entire view tree from parent to kid.


Redux Widgets on a basic illustration 


The greater part of the above guidelines influences information to stream in Redux unidirectional. Be that as it may, what does it mean? Practically speaking it's altogether finished with activities, reducers, store and states. How about we envision application that shows catch counter: 


  • Your application has some state toward the starting (number of snaps, which is 0) 
  • In view of that state see is rendered. 
  • On the off chance that client taps on catch, there's activity send (e.g. IncrementCounter) 
  • Activity is gotten by reducer, which knows past state (counter 0), gets activity (Increment Counter) and can return new state (counter 1) 
  • Your application has new state (counter 1) 
  • In light of new state, see is rendered once more 


So as should be obvious, by and large it's about the state. You have single application express, the state is perused just for see, and to make new state you have to send activity. Sending activity fires reducer that makes and transmits new application state. Also, history rehashes itself.

Redux Data Flow



Case of Shopping List App with Redux 



Give me a chance to demonstrate how Redux functions practically speaking on more advances illustration. We'll make a basic Shopping Cart application. In this application there will be functionalities for: 

  • adding items
  • marking items as checked
  • and that’s basically all


Whole application code on GitHub:


Setup


To run with Redux on Flutter, you need to add dependencies to your pubspec.yaml file:

flutter_redux: "^0.5.0"


You can check the newest version on flutter_redux package page.


Model


Our application needs to manage adding and changing items, so we‘ll use simple CartItem model to store single item state. Our whole application state will be just list of CartItems. As you can see, CartItem is just a plain Dart object.

import 'package:meta/meta.dart';class CartItem {  String name; bool checked;  CartItem({@required this.name, @required this.checked});  @override  String toString() {    return "$name : $checked";  }                            }


Source Code


Actions


Firstly, need to declare actions. Action is basically any intent that can be invoked to change application state. In application we’ll have two actions, for adding and changing item:


import 'package:flutter_shoppingcart/model/cart_item.dart'; class AddItemAction {  final CartItem item;   AddItemAction(this.item);} class ToggleStateItemAction {  final CartItem item;   ToggleStateItemAction(this.item);}                  class DeleteItemAction {  final CartItem item;   DeleteItemAction(this.item);}

Source Code 


Reducing


At that point, we have to tell our application what ought to be finished with those activities. This is the reason reducers are for — they essentially take current application state and the activity, at that point they make and return new application state. We'll have two reducers strategies:


import 'package:flutter_shoppingcart/model/cart_item.dart';import 'package:flutter_shoppingcart/redux/action.dart';                               ListCartItem cartItemsReducer(ListCartItem items, dynamic action) {  if (action is AddItemAction) {    return addItem(items, action);  } else if (action is ToggleStateItemAction) {    return toggleState(items, action);  } else if (action is DeleteItemAction) {    return deleteItem(items, action);  }  return items;} ListCartItem addItem(ListCartItem.  items, AddItemAction action) {  return new List.from(items)..add(action.item)                    List CartItem deleteItem(List CartItem items, DeleteItemAction action) {  return new List.from(items)..remove(action.item);} ListCartItem toggleState(ListCartItem items, ToggleStateItemAction action) {  ListCartItem newItems = items      .map((item) = item.name == action.item.name ? action.item : item)      .toList();  return newItems;}


Source Code



Method appReducers() delegates the action to proper methods. Both methods addItem() and toggleItemState() return new lists — that’s our new application state. As you can see, you shouldn’t modify current list. Instead of it, we create new lists every time.


Store Provider


Presently, when we have activities and reducers, we have to give place to putting away application state. It's called store in Redux and it's single wellspring of truth for our application.





import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart';import 'package:flutter_shoppingcart/model/cart_item.dart';import 'package:flutter_shoppingcart/redux/reducers.dart';import 'package:flutter_shoppingcart/shooping_cart.dart';import 'package:redux_dev_tools/redux_dev_tools.dart';                                                           void main() {  final store = new DevToolsStore ListCartItem (cartItemsReducer,      initialState: new List());                                        runApp(new ShoppingApp(store));}   class ShoppingApp extends StatelessWidget {  final DevToolsStore ListCartItem store    ShoppingApp(this.store);             @override  Widget build(BuildContext context) {    return new StoreProvider(      store: store,      child: new MaterialApp(        debugShowCheckedModeBanner: false,        theme: new ThemeData.dark(),        home: new ShoppingCart(store: store),      ),    );  }}Flutter 


To create store, we need to pass reducers methods and initial application state. If we created the store, we must pass it to the StoreProvider to tell our application than it can be used by anyone who wants to request app state:


Source Code


In the above example ShoppingCartApp() is main application widget.


StoreConnector


Currently we have everything except… actual adding and changing items. How to do that? To make it possible, we need to use StoreConnector. It’s a way to get the store and make some action with it or read it’s state.


Firstly, we’d like to read current data and show this in a list:


import 'package:flutter/material.dart';import 'package:flutter_redux/flutter_redux.dart';import 'package:flutter_shoppingcart/model/cart_item.dart';import 'package:flutter_shoppingcart/shopping_item.dart';                                                      class ShoppingList extends StatelessWidget {  @override  Widget build(BuildContext context) {    return new StoreConnector ListCartItem, List CartItem (      converter: (store) = store.state,      builder: (context, list) = new ListView.builder(            itemCount: list.length,            itemBuilder: (context, i) = new ShoppingItem(item: list[i]),          ),    );  }}


Source Code


Code above wraps default ListView.builder with StoreConnector. StoreConnector can take current app state (which is ListCartItem) and map this with converter function to anything. For purposes of this case, it’ll be the same state (ListCartItem), because we need the whole list here.


Next, in builder function we get list — which is basically list of CartItems from store, which we can use for building ListView.

we have reading data here. Now how to set some data?


To do it, we’ll use also StoreConnector, but in a slightly different way.


lib/add_item_dialog.dart
import 'package:flutter/material.dart';import 'package:flutter_redux/flutter_redux.dart';import 'package:flutter_shoppingcart/model/cart_item.dart';import 'package:flutter_shoppingcart/redux/action.dart';   class AddItemDialog extends StatefulWidget {  @override  AddItemDialogState createState() {    return new AddItemDialogState();  }}class AddItemDialogState extends State AddItemDialog {  String itemName;  @override  Widget build(BuildContext context) {    return new StoreConnector List CartItem, OnItemAddedCallback (      converter: (store) = (itemName) = store          .dispatch(AddItemAction(CartItem(name: itemName, checked: false))),      builder: (context, callback) = new AlertDialog(            title: Text('Add Item'),            contentPadding: const EdgeInsets.all(16.0),            content: new Row(              children: Widget[                new Expanded(                    child: new TextField(                  autofocus: true,                  decoration: new InputDecoration(                      labelText: "Item Name", hintText: "Eg. Apple"),                  onChanged: (text) {                    setState(() {                      itemName = text;                    });                  },                ))              ],            ),            actions: <Widget [              new FlatButton(                  onPressed: () {                    Navigator.pop(context);                  },                  child: new Text("Cancel")),              new FlatButton(                  onPressed: () {                    callback(itemName);                    Navigator.pop(context);                  },                  child: new Text("Add"))            ],          ),    );  }} typedef OnItemAddedCallback = Function(String itemName);

Source Code


We used StoreConnector, as in the previous example, but this time, instead of mapping list of CartItems, into the same list, we’ll map this into OnItemAddedCallback. This way we can pass callback to the AddItemDialogWidget and call it when user adds some new item:


Source Code


Now, every time user press “ADD” button, the callback will dispatch AddItemAction() event.


Now we can do very similar thing for toggling item state:


import 'package:flutter/material.dart';import 'package:flutter_redux/flutter_redux.dart';import 'package:flutter_shoppingcart/model/cart_item.dart';import 'package:flutter_shoppingcart/redux/action.dart';                                           class ShoppingItem extends StatefulWidget {  final CartItem item;                               const ShoppingItem({Key key, this.item}) : super(key: key);                              @override  ShoppingItemState createState() {    return new ShoppingItemState();  }} class ShoppingItemState extends State ShoppingItem {  @override  Widget build(BuildContext context) {    return new StoreConnector List CartItem, OnItemDeleted (      converter: (store) = (itemName) {            store.dispatch(DeleteItemAction(widget.item));          },      builder: (context, callback) = new Dismissible(            onDismissed: (_) {              setState(() {                callback(widget.item.name);              });            },            child: new StoreConnector List CartItem, OnToggleStateAction(              converter: (store) =                  (item) =   store.dispatch(ToggleStateItemAction(item)),              builder: (context, callback) = new ListTile(                    title: new Text(widget.item.name),                    leading: new Checkbox(                        value: widget.item.checked,                        onChanged: (newValue) {                          setState(() {                            callback(CartItem(                                name: widget.item.name, checked: newValue));                          });                        }),                  ),            ),            key: new Key(widget.item.name),          ),    );  }} typedef OnToggleStateAction = Function(CartItem item);typedef OnItemDeleted = Function(String itemName);

source code


As in the previous example, we use StoreConnector for mapping ListCartItem into OnStateChanged callback. Now every time checkbox is changed (in onChanged method), the callback fires ToggleItemStateAction event.


Summary


In this tutorial, I have created flutter shopping cart application from scratch with functionality like add item, check item and delete them using REDUX architecture.


Proof of Work Done 


You can also enjoy this tutorial here


Sort:  

Thank you for your contribution.

  • I found a text similar to yours on a site, it is not allowing plagism.
  • Your tutorial is too basic.

Please see already approved tutorials, such as this one link.

Looking forward to your upcoming tutorials.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]