How to create an add method for List<Item> inside MyList with ChangeNotifier?

Viewed 97

I have trouble with data model to add an item into a List inside MyList model with notifylistener() to notify the UI about changes because im using add button in another page. Is it okay to pass data to another screens when using ChangeNotifierProvider?

mylist.dart

import 'package:grocery_app/models/item.dart'; // import Item model
import 'dart:convert'; // import Json decoder and encoder

MyList myListFromJson(String str) => MyList.fromJson(json.decode(str));

String myListToJson(MyList data) => json.encode(data.toJson());

class MyList{
    MyList({
        this.listName,
        this.items,
    }); // Constructor

    String listName; // List name
    List<Item> items; // List of items in Array


    factory MyList.fromJson(Map<String, dynamic> json) => MyList(  // Constructing a new MyList instance from a Map structure
        listName: json["listName"],
        items: List<Item>.from(json["Items"].map((x) => Item.fromJson(x))),
    );
    Map<String, dynamic> toJson() => { // Convert into Map structure
        "listName": listName,
        "Items": List<dynamic>.from(items.map((x) => x.toJson())),
    };
}

item.dart

import 'dart:convert';

Item itemFromJson(String str) => Item.fromJson(json.decode(str));

String itemToJson(Item data) => json.encode(data.toJson());

class Item {
  Item({
    this.itemName,
    this.itemDesc,
    this.itemCategory,
    this.itemPrice,
    this.itemQty,
    this.checked,
    this.itemTotPrice,
  }); // Constructor

  String itemName; // Item name
  String itemDesc; // Item description
  String itemCategory; // Item category
  double itemPrice; // Item price
  int itemQty; // Item quantity
  bool checked; // Item checked true or false
  double itemTotPrice; // Item total price

  factory Item.fromJson(Map<String, dynamic> json) => Item(
        // Constructing a new Item instance from a Map structure
        itemName: json["itemName"],
        itemDesc: json["itemDesc"],
        itemCategory: json["itemCategory"],
        itemPrice: json["itemPrice"].toDouble(),
        itemQty: json["itemQty"],
        checked: json["checked"],
        itemTotPrice: json["itemTotPrice"].toDouble(),
      );

  Map<String, dynamic> toJson() => {
        // Convert into Map structure
        "itemName": itemName,
        "itemDesc": itemDesc,
        "itemCategory": itemCategory,
        "itemPrice": itemPrice,
        "itemQty": itemQty,
        "checked": checked,
        "itemTotPrice": itemTotPrice,
      };
}

MyListProvider.dart

import 'package:grocery_app/models/mylist.dart'; // import MyList model
import 'package:flutter/foundation.dart'; // import ChangeNotifier

class MyListProvider extends ChangeNotifier {
  // Notify the UI when any changes happen to the list
  List<MyList> _myList = [];
  void addList(MyList myList) {
    // Add new list method
    _myList.add(myList);
    notifyListeners(); // Call notify listener to notify UI about changes in the list
  }

  void removeList(int index) {
    // Remove list method
    _myList.removeAt(index);
    notifyListeners(); // Call notify listener to notify UI about changes in the list
  }


  List<MyList> get myList => _myList; // Getter method
}

Mainpage.dart

import 'package:flutter/material.dart';
import 'package:grocery_app/screens/AddNewList.dart';
// import 'dart:convert'
//     show json, base64, ascii; // Convert Json string into Json object
// import 'package:flutter_secure_storage/flutter_secure_storage.dart'; // Persistent storage for JWToken
import 'package:grocery_app/screens/Login.dart'; // import Login page
import 'package:grocery_app/screens/Signup.dart'; // import Signup page
import 'package:grocery_app/screens/Home.dart'; // import Home page
import 'package:grocery_app/screens/ListDetails.dart'; // import List details page
import 'package:grocery_app/models/mylist.dart'; // import MyList model
// import 'package:grocery_app/models/item.dart'; // import Item model
import 'package:grocery_app/providers/MyListProvider.dart'; // import MyListProvider
import 'package:flutter_svg/flutter_svg.dart'; // import svg module
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; // import Font Awesome package
import 'package:provider/provider.dart'; // import Provider package

