我想在 Flutter 应用程序中创建一个可滚动的 ListView。我尝试从本教程复制使用 ListView 的示例代码。我修改了项目列表,使其比屏幕长。使用教程中的嵌入式版本时,它可以在浏览器中滚动,但当我构建 iOS 应用程序(使用 Visual Studio Code)并在 iOS 模拟器中测试它时,它不会滚动。
这是我修改后的代码:
import 'package:flutter/material.dart';
class Product {
const Product({required this.name});
final String name;
}
typedef CartChangedCallback = Function(Product product, bool inCart);
class ShoppingListItem extends StatelessWidget {
ShoppingListItem({
required this.product,
required this.inCart,
required this.onCartChanged,
}) : super(key: ObjectKey(product));
final Product product;
final bool inCart;
final CartChangedCallback onCartChanged;
Color _getColor(BuildContext context) {
// The theme depends on the BuildContext because different
// parts of the tree can have different themes.
// The BuildContext indicates where the build is
// taking place and therefore which theme to use.
return inCart //
? Colors.black54
: Theme.of(context).primaryColor;
}
TextStyle? _getTextStyle(BuildContext context) {
if (!inCart) return null;
return const TextStyle(
color: Colors.black54,
decoration: TextDecoration.lineThrough,
);
}
@override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
onCartChanged(product, inCart);
},
leading: CircleAvatar(
backgroundColor: _getColor(context),
child: Text(product.name[0]),
),
title: Text(
product.name,
style: _getTextStyle(context),
),
);
}
}
class ShoppingList extends StatefulWidget {
const ShoppingList({required this.products, super.key});
final List<Product> products;
// The framework calls createState the first time
// a widget appears at a given location in the tree.
// If the parent rebuilds and uses the same type of
// widget (with the same key), the framework re-uses
// the State object instead of creating a new State object.
@override
State<ShoppingList> createState() => _ShoppingListState();
}
class _ShoppingListState extends State<ShoppingList> {
final _shoppingCart = <Product>{};
void _handleCartChanged(Product product, bool inCart) {
setState(() {
// When a user changes what's in the cart, you need
// to change _shoppingCart inside a setState call to
// trigger a rebuild.
// The framework then calls build, below,
// which updates the visual appearance of the app.
if (!inCart) {
_shoppingCart.add(product);
} else {
_shoppingCart.remove(product);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Shopping List'),
),
body: ListView(
padding: const EdgeInsets.symmetric(vertical: 8),
children: widget.products.map((product) {
return ShoppingListItem(
product: product,
inCart: _shoppingCart.contains(product),
onCartChanged: _handleCartChanged,
);
}).toList(),
),
);
}
}
void main() {
runApp(const MaterialApp(
title: 'Shopping App',
home: ShoppingList(
products: [
Product(name: 'Eggs'),
Product(name: 'Flour'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
Product(name: 'Chocolate chips'),
],
),
));
}
您的实际问题不是滚动问题,因为那会工作得很好。
您的问题应该在控制台中可见:
'package:flutter/src/rendering/sliver_multi_box_adaptor.dart': Failed assertion: line 259 pos 16: 'child == null || indexOf(child) > index': is not true.
这是因为您在材质应用程序中将
ObjectKey(product)
(这非常好)与 const
构造函数一起使用:
runApp(const MaterialApp(
这样,所有列表项也将被构造为 const。由于您的购物车中有多次相同的产品 (
Chocolate chips
),因此您多次生成完全相同的对象密钥,因此会出现错误。请参阅 this 答案,了解 const 在 Flutter 中如何工作的更多信息。
所以,要解决你可以做两件事:
const
,这样每个产品都会有自己的身份。