简体   繁体   English

如何在已安装在 Flutter 中的小部件中设置状态?

[英]How can I set the state in a widget that has been mounted in Flutter?

I' m new in Flutter.我是 Flutter 的新手。 I made an app that has a catalogue with categories, subcategories and items (as you can see in photo).我制作了一个应用程序,其中包含一个包含类别、子类别和项目的目录(如您在照片中所见)。 My widget tree is:我的小部件树是:

Catalogue(cart):{[Category-List, Subcategory-List, Item-List:{ItemRow(ListTile)}]}.

I'm facing the following problem: I have items in my cart and their quantity is shown on the catalogue (item rows).我面临以下问题:我的购物车中有商品,它们的数量显示在目录(商品行)上。 When I delete an item from the cart, or clear all items, I can't set the controller of the text field of the item Row to zero cause that widget (current item row) has been mounted.当我从购物车中删除一个项目或清除所有项目时,我无法将项目 Row 的文本字段的控制器设置为零,因为该小部件(当前项目行)已安装。 I use Scoped model to add, delete or update items in the cart.我使用 Scoped 模型来添加、删除或更新购物车中的项目。 So, my problem is just visual.所以,我的问题只是视觉问题。 When I click on another category and then go to the previous, the controller has been set to zero correctly (cause the item rows recreated again with initstate()).当我单击另一个类别然后转到上一个类别时,控制器已正确设置为零(因为使用 initstate() 再次重新创建了项目行)。

Is there any solution to my problem?我的问题有什么解决办法吗? Thanks!谢谢!

App Image:应用图片: 如图所示

Catalogue.dart Code: Catalogue.dart 代码:

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_slidable/flutter_slidable.dart';

import '../widgets/categories/categories_manager.dart';
import '../widgets/subcats/subcat_manager.dart';
import '../widgets/items/items_list.dart';
import '../scoped-models/main.dart';
import '../models/item.dart';

class CataloguePage extends StatefulWidget {
  final String _langSelected;
  CataloguePage(this._langSelected, this.model);
  final MainModel model;

  @override
  State<StatefulWidget> createState() {
    return _CataloguePageState();
  }
}

class _CataloguePageState extends State<CataloguePage> {
  Widget currentPage;
  SubcatManager subcatPage;
  bool _loadingProgress;
  List<Item> _listCart;
  final SlidableController slidableController = SlidableController();