class Mainpage extends StatefulWidget {
  // Mainpage(
  //     this.jwt, this.payload); // Receive jwt from FutureBuilder snapshot.data
  // factory Mainpage.fromBase64(String jwt) => Mainpage(
  //     jwt,
  //     json.decode(ascii.decode(base64.decode(base64.normalize(jwt.split(".")[
  //         1]))))); // Split 3 dots from response body into different string and normalize
  // // Normalize will do the following:
  // // Unescape any %-escapes.
  // // Only allow valid characters (A-Z, a-z, 0-9, / and +).
  // // Normalize a _ or - character to / or +.
  // // Validate that existing padding (trailing = characters) is correct.
  // // If no padding exists, add correct padding if necessary and possible.
  // // Validate that the length is correct (a multiple of four).
  // final String jwt; // Jwt access token
  // final Map<String, dynamic>
  //     payload; // JWT payload from response body into Map structure
  // final storage =
  //     FlutterSecureStorage(); // Flutter secure storage instance for persistent storage
  @override
  _MainpageState createState() => _MainpageState();
}

class _MainpageState extends State<Mainpage> {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => MyListProvider()),
      ],
      child: MaterialApp(
        routes: <String, WidgetBuilder>{
          // MaterialApp routes
          '/home': (BuildContext context) => Home(), // Home page route
          '/login': (BuildContext context) => Login(), // Login page route
          '/signup': (BuildContext context) => Signup(), // Sign up page route
        },
        home: SecondMainpage(),
      ),
    );
  }
}

class SecondMainpage extends StatefulWidget {
  // Create a stateful widget for Mainpage
  @override
  _SecondMainpageState createState() => _SecondMainpageState();
}

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

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

  var myListLength = 0; // default MyList length

  @override
  Widget build(BuildContext context) {
    final myListProvider = Provider.of<MyListProvider>(context);
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) =>
                      AddNewList())); // Go to add new list page
        },
        child: FaIcon(
          FontAwesomeIcons.plus,
          size: 30,
        ),
        backgroundColor: Colors.deepPurple[900],
      ),
      drawer: Drawer(
        child: ListView(padding: EdgeInsets.zero, children: [
          DrawerHeader(
            decoration: BoxDecoration(
                image: DecorationImage(
                    image: AssetImage("assets/drawer-header.jpg"),
                    fit: BoxFit.cover)),
            child: Align(
              alignment: Alignment.bottomLeft,
              child: Container(
                margin: EdgeInsets.only(left: 5, bottom: 5),
                child: Text(
                  'Crew Zul',
                  style: TextStyle(
                      color: Colors.white,
                      fontFamily: 'Open Sans',
                      fontWeight: FontWeight.w700,
                      fontSize: 18),
                ),
              ),
            ),
          ),
          InkWell(
            child: ListTile(
              leading: Icon(
                FontAwesomeIcons.donate,
                color: Colors.amber[600],
              ),
              title: Text(
                'Donate',
                style: TextStyle(
                    fontSize: 15,
                    fontFamily: 'Open Sans',
                    fontWeight: FontWeight.w600,
                    color: Colors.black),
              ),
              onTap: () {},
            ),
          ),
          InkWell(
            child: ListTile(
              leading: Icon(
                FontAwesomeIcons.powerOff,
                color: Colors.redAccent[700],
              ),
              title: Text(
                'Log out',
                style: TextStyle(
                    fontSize: 15,
                    fontFamily: 'Open Sans',
                    fontWeight: FontWeight.w600,
                    color: Colors.black),
              ),
              onTap: () {},
            ),
          ),
        ]),
      ),
      backgroundColor: Colors.purple,
      body: CustomScrollView(slivers: <Widget>[
        SliverAppBar(
          backgroundColor: Colors.deepPurple[900],
          title: Text(
            'My List',
            style: TextStyle(
                color: Colors.white,
                fontFamily: 'Open Sans',
                fontWeight: FontWeight.w600),
          ),
          expandedHeight: 200.0,
          flexibleSpace: Container(
            margin: EdgeInsets.only(bottom: 25),
            child: Align(
              alignment: Alignment.bottomCenter,
              child: SvgPicture.asset('assets/groceries.svg',
                  width: 110, height: 110),
            ),
          ),
        ),
        SliverToBoxAdapter(
          child: Container(
            color: Colors.deepPurple[900],
            height: 20,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: <Widget>[
                ClipRRect(
                  borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(70),
                      topRight: Radius.circular(70)),
                  child: Container(
                    height: 20,
                    width: double.infinity,
                    color: Colors.purple,
                  ),
                ),
              ],
            ),
          ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
            MyList mList = myListProvider.myList[index]; // Display myList
            return Dismissible(
              background: Container(
                  padding: EdgeInsets.only(left: 15),
                  color: Colors.amber[600],
                  child: Align(
                    alignment: Alignment.centerLeft,
                    child: FaIcon(
                      FontAwesomeIcons.trashAlt,
                      size: 27,
                      color: Colors.white,
                    ),
                  )),
              // Each Dismissible must contain a Key. Keys allow Flutter to
              // uniquely identify widgets.
              key: UniqueKey(), // Object key to specify which element
              onDismissed: (DismissDirection direction) {
                myListProvider
                    .removeList(index); // Remove object from specific index

                Scaffold.of(context).showSnackBar(SnackBar(
                    content: Text(
                        "List deleted"))); // Show snackbar when list deleted
                print(myListProvider.myList.length);
                print(mList.listName);
              },
              child: GestureDetector(
                onTap: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => ListDetails(
                                myList: mList,
                              ))); // Go to list details page with my list send as parameters to the next screen
                },
                child: Align(
                  alignment: Alignment.center,
                  child: Container(
                      width: MediaQuery.of(context).size.width * 0.85,
                      height: 135,
                      child: Card(
                        elevation: 4,
                        shadowColor: Colors.black,
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(13),
                        ),
                        margin: EdgeInsets.only(bottom: 10, top: 20),
                        child: Stack(children: [
                          Align(
                            alignment: Alignment.centerLeft,
                            child: Container(
                              margin: EdgeInsets.only(left: 10),
                              child: SvgPicture.asset('assets/chart-list.svg',
                                  width: 65, height: 65),
                            ),
                          ),
                          Container(
                            margin: EdgeInsets.only(
                                left: MediaQuery.of(context).size.width * 0.25,
                                top: 10),
                            child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Container(
                                    width: 139,
                                    child: Text(
                                      '${mList.listName}',
                                      overflow: TextOverflow.ellipsis,
                                      maxLines: 2,
                                      style: TextStyle(
                                          color: Colors.black,
                                          fontSize: 17,
                                          fontFamily: 'Open Sans',
                                          fontWeight: FontWeight.w600),
                                    ),
                                  ),
                                  Container(
                                      margin: EdgeInsets.only(top: 5),
                                      alignment: Alignment.center,
                                      width: MediaQuery.of(context).size.width *
                                          0.15,
                                      decoration: BoxDecoration(
                                          color: Colors.red,
                                          borderRadius: BorderRadius.only(
                                              topLeft: Radius.circular(11),
                                              bottomRight:
                                                  Radius.circular(11))),
                                      child: Text(
                                        '0/99',
                                        style: TextStyle(
                                            color: Colors.white,
                                            fontFamily: 'Open Sans',
                                            fontWeight: FontWeight.w600),
                                      )),
                                ]),
                          ),
                          Align(
                            alignment: Alignment.centerRight,
                            child: IconButton(
                                icon: FaIcon(
                                  FontAwesomeIcons.ellipsisV,
                                  size: 20,
                                  color: Colors.black,
                                ),
                                onPressed: () {}),
                          ),
                        ]),
                      )),
                ),
              ),
            );
          },
              childCount: myListProvider.myList == null
                  ? myListLength
                  : myListProvider.myList.length),
        )
      ]),
    );
  }
}

