如何在 flutter 中添加另一个可重新排序的列表视图
how to add another reorderable list view in flutter
我正在学习 Flutter,是否可以创建一个可重新排序的列表,其中的项目本身就是可重新排序的列表(下图)。如果有人可以建议我或帮助我,我将不胜感激。谢谢
这是我的代码
class _ReorderItemsState extends State<ReorderItems> {
List<String> topTen = [
"EPL",
"MLS",
"LLG",
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: ReorderableListView(
onReorder: onReorder,
children: getListItem(),
),
);
}
List<ExpansionTile> getListItem() => topTen.asMap()
.map((index, item) => MapEntry(index, buildTenableListTile(item, index)))
.values.toList();
ExpansionTile buildTenableListTile(String item, int index) => ExpansionTile(
key: ValueKey(item),
title: Text(item),
leading: Icon(Icons.list),
backgroundColor: Colors.blue,
);
void onReorder(int oldIndex, int newIndex){
if(newIndex > oldIndex){
newIndex -=1;
}
setState(() {
String game = topTen[oldIndex];
topTen.removeAt(oldIndex);
topTen.insert(newIndex, game);
});
}
}
您可以通过设置 ExpansionTile
小部件的 children
属性来实现。
方法如下
- 您需要一个通用的 数据处理程序 或某种状态管理来将 parent 和 children 的状态保持在小部件外部以避免当 child 列表更改时重建。为简洁起见,我使用单例来保存公共数据。在实际情况下,这应该是一些基于
ChangeNotifier
或 BLoc
的方法。但是,如果您改变 parent 或 child 列表,则不需要完全重建,因为 Flutter,小部件是不可变的。
/// Holding the common data as a singleton to avoid excessive rebuilds.
/// Usually this should be replaced with a manager or bloc or changenotifier class
class DataHolder {
List<String> parentKeys;
Map<String, List<String>> childMap;
DataHolder._privateConstructor();
static final DataHolder _dataHolder = DataHolder._privateConstructor();
static DataHolder get instance => _dataHolder;
factory DataHolder.initialize({@required parentKeys}) {
_dataHolder.parentKeys = parentKeys;
_dataHolder.childMap = {};
for (String key in parentKeys) {
_dataHolder.childMap.putIfAbsent(
key, () => ['${key}child_1', '${key}child_2', '${key}child_3']);
}
return _dataHolder;
}
}
- 创建一个小部件,其中 return 是 child
ReorderableListView
,每个小部件都具有唯一 ScrollController
。例如ReorderList
小部件。除了我 return 一个 ListTile
而不是 ExpansionTile
并设置 scrollController
属性外,它几乎与您写的完全相同。当前的稳定版本没有这个属性。因此,在此解决方案中,它用 PrimaryScrollController
小部件包装以避免重复使用 scrollController。
class ReorderList extends StatefulWidget {
final String parentMapKey;
ReorderList({this.parentMapKey});
@override
_ReorderListState createState() => _ReorderListState();
}
class _ReorderListState extends State<ReorderList> {
@override
Widget build(BuildContext context) {
return PrimaryScrollController(
controller: ScrollController(),
child: ReorderableListView(
// scrollController: ScrollController(),
onReorder: onReorder,
children: DataHolder.instance.childMap[widget.parentMapKey]
.map(
(String child) => ListTile(
key: ValueKey(child),
leading: Icon(Icons.done_all),
title: Text(child),
),
)
.toList(),
),
);
}
void onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
List<String> children = DataHolder.instance.childMap[widget.parentMapKey];
String game = children[oldIndex];
children.removeAt(oldIndex);
children.insert(newIndex, game);
DataHolder.instance.childMap[widget.parentMapKey] = children;
// Need to set state to rebuild the children.
setState(() {});
}
}
- 在 Parent
ExpansionTile
小部件中将此新小部件设置为 children
之一。这个 children 和 parent 都是从 DataHolder
单例 class 的值构建的。
Note I am setting a constant height to avoid conflicts of layout. You have to play with this for dynamic sizes.
class ReorderItems extends StatefulWidget {
final List<String> topTen;
ReorderItems({this.topTen});
@override
_ReorderItemsState createState() => _ReorderItemsState();
}
class _ReorderItemsState extends State<ReorderItems> {
@override
void initState() {
super.initState();
// initialize the children for the Expansion tile
// This initialization can be replaced with any logic like network fetch or something else.
DataHolder.initialize(parentKeys: widget.topTen);
}
@override
Widget build(BuildContext context) {
return PrimaryScrollController(
key: ValueKey(widget.topTen.toString()),
controller: ScrollController(),
child: ReorderableListView(
onReorder: onReorder,
children: getListItem(),
),
);
}
List<ExpansionTile> getListItem() => DataHolder.instance.parentKeys
.asMap()
.map((index, item) => MapEntry(index, buildTenableListTile(item, index)))
.values
.toList();
ExpansionTile buildTenableListTile(String mapKey, int index) => ExpansionTile(
key: ValueKey(mapKey),
title: Text(mapKey),
leading: Icon(Icons.list),
backgroundColor: Colors.blue,
children: [
Container(
key: ValueKey('$mapKey$index'),
height: 400,
child: Container(
padding: EdgeInsets.only(left: 20.0),
child: ReorderList(
parentMapKey: mapKey,
),
),
),
],
);
void onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
setState(() {
String game = DataHolder.instance.parentKeys[oldIndex];
DataHolder.instance.parentKeys.removeAt(oldIndex);
DataHolder.instance.parentKeys.insert(newIndex, game);
});
}
}
codepen 中提供了一个完整的解决方案。
我更改了代码以从 parent 小部件动态接受项目列表。您将不得不尝试如何维护数据和减少重建。但一般来说,只要维护列表,child ReorderableListView
就可以工作。
希望这对您有所帮助。
我正在学习 Flutter,是否可以创建一个可重新排序的列表,其中的项目本身就是可重新排序的列表(下图)。如果有人可以建议我或帮助我,我将不胜感激。谢谢
这是我的代码
class _ReorderItemsState extends State<ReorderItems> {
List<String> topTen = [
"EPL",
"MLS",
"LLG",
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: ReorderableListView(
onReorder: onReorder,
children: getListItem(),
),
);
}
List<ExpansionTile> getListItem() => topTen.asMap()
.map((index, item) => MapEntry(index, buildTenableListTile(item, index)))
.values.toList();
ExpansionTile buildTenableListTile(String item, int index) => ExpansionTile(
key: ValueKey(item),
title: Text(item),
leading: Icon(Icons.list),
backgroundColor: Colors.blue,
);
void onReorder(int oldIndex, int newIndex){
if(newIndex > oldIndex){
newIndex -=1;
}
setState(() {
String game = topTen[oldIndex];
topTen.removeAt(oldIndex);
topTen.insert(newIndex, game);
});
}
}
您可以通过设置 ExpansionTile
小部件的 children
属性来实现。
方法如下
- 您需要一个通用的 数据处理程序 或某种状态管理来将 parent 和 children 的状态保持在小部件外部以避免当 child 列表更改时重建。为简洁起见,我使用单例来保存公共数据。在实际情况下,这应该是一些基于
ChangeNotifier
或BLoc
的方法。但是,如果您改变 parent 或 child 列表,则不需要完全重建,因为 Flutter,小部件是不可变的。
/// Holding the common data as a singleton to avoid excessive rebuilds.
/// Usually this should be replaced with a manager or bloc or changenotifier class
class DataHolder {
List<String> parentKeys;
Map<String, List<String>> childMap;
DataHolder._privateConstructor();
static final DataHolder _dataHolder = DataHolder._privateConstructor();
static DataHolder get instance => _dataHolder;
factory DataHolder.initialize({@required parentKeys}) {
_dataHolder.parentKeys = parentKeys;
_dataHolder.childMap = {};
for (String key in parentKeys) {
_dataHolder.childMap.putIfAbsent(
key, () => ['${key}child_1', '${key}child_2', '${key}child_3']);
}
return _dataHolder;
}
}
- 创建一个小部件,其中 return 是 child
ReorderableListView
,每个小部件都具有唯一ScrollController
。例如ReorderList
小部件。除了我 return 一个ListTile
而不是ExpansionTile
并设置scrollController
属性外,它几乎与您写的完全相同。当前的稳定版本没有这个属性。因此,在此解决方案中,它用PrimaryScrollController
小部件包装以避免重复使用 scrollController。
class ReorderList extends StatefulWidget {
final String parentMapKey;
ReorderList({this.parentMapKey});
@override
_ReorderListState createState() => _ReorderListState();
}
class _ReorderListState extends State<ReorderList> {
@override
Widget build(BuildContext context) {
return PrimaryScrollController(
controller: ScrollController(),
child: ReorderableListView(
// scrollController: ScrollController(),
onReorder: onReorder,
children: DataHolder.instance.childMap[widget.parentMapKey]
.map(
(String child) => ListTile(
key: ValueKey(child),
leading: Icon(Icons.done_all),
title: Text(child),
),
)
.toList(),
),
);
}
void onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
List<String> children = DataHolder.instance.childMap[widget.parentMapKey];
String game = children[oldIndex];
children.removeAt(oldIndex);
children.insert(newIndex, game);
DataHolder.instance.childMap[widget.parentMapKey] = children;
// Need to set state to rebuild the children.
setState(() {});
}
}
- 在 Parent
ExpansionTile
小部件中将此新小部件设置为children
之一。这个 children 和 parent 都是从DataHolder
单例 class 的值构建的。Note I am setting a constant height to avoid conflicts of layout. You have to play with this for dynamic sizes.
class ReorderItems extends StatefulWidget {
final List<String> topTen;
ReorderItems({this.topTen});
@override
_ReorderItemsState createState() => _ReorderItemsState();
}
class _ReorderItemsState extends State<ReorderItems> {
@override
void initState() {
super.initState();
// initialize the children for the Expansion tile
// This initialization can be replaced with any logic like network fetch or something else.
DataHolder.initialize(parentKeys: widget.topTen);
}
@override
Widget build(BuildContext context) {
return PrimaryScrollController(
key: ValueKey(widget.topTen.toString()),
controller: ScrollController(),
child: ReorderableListView(
onReorder: onReorder,
children: getListItem(),
),
);
}
List<ExpansionTile> getListItem() => DataHolder.instance.parentKeys
.asMap()
.map((index, item) => MapEntry(index, buildTenableListTile(item, index)))
.values
.toList();
ExpansionTile buildTenableListTile(String mapKey, int index) => ExpansionTile(
key: ValueKey(mapKey),
title: Text(mapKey),
leading: Icon(Icons.list),
backgroundColor: Colors.blue,
children: [
Container(
key: ValueKey('$mapKey$index'),
height: 400,
child: Container(
padding: EdgeInsets.only(left: 20.0),
child: ReorderList(
parentMapKey: mapKey,
),
),
),
],
);
void onReorder(int oldIndex, int newIndex) {
if (newIndex > oldIndex) {
newIndex -= 1;
}
setState(() {
String game = DataHolder.instance.parentKeys[oldIndex];
DataHolder.instance.parentKeys.removeAt(oldIndex);
DataHolder.instance.parentKeys.insert(newIndex, game);
});
}
}
codepen 中提供了一个完整的解决方案。
我更改了代码以从 parent 小部件动态接受项目列表。您将不得不尝试如何维护数据和减少重建。但一般来说,只要维护列表,child ReorderableListView
就可以工作。
希望这对您有所帮助。