  @override
  void initState() {
    _listCart = widget.model.itemsInCart;

    _loadingProgress = true;
    widget.model
        .fetchCategories(widget.model.serverAddress, widget.model.serverPort)
        .then((bool success) {
      if (success) {
        setState(() {
          widget.model
              .fetchSubcats(widget.model.serverAddress, widget.model.serverPort,
                  widget.model.categories[0].catid)
              .then((bool success2) {
            if (success2) {
              setState(() {
                widget.model
                    .fetchItems(
                        widget.model.serverAddress,
                        widget.model.serverPort,
                        widget.model.categories[0].catid,
                        widget.model.subcats[0].subcatid)
                    .then((bool success3) {
                  if (success3) {
                    _loadingProgress = false;
                  }
                });
              });
            }
          });
        });
      } else {
        showDialog(
            barrierDismissible: true,
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                title: Text('An error has occured.'),
                content: Text('Connection with Server failed!'),
                actions: <Widget>[
                  FlatButton(
                    onPressed: () {
                      Navigator.popUntil(
                          context, (_) => !Navigator.canPop(context));
                      Navigator.pushReplacementNamed(context, '/');
                    },
                    child: Text('OK'),
                  )
                ],
              );
            });
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).primaryColor,
        title: Text('Catalogue'),
        actions: <Widget>[
          Stack(
            children: <Widget>[
              IconButton(
                icon: Icon(
                  Icons.shopping_cart,
                  size: 30.0,
                ),
                onPressed: () {
                  showModalBottomSheet(
                    context: context,
                    builder: (BuildContext contex) {
                      return _buildCartList(_listCart);
                    },
                  );
                },
              ),
              widget.model.itemsInCart.length > 0
                  ? CircleAvatar(
                      radius: 10.0,
                      child: Text(widget.model.itemsInCart.length.toString()),
                    )
                  : Container()
            ],
          )
        ],
      ),
      body: _buildBody(),
    );
  }

  Widget _buildBody() {
    if (_loadingProgress) {
      return Container(
        color: Theme.of(context).backgroundColor,
        child: Center(
          child: Theme.of(context).platform == TargetPlatform.iOS
              ? CupertinoActivityIndicator(
                  radius: 20.0,
                )
              : CircularProgressIndicator(
                  strokeWidth: 3.0,
                ),
        ),
      );
    } else {
      return Container(
        padding: EdgeInsets.all(20),
        color: Theme.of(context).backgroundColor,
        child: Row(
          children: <Widget>[
            Flexible(
              flex: 3,
              child: Column(
                children: [
                  Flexible(
                    child: CategoriesManager(widget.model),
                  ),
                ],
              ),
            ),
            widget.model.subcats[0].subcatid == 0
                ? Container()
                : VerticalDivider(
                    color: widget.model.themeBrightness == 1
                        ? Colors.white
                        : Colors.black,
                  ),
            widget.model.subcats[0].subcatid == 0
                ? Container()
                : Flexible(
                    flex: 3,
                    child: Column(
                      children: [
                        Flexible(
                          child: SubcatManager(widget.model),
                        ),
                      ],
                    ),
                  ),
            VerticalDivider(
              color: widget.model.themeBrightness == 1
                  ? Colors.white
                  : Colors.black,
            ),
            Flexible(
              flex: 4,
              child: Column(
                children: [
                  Text('Items'),
                  SizedBox(
                    height: 20.0,
                  ),
                  Flexible(
                    child: ItemList(widget.model),
                  ),
                ],
              ),
            ),
          ],
        ),
      );
    }
  }

  Widget _buildCartList(List<Item> listCart) {
    Widget itemCartCards;
    if (listCart.length > 0) {
      itemCartCards = Padding(
        padding: EdgeInsets.all(20.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Text('Selected Items'),
                Text('Total Quantity: ' +
                    widget.model.cartTotalItems.toString()),
              ],
            ),
            SizedBox(
              height: 10.0,
            ),
            Expanded(
              child: ListView.separated(
                separatorBuilder: (contex, index) => Divider(
                      color: widget.model.themeBrightness == 1
                          ? Colors.white
                          : Colors.black,
                    ),
                itemBuilder: (BuildContext context, index) {
                  return Slidable(
                    key: Key(listCart[index].itemid),
                    controller: slidableController,
                    delegate: SlidableDrawerDelegate(),
                    actionExtentRatio: 0.25,
                    secondaryActions: <Widget>[
                      IconSlideAction(
                        icon: Icons.delete,
                        caption: 'Delete',
                        color: Colors.red,
                        onTap: () {
                          widget.model
                              .deleteItemFromCart(listCart[index].itemid);
                        },
                      )
                    ],
                    child: ListTile(
                      title: Text(
                        listCart[index].itemperi,
                      ),
                      trailing: Row(
                        mainAxisSize: MainAxisSize.min,
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: <Widget>[
                          widget.model.showListItemsPrices
                              ? Text(listCart[index].itemCount.toString() +
                                  ' x ' +
                                  listCart[index].itemprice.toString() +
                                  ' €')
                              : Text(listCart[index].itemCount.toString()),
                        ],
                      ),
                    ),
                  );
                },
                itemCount: listCart.length,
              ),
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10)),
                  child: Text('Clear Order'),
                  color: Colors.red,
                  onPressed: () {
                    return showDialog<void>(
                      context: context,
                      barrierDismissible: false,
                      builder: (BuildContext context) {
                        return AlertDialog(
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(10.0),
                          ),
                          title: Text('Warning!'),
                          content:
                              Text('Are you sure you want to empty your cart?'),
                          actions: <Widget>[
                            FlatButton(
                                child: Text('Yes'),
                                onPressed: () {
                                  Navigator.of(context).pop();
                                  Navigator.of(context).pop();
                                  widget.model.deleteAllCartItems();
                                }),
                            FlatButton(
                              child: Text('No'),
                              onPressed: () {
                                Navigator.of(context).pop();
                              },
                            ),
                          ],
                        );
                      },
                    );
                  },
                ),
                SizedBox(
                  width: 20.0,
                ),
                RaisedButton(
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10)),
                  child: Text('Confirm Order'),
                  color: Colors.green,
                  onPressed: () {
                    _buildJsonOrder();
                  },
                ),
              ],
            ),
          ],
        ),
      );
    } else {
      itemCartCards = Container(
        child: Center(
          child: Text('Your cart is empty.'),
        ),
      );
    }
    return itemCartCards;
  }

  void _buildJsonOrder() {
    final List<dynamic> _listItems = [];
    for (Item item in widget.model.itemsInCart) {
      final Map<String, dynamic> itemData = {
        'hallid': [widget.model.hallNumber],
        'tableid': [widget.model.tableNumber],
        'itemid': ['${item.itemid}'],
        'itemperi': ['${item.itemperi}'],
        'kind': [0],
        'catid': [item.itemCatId],
        'subcatid': [item.itemSubcatId],
        'quantity': [item.itemCount],
        'price': [item.itemprice]
      };
      _listItems.add(itemData);
    }

    final Map<String, dynamic> orderData = {
      'hallid': [widget.model.hallNumber],
      'tableid': [widget.model.tableNumber],
      'typeofpos': ['4'],
      'posid': [600],
      'userid': [widget.model.currentUserId],
      'items': _listItems
    };

    widget.model
        .sendOrder(
            widget.model.serverAddress, widget.model.serverPort, orderData)
        .then((bool success) {
      if (success) {
        showDialog(
            barrierDismissible: false,
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                title: Text('Success.'),
                content: Text('Your order has been placed successfully!'),
                actions: <Widget>[
                  FlatButton(
                    onPressed: () {
                      widget.model.deleteAllCartItems();
                      Navigator.pop(context);
                      Navigator.of(context).pop();
                    },
                    child: Text('OK'),
                  )
                ],
              );
            });
      } else {
        showDialog(
            barrierDismissible: false,
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                title: Text('An error has occured.'),
                content: Text('Something went wrong with your order.'),
                actions: <Widget>[
                  FlatButton(
                    onPressed: () {
                      Navigator.pop(context);
                    },
                    child: Text('OK'),
                  )
                ],
              );
            });
      }
    });
  }
}

