Sami Hammoud
Sami Hammoud

Reputation: 57

ListTile Card color onTap

I'm trying to implement the call button inside a card widget,

I want the whole card background to change color to blue (like selected) when I press the call button, and to be changed back to normal when I press any other card, like to make the call button switch for card selection, tried to use the setState function but it didn't work since it changes color only when I'm tapping the whole card not a specific button in it. How do I make the whole card selected when I press the call button and released when I press any other card (after I get back from the dialer application)

Here's my code:

_launchCaller() async {
  const url = "tel:+972545522973";
  if (await canLaunch(url)) {
    await launch(url);
  } else {
    throw 'Could not launch $url';
  }
}
return Padding(
  padding: const EdgeInsets.only(top: 8.0),
  child: Card(
    margin: EdgeInsets.fromLTRB(20.0, 6.0, 20.0, 0.0),
    color: Colors.brown[30],
    child: ListTile(
      isThreeLine: true,
      title: Row(
        children: <Widget> [
          Container(
            child:Text(widget.helpRequest.category.description) ,
            alignment: Alignment.topLeft,
          ),
          Spacer(),
          Container(
            child:Text(formatter.format(now)),
            alignment: Alignment.topRight,
          ),
        ]
      )
      ,
      subtitle: Container(
        child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            GetUserName(widget.helpRequest.sender_id, DataBaseService().userInNeedCollection),
            Text(widget.helpRequest.description),
          ]
        )
      ),
      trailing: Row(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          GestureDetector(
            onTap: (){
              _launchCaller();
              ** Here i think I should add the code **
            },
            onLongPress: () => print("Long Press: Call"),
            child: Padding(
                padding: EdgeInsets.all(16.0),
                child: Icon(Icons.call,
                    size: 20.0,
                    color: Colors.green,
                    )
            ),
          ),
        ],
      ),
    ),
  ),
);

And this setState function I tried to use which didn't work well in my case (I was changing state on the onTap function):

void initState() {
           super.initState();

            color = Colors.transparent;
  }

Upvotes: 1

Views: 1297

Answers (2)

Ketan Ramteke
Ketan Ramteke

Reputation: 10655

Final Output:

enter image description here

You can set the color of a specific card, but for that to happen, you need to have some way to reference that the selected card was clicked on, with this reference we can decide whether the card is selected and if yes then change the color according to our preference.

In the following example, I am more or less using the same card widget template that you stated in the question, then I am using the ListView.builder to render five cards, each having the same functionality.

Whenever the call button is pressed, the corresponding index of that specific card is assigned to the state selectedIndex and from this, we can assign the color to the selected Card.

Here is the full example:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int selectedIndex;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.only(top: 8.0),
        child: ListView.builder(
          itemCount: 5,
          itemBuilder: (context, index) {
            return Padding(
              padding: const EdgeInsets.only(top: 8.0),
              child: Card(
                margin: EdgeInsets.fromLTRB(20.0, 6.0, 20.0, 0.0),
                color: index == selectedIndex
                    ? Colors.amberAccent
                    : Colors.brown[30],
                child: ListTile(
                  isThreeLine: true,
                  title: Row(children: <Widget>[
                    Container(
                      child: Text("Some Text"),
                      alignment: Alignment.topLeft,
                    ),
                    Spacer(),
                    Container(
                      child: Text("Some Text"),
                      alignment: Alignment.topRight,
                    ),
                  ]),
                  subtitle: Container(
                      child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: <Widget>[
                        Text("Some Text"),
                      ])),
                  trailing: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      GestureDetector(
                        onTap: () {
                          setState(() {
                            selectedIndex = index;
                          });
                        },
                        onLongPress: () => print("Long Press: Call"),
                        child: Padding(
                          padding: EdgeInsets.all(16.0),
                          child: Icon(
                            Icons.call,
                            size: 20.0,
                            color: Colors.green,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

Upvotes: 2

shb
shb

Reputation: 6277

Keep track of the clicked item and pass the index of the list

int clickedItemPosition = -1;
  
bool isChecked(currentPosition) => clickedItemPosition == currentPosition;

Then in your card

 //..
Card(
    margin: EdgeInsets.fromLTRB(20.0, 6.0, 20.0, 0.0),
    color: isChecked(index) ? Colors.blue : Colors.transparent,
//..

In Gesture detector update the clickedItemPosition

//...
GestureDetector(
            onTap: () => setState(() => clickedItemPosition = index),
            //..

Upvotes: 1

Related Questions