如何使用ChangeNotifier在MyList中为List <Item>创建add方法?

时间:2020-09-21 15:52:16

标签: flutter dart provider

我在数据模型上遇到麻烦,因为我使用另一个页面中的添加按钮,因此无法通过notifylistener()将项目添加到MyList模型内的列表中,以通知UI有关更改。使用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 个答案:

没有答案