ItemList.dart Code: ItemList.dart 代码:

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';

import '../../models/item.dart';
import '../../scoped-models/main.dart';
import './item_row.dart';

class ItemList extends StatefulWidget {
  final MainModel model;
  ItemList(this.model);
  @override
  State<StatefulWidget> createState() {
    return _ItemListState();
  }
}

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

  Widget _buildItemList(List<Item> items) {
    Widget itemCards;
    if (items.length > 0) {
      itemCards = ListView.separated(
        separatorBuilder: (contex, index) => Divider(
              color: widget.model.themeBrightness == 1
                  ? Colors.white
                  : Colors.black,
            ),
        itemBuilder: (BuildContext context, index) {
          return ItemRow(widget.model, items[index]);
        },
        itemCount: items.length,
      );
    } else {
      itemCards = Container();
    }
    return itemCards;
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Expanded(
          child: ScopedModelDescendant<MainModel>(
            builder: (BuildContext context, Widget child, MainModel model) {
              return _buildItemList(model.items);
            },
          ),
        ),
      ],
    );
  }
}

ItemRow.dart Code: ItemRow.dart 代码:

import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';

import '../../models/item.dart';
import '../../scoped-models/main.dart';

class ItemRow extends StatefulWidget {
  final MainModel model;
  final Item item;
  ItemRow(this.model, this.item);

  @override
  State<StatefulWidget> createState() {
    return _ItemRowState();
  }
}

class _ItemRowState extends State<ItemRow> {
  int _itemCount;
  TextEditingController _itemCountController;

  @override
  void initState() {
    setState(() {
      _itemCount = widget.item.itemCount;
      _itemCountController = TextEditingController(text: _itemCount.toString());
    });
    super.initState();
  }

  @override
  void didUpdateWidget(ItemRow oldWidget) {
    if (oldWidget.item.itemid != widget.item.itemid) {
      setState(() {
        _itemCount = widget.item.itemCount;
        _itemCountController =
            TextEditingController(text: _itemCount.toString());
      });
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return ScopedModelDescendant<MainModel>(
      builder: (BuildContext context, Widget child, MainModel model) {
        return _buildItem(widget.item);
      },
    );
  }

  Widget _buildItem(Item item) {
    return ListTile(
      leading: CircleAvatar(
          backgroundImage: item.itemimage == ''
              ? AssetImage('assets/noimage.png')
              : NetworkImage(item.itemimage)),
      title: Text(
        item.itemperi,
      ),
      trailing: Row(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          widget.model.showListItemsPrices
              ? Text(
                  item.itemprice.toString() + ' €',
                )
              : Container(),
          widget.model.showListItemsCart ? VerticalDivider() : Container(),
          widget.model.showListItemsCart
              ? _buildListItemCart(item)
              : Container()
        ],
      ),
      onTap: () {
        if (widget.model.clickItems) {
          Navigator.pushNamed(context, '/item/' + item.itemid);
        }
      },
    );
  }

