'Flutter: After deleting the last item in ListView item, it always gives a RangeError. Why doesn't it stop on new itemCount.length?
I've a listview of products and when I delete any item other than the last item, it works fine. But when I delete the last item in the listview, may it be 0th or 5th, it always gives this error.
main.dart
routes: {
'/': (BuildContext context) => ProductsPage(_products, _addProduct, _deleteProduct),
'/admin': (BuildContext context) => ProductsAdminPage(),
},
onGenerateRoute: (RouteSettings settings) {
final List<String> pathElements = settings.name.split('/');
if (pathElements[0] != '') {
return null;
}
if (pathElements[1] == 'product') {
final int index = int.parse(pathElements[2]);
print('[main.dart:] current items lefts: ' + _products.length.toString());
print('[main.dart:] current index: ' + index.toString());
return MaterialPageRoute<bool>(
builder: (BuildContext context) {
print('checking error place.');
print(index);
/* === PRECISE LOCATION OF ERROR WHEN THERE IS NO ITEM IS LEFT AND LAST ONE IS DELETED === */
/*
I/flutter ( 7709): [main.dart:] current items lefts: 1
I/flutter ( 7709): [main.dart:] current index: 0
I/flutter ( 7709): checking error place.
I/flutter ( 7709): 0
I/flutter ( 7709): Sweets
I/flutter ( 7709): assets/food.jpg
I/flutter ( 7709): [Products Widget] Product removedAt index: 0
I/flutter ( 7709): checking error place.
I/flutter ( 7709): 0
Another exception was thrown: RangeError (index): Invalid value: Valid value range is empty: 0
I/flutter ( 7709): [ProductManager State] build()
I/flutter ( 7709): [Products Widget] Constructor
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building Builder(dirty):
RangeError (index): Invalid value: Valid value range is empty: 0
The relevant error-causing widget was:
MaterialApp file:///D:/MobileDev/maximillian/tutorial/lib/main.dart:48:12
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:166:60)
#1 _MyAppState.build.<anonymous closure>.<anonymous closure> (package:tutorial/main.dart:72:30)
#2 MaterialRouteTransitionMixin.buildPage (package:flutter/src/material/page.dart:104:34)
#3 _ModalScopeState.build.<anonymous closure> (package:flutter/src/widgets/routes.dart:826:45)
#4 Builder.build (package:flutter/src/widgets/basic.dart:7118:48)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter ( 7709): [Products Widget] build()
*/
print(_products[index]['title']);
print(_products[index]['image']);
return ProductPage(_products[index]['title'], _products[index]['image']);
},
);
}
return null;
},
ProductPage
class ProductPage extends StatelessWidget {
final String title;
final String imageUrl;
ProductPage(this.title, this.imageUrl);
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
print('Back button pressed');
Navigator.of(context).pop(false);
return Future.value(false); // only to accommodate the return type WillPopScope widget
},
child: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(imageUrl),
Container(padding: EdgeInsets.all(10.0), child: Text(title)),
Container(
padding: EdgeInsets.all(10.0),
child: RaisedButton(
color: Theme.of(context).accentColor,
onPressed: () => Navigator.of(context).pop(true),
child: Text('Delete'),
),
),
],
),
),
);
}
}
Products widget
class Products extends StatelessWidget {
final List<Map<String, String>> products;
final Function deleteProduct;
Products(this.products, {this.deleteProduct}) {
print('[Products Widget] Constructor');
}
Widget _buildProductList() {
Widget productCards;
if (products.length > 0) {
/* === PRECISE LOCATION OF ERROR WHEN THE LAST ONE OF TWO ITEMS IS DELETED, ONLY ONE ITEM LEFT IN THE LISTVIEW
VIEW THE ERROR BELOW TO SEE BELOW PRINTED LINE. ===
I/flutter ( 7709): products.dart: total items - 1
*/
print('products.dart: total items - ' + products.length.toString()); // 1
productCards = ListView.builder(
// builder always passes two args implicitly. context & index
itemBuilder: _buildProductItem,
itemCount: products.length,
);
} else {
productCards = Center(
child: Text('No Products found. Please add some'),
);
}
return productCards;
}
Widget _buildProductItem(BuildContext context, int index) {
/* === WHERE THE LAST ONE OF TWO ITEMS IS DELETED, ONLY ONE ITEM LEFT IN THE LISTVIEW
THE BELOW LINE PRINTS 0 BUT IT GIVES THE FOLLOWING ERROR BEFORE THIS. */
/*
I/flutter ( 7709): products.dart: total items - 1
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building Builder(dirty):
RangeError (index): Invalid value: Only valid value is 0: 1
The relevant error-causing widget was:
MaterialApp file:///D:/MobileDev/maximillian/tutorial/lib/main.dart:48:12
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:166:60)
#1 _MyAppState.build.<anonymous closure>.<anonymous closure> (package:tutorial/main.dart:72:30)
#2 MaterialRouteTransitionMixin.buildPage (package:flutter/src/material/page.dart:104:34)
#3 _ModalScopeState.build.<anonymous closure> (package:flutter/src/widgets/routes.dart:826:45)
#4 Builder.build (package:flutter/src/widgets/basic.dart:7118:48)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter ( 7709): [product widget] current item index: 0
*/
print('[product widget] current item index: ' + index.toString()); // 0
return Card(
child: Column(
children: <Widget>[
Image.asset(products[index]['image']),
Text(products[index]['title']),
ButtonBar(
alignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Text('Details'),
onPressed: () => Navigator.pushNamed<bool>(
context, '/product/' + index.toString())
.then((bool value) {
if (value) {
deleteProduct(index);
}
}),
),
],
)
],
),
);
}
Widget _buildProductItem(BuildContext context, int index) {
/* === WHERE THE LAST ONE OF TWO ITEMS IS DELETED, ONLY ONE ITEM LEFT IN THE LISTVIEW
THE BELOW LINE PRINTS 0 BUT IT GIVES THE FOLLOWING ERROR BEFORE THIS. */
/*
I/flutter ( 7709): products.dart: total items - 1
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following RangeError was thrown building Builder(dirty):
RangeError (index): Invalid value: Only valid value is 0: 1
The relevant error-causing widget was:
MaterialApp file:///D:/MobileDev/maximillian/tutorial/lib/main.dart:48:12
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:166:60)
#1 _MyAppState.build.<anonymous closure>.<anonymous closure> (package:tutorial/main.dart:72:30)
#2 MaterialRouteTransitionMixin.buildPage (package:flutter/src/material/page.dart:104:34)
#3 _ModalScopeState.build.<anonymous closure> (package:flutter/src/widgets/routes.dart:826:45)
#4 Builder.build (package:flutter/src/widgets/basic.dart:7118:48)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter ( 7709): [product widget] current item index: 0
*/
print('[product widget] current item index: ' + index.toString()); // 0
return Card(
child: Column(
children: <Widget>[
Image.asset(products[index]['image']),
Text(products[index]['title']),
ButtonBar(
alignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Text('Details'),
onPressed: () => Navigator.pushNamed<bool>(
context, '/product/' + index.toString())
.then((bool value) {
if (value) {
deleteProduct(index);
}
}),
),
],
)
],
),
);
}
@override
Widget build(BuildContext context) {
print('[Products Widget] build()');
return _buildProductList();
}
}
In either case, the required page loads fine after deletion, however, there's a blink of red error screen just before the required page is loaded.
Solution 1:[1]
It's a bit late I had a similar problem and I solved it by using the condition around the widget page and not inside it something like
if(_products.isNotEmpty){
return ProductPage(_products[index]['title'], _products[index]['image']);
} else {
return Center(
child: Text('No Products found. Please add some'),
);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |