How to make SwitchListTile Icon clickable? - dart

Is it possible to make the secondary property of the SwitchListTile tapable? In this case, an icon is used:
SwitchListTile(
title: const Text('Lights'),
value: _lights,
onChanged: (bool value) { setState(() { _lights = value; }); },
secondary: const Icon(Icons.lightbulb_outline), //can this be selected?
)
Ideally, instead of creating another widget, I would like to use the Icon in the secondary property to display a message when the user selects it.
Currently when the icon, or entire widget is selected, the switch is toggled. What is the best way to handle this action?
Thanks.

Wrap your Icon inside InkWell to handle the tap :
secondary: InkWell(
onTap: () {
print("click light");
},
child: const Icon(Icons.lightbulb_outline),
),
More info here: https://docs.flutter.io/flutter/material/InkWell-class.html

You could wrap your Icon in an IconButton.
SwitchListTile(
title: const Text('Lights'),
value: _lights,
onChanged: (value) => setState(() => _lights = value),
secondary: IconButton(
icon: Icon(Icons.lightbulb_outline),
onPressed: () {},
),
)

Related

Flutter: Mark ListTile as selected in drawer

I want to mark the ListTile of the current page as selected but 2 days ago I'm looking for a general way to do it.
I saw examples like this one where you hardcode the tile ID and use a case to know which is the current Tile. My question is, what if I have, to exaggerate, 100 ListTiles? How do I change the selected attribute programmatically to the selected Tile? Or a more real case: I have a Drawer that changes shape in each release, so keeping a code with the hardcoded IDs is not useful. I hope you understand the idea.
I've been trying different solutions for days but none seems general enough to me.
Simple create enum class like below.
enum DrawerSelection { home, favorites, settings}
Create enum object and pass pre-defined value if you want, in my case i pass home as selected ListTile item. Like below code.
class _MyHomePage extends State<MyHomePage> {
DrawerSelection _drawerSelection = DrawerSelection.home;
Then in ListTile use selected property and change enum onTap() like below code.
ListTile(
selected: _drawerSelection == DrawerSelection.home,
title: Text('Home'),
leading: Icon(Icons.home),
onTap: () {
Navigator.pop(context);
setState(() {
_drawerSelection = DrawerSelection.home;
_currentWidget = MainWidget();
_appBarTitle = Text("Home");
});
},
),
ListTile(
selected: _drawerSelection == DrawerSelection.favorites,
title: Text('Favorites'),
leading: Icon(Icons.favorite),
onTap: () {
Navigator.pop(context);
setState(() {
_drawerSelection = DrawerSelection.favorites;
_currentWidget = FavoritesWidget();
_appBarTitle = Text("Favorites");
});
},
),
ListTile(
selected: _drawerSelection == DrawerSelection.settings,
title: Text('Settings'),
leading: Icon(Icons.settings),
onTap: () {
Navigator.pop(context);
setState(() {
_drawerSelection = DrawerSelection.settings;
_currentWidget = SettingsWidget();
_appBarTitle = Text("Settings");
});
},
),
i think it is just simple you can create a new class that have your data and the bool selected
class Post {
final bool selected;
var data;
Post({
this.selected,
this.data
});
}
now when you use LIstView.builder in the itemBuilder if list[index].selected is true then set the color to blue if not then let it white or whatever in the on tap or onpressed whatever you are you using save the last clicked index in a global variable(called savedIndex)-initialize it with (-1)- and change selected to true at the this list index,then if savedIndex wasnt equal -1 change list[savedIndex].selected=false.
global variable
int selectedIndex =-1;
and itemBuilder.
itemBuilder: (BuildContext _context, int i) {
return GestureDetector(
child:
Container(
decoration: new BoxDecoration(
borderRadius: new BorderRadius.circular(16.0),
color:_list[index].selected? Colors.blue:colors.white,
),
child: _buildRow(_list[index]),) ,
onTap: () {
setState(){
if(savedIndex!=-1){
list[savedIndex].selected=false
}
_list[index].selected=true;
savedIndex=index;
}
}
);
}

Flutter snackbar dismiss on SnackBarAction onPressed

I want to dismiss SnackBar on SnackBarAction's onPressed method. I tried with Navigator.of(context).pop(); but SnackBar is not dismissing my screen get black instead.
Here is code:
void showInSnackBar(String value) {
homeScaffoldKey.currentState.showSnackBar(new SnackBar(content: new Text(value),
action: SnackBarAction(
label: 'Dissmiss',
textColor: Colors.yellow,
onPressed: () {
// Navigator.of(context).pop();
},
),));
}
You can also use,
Scaffold.of(context).hideCurrentSnackBar();
Be careful when you use context, use the correct context.
NOTE
In the new Flutter Version, this method is deprecated.
Therefore use
ScaffoldMessenger.of(context).hideCurrentSnackBar();
Try using hideCurrentSnackBar method
onPressed: () {
homeScaffoldKey.currentState.hideCurrentSnackBar();
},
Update
Use ScaffoldMessenger instead, heee you have the guide:
https://docs.flutter.dev/release/breaking-changes/scaffold-messenger
More info here: https://api.flutter.dev/flutter/material/ScaffoldMessengerState/hideCurrentSnackBar.html
ScaffoldMessenger.of(context).hideCurrentSnackBar();
If you want to replace snackbar that show only one time,
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final snackBar = SnackBar(content: Text("Hello, world"));
And also,
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
And also,
onPressed: () {
_scaffoldKey.currentState.removeCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(snackBar);
}
To clear the previous snackbars & show only the new one, use removeCurrentSnackBar method rather than hideCurrentSnackBar as it does not clear the stack. So the code will be
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
Define your SnackBar:
var snackBar = SnackBar(content: Text('Hello World'));
To show it:
ScaffoldMessenger.of(context).showSnackBar(snackBar);
To hide it:
ScaffoldMessenger.of(context).hideCurrentSnackBar();
To hide the last one and show a new one:
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
All these answers will not work because you cannot reference the ScarfoldMessenger from a Snackbar.
You'll have to save a reference to the snackbar and call it's "close" method. Like so
void Function () close; var snackbar = ScaffoldMessenger.of(context).showsnackbar(Snackbar (content:Text(), action: SnackbarAction(onPressed:()=>close())) close = ()=> snackbar.close();
Scaffold.of(context).hideCurrentSnackBar();
Above method is used previously but,
ScaffoldMessenger.of(context).hideCurrentSnackBar();
This is now recommended.
You can also show and dismiss a snackbar like this without any key
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Hello from snackbar!'),
action: SnackBarAction(
label: 'Dissmiss',
textColor: Colors.yellow,
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
),
);
As today using Flutter v3 there seems to be even a simpler solution to dismiss the Snackbar, which is shown on Flutter official cookbook, not sure if it works the same on previous versions.
It's as simple as passing an empty function to onPressed
final snackBar = SnackBar(
content: const Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Close',
onPressed: () {},
),
);
IconButton(
// 1
icon: Icon(_isFavorited ? Icons.favorite : Icons.favorite_border),
iconSize: 30,
// 2
color: Colors.red[400],
onPressed: () {
// 3
setState(() {
_isFavorited = !_isFavorited;
if (_isFavorited) {
final snackBar = SnackBar(
content: const Text('Added to favorite'),
action: SnackBarAction(
label: 'Ok',
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
}),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} else {
final snackBar = SnackBar(
content: const Text('removed from Favorite'),
action: SnackBarAction(
label: 'Ok',
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
}),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
});
},
)

Radio Button widget not working inside AlertDialog Widget in Flutter

I want user to select the option given in Radio Button before moving to second page in My Flutter Application. I'm showing Radio button widget inside Alertdialog it shows but radio button not changed after selecting.
Everything State Class
floatingActionButton: FloatingActionButton(
child: Icon(Icons.create),
onPressed: () {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("Select Grade System and No of Subjects"),
actions: <Widget>[
Radio(value: 0, groupValue: groupValue, onChanged: selectRadio),
Radio(value: 1, groupValue: groupValue, onChanged: selectRadio),
],
));
},
),
selectRadio Function
void selectRadio(int value)
{
setState(() {
groupValue=value;
});
}
I had the same issue. I solved it by using this:
showDialog<void>(
context: context,
builder: (BuildContext context) {
int selectedRadio = 0;
return AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int value) {
setState(() => selectedRadio = value);
},
);
}),
);
},
),
);
},
As I said the above comment showDialog creates new context and that setState on the calling widget therefore won't affect the dialog
You can create new stateful widget naming MyDialog.Checkout this gist such that you can get it(it uses dropdown but you can implement radio widget in same way).
custom language popup
You need to create separate statefulwidget class to handle state of radio button. Refer this example