  Widget _buildListItemCart(Item item) {
    return Container(
      child: Row(
        children: <Widget>[
          GestureDetector(
            onLongPress: () {
              if (_itemCount != 0) {
                setState(() {
                  _itemCount = 0;
                  _itemCountController =
                      TextEditingController(text: _itemCount.toString());
                  widget.model.deleteItemFromCart(item.itemid);
                });
              }
            },
            child: IconButton(
              icon: Icon(Icons.remove),
              onPressed: () {
                if (_itemCount != 0) {
                  setState(() {
                    _itemCount--;
                    _itemCountController =
                        TextEditingController(text: _itemCount.toString());
                    if (_itemCount == 0) {
                      widget.model.deleteItemFromCart(item.itemid);
                    } else {
                      widget.model.updateItemCart(item.itemid, _itemCount);
                    }
                  });
                }
              },
            ),
          ),
          Container(
            width: 30.0,
            child: TextField(
              decoration: InputDecoration(border: InputBorder.none),
              textAlign: TextAlign.center,
              controller: _itemCountController,
              keyboardType: TextInputType.numberWithOptions(),
              onTap: () {
                _itemCountController.selection = TextSelection(
                    baseOffset: 0,
                    extentOffset: _itemCountController.text.length);
              },
              onSubmitted: (value) {
                if (value == 0.toString()) {
                  widget.model.deleteItemFromCart(item.itemid);
                }
                if (value != 0.toString() && _itemCount == 0) {
                  widget.model.addItemToCart(
                      item.itemid,
                      item.itemperi,
                      item.itemprice,
                      int.parse(value),
                      int.parse(widget.model.selectedCatid),
                      int.parse(widget.model.selectedSubcatId));
                }
                if (value != 0.toString() && _itemCount != 0) {
                  widget.model.updateItemCart(item.itemid, int.parse(value));
                }
                _itemCount = int.parse(value);
                TextEditingController(text: _itemCount.toString());
              },
            ),
          ),
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () {
              setState(
                () {
                  _itemCount++;
                  _itemCountController =
                      TextEditingController(text: _itemCount.toString());
                  if (_itemCount == 1) {
                    widget.model.addItemToCart(
                        item.itemid,
                        item.itemperi,
                        item.itemprice,
                        _itemCount,
                        int.parse(widget.model.selectedCatid),
                        int.parse(widget.model.selectedSubcatId));
                  } else {
                    widget.model.updateItemCart(item.itemid, _itemCount);
                  }
                },
              );
            },
          ),
        ],
      ),
    );
  }
}

ScopedModelItems.dart Code: ScopedModelItems.dart 代码:

import 'package:scoped_model/scoped_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

import '../models/item.dart';
import '../scoped-models/main.dart';

