Reputation: 1227
I have ListView.builder with a Column child and ListTile and Row child of that Column. What i'm trying to achieve is, when a user tap the ListTile item, it will make it's Row visible. How can i get which ListTile item is clicked and the Row connected to it will be visible?
Here is the code:
class DebitInvoiceLineList extends StatefulWidget {
final List<DebitInvoiceLine> dInvoiceLines;
DebitInvoiceLineList({
@required
this.dInvoiceLines
});
@override
_DebitInvoiceLineListState createState() => _DebitInvoiceLineListState();
}
class _DebitInvoiceLineListState extends State<DebitInvoiceLineList> {
bool visibleEdit = false;
_changed(bool visibility) {
setState(() {
visibleEdit = visibility;
});
}
@override
Widget build(BuildContext context) {
return NotificationListener<OverscrollIndicatorNotification>(
onNotification: (OverscrollIndicatorNotification overScroll) {
overScroll.disallowGlow();
return false;
},
child: ListView.builder(
itemCount: widget.dInvoiceLines.length,
shrinkWrap: true,
itemBuilder: (context, int index) {
return Column(
children: <Widget>[
ListTile(
key: Key(index.toString()),
leading: Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Text(
(index + 1).toString() + " - "
),
),
title: Text(widget.dInvoiceLines[index].description),
trailing: Text(
NumberFormat.currency(locale: 'da').format(widget.dInvoiceLines[index].price),
style: dSecondHeaderStyle
),
onTap: () {
setState(() {
visibleEdit = !visibleEdit;
});
}
),
Visibility(
visible: visibleEdit,
key: Key(index.toString()),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Edit"),
Text("Delete")
],
),
)
],
);
},
),
);
Can i do it by the keys or how can i get the correct item?
Upvotes: 0
Views: 1273
Reputation: 8998
The best thing you can do is to make a private instance for every ListTile
with the Row
underneath it. In this way, you can do the operation to a specific widget, it will not affect others.
Disclaimer: I have not used your code, but showed the best way to achieve what you want to achieve right now.
Home page:
class _MyHomePageState extends State<MyHomePage> {
// This is just for demo
List<String> _items = ['Item 1', 'Item 2', 'Item 3'];
Widget get listTile{
return ListView.builder(
itemCount: _items.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index){
// This is a widget which is created out of this widget
// and created with private instance, every instance
// will be having it's own bool to show/hide the Row
// There is a widget named ListTileWidget mentioned in the code
return ListTileWidget(title: _items[index]);
}
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: double.infinity,
width: double.infinity,
child: listTile
)
);
}
}
ListTileWidget:
class _ListTileWidget extends State<ListTileWidget> {
// This is for individual ListTile reference
bool isSelected = false;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ListTile(
title: Text(widget.title),
onTap: () => setState((){ isSelected = !isSelected; }) // doing toggle
),
// for each listtile individually, it shows/hide Row accordingly
isSelected
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Edit"),
SizedBox(width: 50.0),
Text("Delete")
]
) : Container()
]
);
}
}
Result:
Upvotes: 0
Reputation: 2529
visibleEdit will work on all lines and this is an error, you need to define a variable inside the DebitInvoiceLine class, And use it instead of the old one, like this:
onTap: () {
setState(() {
widget.dInvoiceLines[index].visibleEdit=
!widget.dInvoiceLines[index].visibleEdit;
});
}
Upvotes: 1
Reputation: 1101
Make use of ExpansionTile widget, in flutter we achieve this behaviour using ExpansionTile.
Check the sample code:
import 'package:flutter/material.dart';
class ExpansionTileSample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('ExpansionTile'),
),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) =>
EntryItem(data[index]),
itemCount: data.length,
),
),
);
}
}
// One entry in the multilevel list displayed by this app.
class Entry {
Entry(this.title, [this.children = const <Entry>[]]);
final String title;
final List<Entry> children;
}
// The entire multilevel list displayed by this app.
final List<Entry> data = <Entry>[
Entry(
'Chapter A',
<Entry>[
Entry(
'Section A0',
<Entry>[
Entry('Item A0.1'),
Entry('Item A0.2'),
Entry('Item A0.3'),
],
),
Entry('Section A1'),
Entry('Section A2'),
],
),
Entry(
'Chapter B',
<Entry>[
Entry('Section B0'),
Entry('Section B1'),
],
),
Entry(
'Chapter C',
<Entry>[
Entry('Section C0'),
Entry('Section C1'),
Entry(
'Section C2',
<Entry>[
Entry('Item C2.0'),
Entry('Item C2.1'),
Entry('Item C2.2'),
Entry('Item C2.3'),
],
),
],
),
];
// Displays one Entry. If the entry has children then it's displayed
// with an ExpansionTile.
class EntryItem extends StatelessWidget {
const EntryItem(this.entry);
final Entry entry;
Widget _buildTiles(Entry root) {
if (root.children.isEmpty) return ListTile(title: Text(root.title));
return ExpansionTile(
key: PageStorageKey<Entry>(root),
title: Text(root.title),
children: root.children.map(_buildTiles).toList(),
);
}
@override
Widget build(BuildContext context) {
return _buildTiles(entry);
}
}
void main() {
runApp(ExpansionTileSample());
}
You can create your own data model.
Reference: https://flutter.dev/docs/catalog/samples/expansion-tile-sample
Upvotes: 1