DropdownButton can't update it's value(null)

The full page code is very long but my DropdownButton widget code like this.
The problems are,
first: I can't update my selectedCity, it doesn't get an update. Also, the print function calls null, since my cityList data is like [new york, paris, london] etc...
second: flutter doesn't change focus from any TextField to DropdownButton fully. I mean, clicked TextField, then DropdownButton but focus reverts to that TextField after the button click. It is default action of Flutter?
List<dynamic> _cityList;
String _selectedCity;
#override
Widget build(BuildContext context) {
return DropdownButton(
value: _selectedCity,
style: TextStyle(
fontSize: 11,
color: textColor,
),
items: _cityList.map((city) {
return DropdownMenuItem<String>(
child: Padding(
padding: const EdgeInsets.only(left: 4),
child: Text(city),
),
);
}).toList(),
onChanged: (String value) {
setState(() {
_selectedCity = value;
print(_selectedCity);
});
},
isExpanded: true,
);
}
Edit: The solution of resetting FocusNode after selecting an item from DropdownMenuItem is adding this line inside of setstate like this:
this: FocusScope.of(context).requestFocus(new FocusNode());
to here: onChanged:(){setSate((){here}}
I hope it will help you. I have modified your code a little bit
List<dynamic> _cityList;
String _selectedCity;
It will show the Dropdown Button and when you click on it and select any value showing in the print
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
body: ListView(
children: [
Column(
children: <Widget>[
DropdownButton<String>(
items: _cityList.map((dynamic value) {
return DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
_selectedCity = value;
print(_selectedCity);
});
},
),
],
),
],
),
);
}
for the focus problem you should use focusNodes one with the drop down list and another with the text field https://docs.flutter.io/flutter/widgets/FocusNode-class.html.

cant fit two dropdownmenubutton in a row

when i try to add two dropDownFormField in a row its showing an exception i don't know why they are not fitting into a row. i tried to wrap it also not working.when add two dropDown buttons in a row it works perfectly.but i want use validator so i am using dropdownformfield
Widget collgDD= Wrap(children:<Widget>[Row(
children:<Widget>[DropdownButtonFormField(
value: selectedcollg,
items: collg.map((value){
return new DropdownMenuItem<String>(child: Text(value),
value: value,);
}).toList(),
hint: Text("select ur collg"),
onChanged: (value){
setState(() {
selectedcollg=value;
});
},
),
DropdownButtonFormField(
value: selectbranch,
items: branch.map((value){
return new DropdownMenuItem<String>(child: Text(value),
value: value,);
}).toList(),
hint: Text("select ur collg"),
onChanged: (value){
setState(() {
selectbranch=value;
});
},
)
])]);
Use flexible widget
Widget collgDD = Row(children: <Widget>[
Flexible(
child: dropdown1
),
Flexible(
child: dropdown2,
)
]);

Resources