mixin ItemsModel on Model {
  MainModel model;
  List<Item> _items = [];
  List<Item> itemsEmpty = [];
  int _cartTotalItems = 0;
  List<Item> _itemsCartEmpty = [];
  Item _itemDetails;

  Item get itemDetails {
    return _itemDetails;
  }

  List<Item> get items {
    return List.of(_items);
  }

  List<Item> get itemsInCart {
    return _itemsCartEmpty;
  }

  int get cartTotalItems {
    if (_cartTotalItems == null) {
      _cartTotalItems = 0;
    }
    return _cartTotalItems;
  }

  void initState() {
    _items.forEach(_addItem);
  }

  void _addItem(Item item) {
    itemsEmpty.add(item);
  }

  void addItemToCart(String itemid, String itemperi, num itemprice,
      int itemQuantity, int itemCatid, int itemSubcatid) {
    Item item = Item(
        itemid: itemid,
        itemperi: itemperi,
        itemprice: itemprice,
        itemCount: itemQuantity,
        itemCatId: itemCatid,
        itemSubcatId: itemSubcatid);
    _itemsCartEmpty.add(item);
    _cartTotalItems += itemQuantity;
    notifyListeners();
  }

  void updateItemCart(String itemid, int itemQuantity) {
    _itemsCartEmpty.forEach((item) {
      if (item.itemid == itemid) {
        _cartTotalItems -= item.itemCount;
        item.itemCount = itemQuantity;
        _cartTotalItems += itemQuantity;
      }
    });
    notifyListeners();
  }

  void deleteItemFromCart(String itemid) {
    _itemsCartEmpty.forEach((item) {
      if (item.itemid == itemid) {
        _cartTotalItems -= item.itemCount;
      }
    });
    _itemsCartEmpty.removeWhere((item) => item.itemid == itemid);
    notifyListeners();
  }

  void deleteAllCartItems() {
    _itemsCartEmpty.removeRange(0, _itemsCartEmpty.length);
    _cartTotalItems = 0;
    notifyListeners();
  }

  Future<bool> fetchItems(String serverAddress, String serverPort,
      dynamic catid, dynamic subcatid) {
    return http.get(
        'http://$serverAddress:$serverPort/cats/$catid/subcats/$subcatid/items/GR',
        headers: {'Accept': 'application/json'}).then((http.Response response) {
      if (response.statusCode == 200 || response.statusCode == 201) {
        final List<Item> fetchedItemList = [];
        final List<dynamic> itemListData = json.decode(response.body);
        itemListData.forEach((dynamic itemData) {
          String imageData = '', periData = '';
          if (itemData['item_image_path'] != '') {
            imageData = 'http://$serverAddress:$serverPort/photos/' +
                itemData['item_image_path'];
          }
          if (itemData['item_webperi'] == '') {
            periData = itemData['item_peri'];
          } else {
            periData = itemData['item_webperi'];
          }
          final Item item = Item(
              itemid: itemData['item_id'],
              itemperi: periData,
              itemimage: imageData,
              itemprice: itemData['item_price'],
              itemCount: 0);
          if (_itemsCartEmpty.isNotEmpty) {
            for (Item itemCart in _itemsCartEmpty) {
              if (itemCart.itemid == item.itemid) {
                item.itemCount = itemCart.itemCount;
              }
            }
          }
          fetchedItemList.add(item);
        });
        _items = fetchedItemList;
        notifyListeners();
        return true;
      } else {
        return false;
      }
    }).catchError((error) {
      print(error);
      notifyListeners();
      return false;
    });
  }
}

Since there's no code I'll just list the common errors that cause this when using scoped model:由于没有代码,我将仅列出使用作用域模型时导致此问题的常见错误:

1. Not calling notify 1.不调用notify

You have to call notifyListeners() in your scoped model to update the UI that's using your porperties.您必须在范围模型中调用 notifyListeners() 以更新使用您的属性的 UI。

2. Not telling model to rebuild 2. 不告诉模型重建

If you're not using a ScopedModelDescendent widget and instead you're getting your model using如果您没有使用 ScopedModelDescendent 小部件,而是使用

appModel = ScopedModel.of<AppModel>(context);

change your code to将您的代码更改为

appModel = ScopedModel.of<AppModel>(context, rebuildOnChange: true);

If you're doing both of these then please post your code for your UI and scoped model so that it's easier to offer our help.如果您同时执行这两项操作,请发布您的 UI 和范围模型的代码,以便更轻松地提供我们的帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 flutter如何查看Widget是否挂载 - How to check if Widget has mounted in flutter Flutter - 如何在复选框的动态列表中设置复选框小部件的状态 - Flutter - How can I set the state of a Checkbox widget in a dynamic list of Checkbox Flutter:如何设置父窗口小部件的 state - Flutter: How to set state of parent widget 如何测量 flutter 中已安装小部件的时间? - How to measure time of mounted widget in flutter? flutter 错误:此小部件已卸载,因此 State 不再具有上下文(应被视为已失效) - flutter error: This widget has been unmounted, so the State no longer has a context (and should be considered defunct) Flutter 错误:未处理的异常:此小部件已卸载,因此 State 不再具有上下文 - Flutter error: Unhandled Exception: This widget has been unmounted, so the State no longer has a context 在 Flutter 中设置 Parent Widget 的 State - Set State of Parent Widget in Flutter 如何在flutter中的listview.builder中的索引处设置小部件的状态 - How to set the state of a widget at an index in a listview.builder in flutter Draggable 和 DragTarget,如何在 DragTarget 接受 Draggable 后构建小部件? - Draggable and DragTarget, How can I build a widget after a Draggable has been accepted in a DragTarget? 此小部件已卸载,因此 State 不再具有上下文(应视为已失效)。 当从 flutter 应用程序注销时 - This widget has been unmounted, so the State no longer has a context (and should be considered defunct). when Sign out from flutter app
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM