我是Flutter的新手。我制作了一个具有类别,子类别和项目的目录的应用程序(如您在照片中所见)。我的小部件树是:
Catalogue(cart):{[Category-List, Subcategory-List, Item-List:{ItemRow(ListTile)}]}.
我遇到以下问题:我的购物车中有物品,其数量显示在目录中(项目行)。当我从购物车中删除一个项目或清除所有项目时,我无法将项目行的文本字段的控制器设置为零,从而导致已安装小部件(当前项目行)。我使用范围模型来添加,删除或更新购物车中的项目。所以,我的问题只是视觉上的。当我单击另一个类别然后转到上一个类别时,控制器已正确设置为零(因为使用initstate()再次重新创建了项目行)。
我的问题有解决方案吗?谢谢!
应用图片: Application Image
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代码:
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代码:
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代码:
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;
});
}
}
答案 0 :(得分:0)
由于没有代码,我将列出使用范围模型时导致此错误的常见错误:
1。不打电话通知
您必须在作用域模型中调用notifyListeners()来更新使用您的功能的UI。
2。不告诉模型要重建
如果您不使用ScopedModelDescendent小部件,而是使用
appModel = ScopedModel.of<AppModel>(context);
将代码更改为
appModel = ScopedModel.of<AppModel>(context, rebuildOnChange: true);
如果您同时做这两种情况,请为您的UI和范围模型发布代码,以便更轻松地提供我们的帮助。