ListDetails.dart

import 'package:flutter/material.dart';
import 'package:grocery_app/models/mylist.dart'; // import MyList model
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; // import Font Awesome package
import 'package:flutter_svg/flutter_svg.dart'; // import svg module
import 'package:grocery_app/models/item.dart'; // import Item model
import 'package:grocery_app/screens/AddItem.dart'; // import add new item page

class ListDetails extends StatefulWidget {
  final MyList myList;
  ListDetails({Key key, @required this.myList}) : super(key: key);
  @override
  _ListDetailsState createState() => _ListDetailsState(myList);
}

class _ListDetailsState extends State<ListDetails> {
  final MyList myList;
  _ListDetailsState(this.myList);

  var myListItemLength =
      0; // Default list item length when the list just created
  void initState() {

    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => AddItem(myList: myList))); // Go to add new item page
        },
        child: FaIcon(
          FontAwesomeIcons.plus,
          size: 30,
        ),
        backgroundColor: Colors.deepPurple[900],
      ),
      backgroundColor: Colors.white,
      body: CustomScrollView(
        slivers: [
          SliverAppBar(
            backgroundColor: Colors.deepPurple[900],
            leading: IconButton(
                icon: Icon(
                  Icons.keyboard_arrow_left,
                  size: 30,
                  color: Colors.white,
                ),
                onPressed: () {
                  Navigator.of(context).popUntil(
                      (route) => route.isFirst); // Return to My List page
                }),
            expandedHeight: 200.0,
            flexibleSpace: Stack(
              children: [
                Container(
                    alignment: Alignment.center,
                    margin: EdgeInsets.only(bottom: 10),
                    child: SvgPicture.asset('assets/chart-list.svg',
                        width: 110, height: 110)),
                Container(
                  alignment: Alignment.bottomCenter,
                  margin: EdgeInsets.only(bottom: 25),
                  child: Text(
                    myList.listName,
                    maxLines: 2,
                    textAlign: TextAlign.center,
                    overflow: TextOverflow.ellipsis,
                    style: TextStyle(
                        color: Colors.white,
                        fontSize: 24,
                        fontFamily: 'Open Sans',
                        fontWeight: FontWeight.w600),
                  ),
                )
              ],
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              color: Colors.deepPurple[900],
              height: 20,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  ClipRRect(
                    borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(70),
                        topRight: Radius.circular(70)),
                    child: Container(
                      height: 20,
                      width: double.infinity,
                      color: Colors.white,
                    ),
                  ),
                ],
              ),
            ),
          ),
          SliverList(
              delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
            Item itm = myList.items[index];
            return Dismissible(
                background: Container(
                    padding: EdgeInsets.only(left: 15),
                    color: Colors.green,
                    child: Align(
                      alignment: Alignment.centerLeft,
                      child: FaIcon(
                        FontAwesomeIcons.check,
                        size: 23,
                        color: Colors.white,
                      ),
                    )),
                secondaryBackground: Container(
                    padding: EdgeInsets.only(right: 15),
                    color: Colors.red,
                    child: Align(
                      alignment: Alignment.centerRight,
                      child: FaIcon(
                        FontAwesomeIcons.times,
                        size: 23,
                        color: Colors.white,
                      ),
                    )),
                // Each Dismissible must contain a Key. Keys allow Flutter to
                // uniquely identify widgets.
                key: UniqueKey(), // Object key to specify which element
                onDismissed: (DismissDirection direction) {
                  if (direction == DismissDirection.startToEnd) {
                    // if swipe item to right, then item checked is set to true
                    setState(() {
                      itm.checked = true;
                      myList.items.removeAt(index); // Remove item from list
                      myList.items.add(
                          itm); // Add item to bottom with checked status is true
                    });

                    Scaffold.of(context).showSnackBar(SnackBar(
                        content: Text("Item checked"))); // Show snackbar
                    print(myList.items.length);
                    print(index);
                    print(itm.itemName);
                  } else if (direction == DismissDirection.endToStart) {
                    // if swipe item to left, then item checked is set to false
                    setState(() {
                      itm.checked = false;
                      myList.items.removeAt(index); // Remove item from list
                      myList.items.add(itm);
                    });

                    Scaffold.of(context).showSnackBar(SnackBar(
                        content: Text("Item unchecked"))); // Show snackbar
                    print(myList.items.length);
                    print(index);
                    print(itm.itemName);
                  }
                },
                child: Align(
                  alignment: Alignment.center,
                  child: Container(
                    margin: EdgeInsets.only(bottom: 10),
                    width: MediaQuery.of(context).size.width * 0.85,
                    height: 99,
                    child: Card(
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(18),
                      ),
                      color: itm.checked ? Colors.green : Colors.purple[900],
                      child: Row(
                        children: [
                          Container(
                            margin: EdgeInsets.only(left: 15, right: 15),
                            decoration: BoxDecoration(
                                color: Colors.white,
                                borderRadius: BorderRadius.circular(10)),
                            width: 50,
                            height: 50,
                            child: Icon(
                              Icons.shopping_cart,
                              color: itm.checked
                                  ? Colors.green
                                  : Colors.purple[900],
                              size: 20,
                            ),
                          ),
                          Container(
                            margin: EdgeInsets.only(top: 15),
                            child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    itm.itemName,
                                    style: TextStyle(
                                        color: Colors.white,
                                        fontFamily: 'Open Sans',
                                        fontWeight: FontWeight.w600,
                                        fontSize: 14),
                                  ),
                                  Text(
                                    'Qty: ${itm.itemQty}',
                                    style: TextStyle(
                                        color: Colors.white,
                                        fontFamily: 'Open Sans',
                                        fontWeight: FontWeight.w600,
                                        fontSize: 14),
                                  ),
                                  Text(
                                    'Price: ${itm.itemPrice.toStringAsFixed(2)}',
                                    style: TextStyle(
                                        color: Colors.white,
                                        fontFamily: 'Open Sans',
                                        fontWeight: FontWeight.w600,
                                        fontSize: 14),
                                  )
                                ]),
                          ),
                        ],
                      ),
                    ),
                  ),
                ));
          },
                  childCount: myList.items == null
                      ? myListItemLength
                      : myList.items.length))
        ],
      ),
    );
  }
}

0 Answers
Related