mehmetserif
mehmetserif

Reputation: 1227

Making Visible a Row on ListtTile Tap

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

Answers (3)

Alok
Alok

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:

THIS IS HOW THE RESULT LOOK LIKE

Upvotes: 0

farouk osama
farouk osama

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

Mohd Danish Khan
Mohd Danish Khan

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

Related Questions