用一个非常简短、具体和实用的方式来理解:这是一个 ListView 位于另一个 ListView 中的错误。现在,更多关于这些 Widget 的构建方式的详细信息: 一个简单的 Scaffold,其主体显示一个 ListView(无论是 ListView 还是 ListView.builder,两者都将具有相同的行为,因此,同样的错误。)其中有一个 Column Widget,里面有两个 Widget:一个 StatefulWidget 和另一个 ListView.builder Widget,并且 Column Widget 内的这个 ListView.builder Widget 有一个 ListTile Widget,它只显示信息:带有索引的文本和删除图标。而 StatefulWidget 包含另一个 ListView.builder。
通过尽可能简单、最基本和干净的方式构建这个简单的结构,您会发现 StatefulWidget 的行为存在一个非常奇怪的问题。问题是:它“卡在”特定位置,不是控制 ListView Widget 的列表中的位置,而是布局本身,这是极其奇怪的。即使您删除了具有 StatefulWidget 的 Column Widget,它也不会消失,而是保留下来,无论您在调用 StatefulWidget 时是否使用 const。
这个极其奇怪的问题已经花了我整整一周的时间,我厌倦了试图弄清楚发生了什么,我什至认为我要疯了。
我为此深受创伤,所以我创建了一个极其简单的DartPad,只有150行代码,下面我将代码分享给大家,以便大家测试。
想到一个几乎让我失去理智的问题。
希望Flutter团队尽快看到并修复这个奇怪的问题。
更改 DartPad 中的 Flutter 版本(Main、Stable、Beta)并且始终保持相同的行为。在任何情况下,我都不会更改计算机上安装的 Flutter,因为我对需要为客户的特定项目保留的文件进行了编辑。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ListViewWithDelete(),
);
}
}
class ListViewWithDelete extends StatefulWidget {
@override
_ListViewWithDeleteState createState() => _ListViewWithDeleteState();
}
class _ListViewWithDeleteState extends State<ListViewWithDelete> {
List<String> items = List<String>.generate(10, (index) => 'Item ${index + 1}');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView com Botão de Excluir'),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Column(
children: [
AulaWidget(),
ListTile(
leading: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
items.removeAt(index);
});
},
),
title: Text(items[index]),
),
],
);
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {
setState(() {
items.add('Item ${items.length + 1}');
});
},
child: Text('Adicionar Item'),
),
),
],
),
);
}
}
class AulaWidget extends StatefulWidget {
@override
_AulaWidgetState createState() => _AulaWidgetState();
}
class _AulaWidgetState extends State<AulaWidget> {
List<String> listaDeAulas = List<String>.generate(5, (index) => 'Aula $index');
@override
Widget build(BuildContext context) {
return Column(
children: [
ListView.builder(
itemCount: listaDeAulas.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
setState(() {
listaDeAulas.removeAt(index);
});
},
),
title: Text("Sinais de Trânsito ${listaDeAulas[index]}"),
);
},
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
setState(() {
listaDeAulas.add('Aula ${listaDeAulas.length}');
});
},
child: const Text('Adicionar Aula'),
),
),
],
),
],
);
}
void _showConfirmationDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Adicionar Nova Aula"),
content: const Text("Deseja adicionar uma nova aula?"),
actions: <Widget>[
TextButton(
child: const Text("Cancelar"),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text("Confirmar"),
onPressed: () {
setState(() {
listaDeAulas.add('Aula ${listaDeAulas.length}');
});
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
简而言之,您不需要使用嵌套的 ListView,因为内部 ListView 替换为 Column 并渲染子级。
class _AulaWidgetState extends State<AulaWidget> {
List<String> listaDeAulas = List<String>.generate(5, (index) => 'Aula $index');
@override
Widget build(BuildContext context) {
return Column(
children: [
...listaDeAulas.map((e) => ListTile(
leading: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
setState(() {
listaDeAulas.remove(e);
});
},
),
title: Text("Sinais de Trânsito ${e}"),
)),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
setState(() {
listaDeAulas.add('Aula ${listaDeAulas.length}');
});
},
child: const Text('Adicionar Aula'),
),
),
],
),
],
);
}
}