I have six buttons on the screen and they all do the same function. But I want to control the colors of these buttons to be clicked. If the button is clicked, the button color should be green (I'm doing this buttonColorDisable.) Everything is normal so far, but in _buttonFunction() widget.callbackColor(); When I call it, I expect all button colors to change again, but only the last button is affected. Other buttons still remain green. how do i solve this.
class BuildNumButton extends StatefulWidget {
final int number;
final Color color;
final Color buttonColorDisable;
final Function callbackColor;
final Function callbackList;
final Function callbackScore;
final Function callbackTarget;
const BuildNumButton({
Key? key,
required this.number,
required this.callbackScore,
required this.callbackList,
required this.callbackTarget,
required this.callbackColor,
required this.color,
required this.buttonColorDisable,
}) : super(key: key);
#override
State<BuildNumButton> createState() => _BuildNumButtonState();
}
class _BuildNumButtonState extends State<BuildNumButton> {
bool isButtonVisible = false;
void _buttonFunction() {
isButtonVisible = true;
CalculateScore.sumNumbers(widget.number);
CalculateScore.calculateScore();
widget.callbackScore();
if (CalculateScore.answer == true) {
if (!CalculateScore.endGame) {
widget.callbackList();
widget.callbackColor();
isButtonVisible = false;
}
widget.callbackTarget();
}
}
#override
Widget build(BuildContext context) {
return SizedBox(
width: 150,
height: 120,
child: TextButton(
style: ButtonStyle(
backgroundColor: isButtonVisible
? MaterialStateProperty.all(
widget.buttonColorDisable) //button color green
: MaterialStateProperty.all(widget.color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: Colors.white, width: 3),
),
),
),
onPressed: isButtonVisible ? null : _buttonFunction,
child: Text(
widget.number.toString(),
style: numButtonTextStyle,
),
),
);
}
}
I will prefer creating List<int> to hold tapped index and use BuildNumButton extends StatelessWidget.
Run on dartPad.
class BuildNumButton extends StatelessWidget {
final int number;
final Color color;
final Color buttonColorDisable;
final VoidCallback callback;
final bool isDisable;
const BuildNumButton({
Key? key,
required this.number,
required this.color,
required this.buttonColorDisable,
required this.callback,
required this.isDisable,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return SizedBox(
width: 150,
height: 120,
child: TextButton(
style: ButtonStyle(
backgroundColor: isDisable
? MaterialStateProperty.all(
buttonColorDisable) //button color green
: MaterialStateProperty.all(color),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: Colors.white, width: 3),
),
),
),
onPressed: isDisable ? null : callback,
child: Text(
number.toString(),
),
),
);
}
}
and VoidCallback used to get tapEvent and based on condition update the state.
List<int> disableButtons = [];
.....
...List.generate(
6,
(index) => BuildNumButton(
buttonColorDisable: Colors.green,
isDisable: disableButtons.contains(index),
callback: () {
disableButtons.add(index);
if (disableButtons.length == 6) disableButtons.clear();
setState(() {});
},
color: Colors.cyanAccent,
number: index,
),
)
Related
I have created a class containing a single button with certain parameters. In the future I want to make an array of buttons containing random parameters
class _ButtonWidget extends StatelessWidget {
_ButtonWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 200,
height: 200,
child: ElevatedButton
(onPressed: (() {
}),
child: Text('PRESS',
style: TextStyle(color: Colors.white),),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.black),
overlayColor: MaterialStateProperty.all(Colors.green),
shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder(
borderRadius: BorderRadius.circular(300.0),
side: BorderSide(color: Colors.blue, width: 3),
),
),
),
),
),
);
}
}
I also have a list in which I want to set parameters such as color and radius randomly in the future.
class StyleButton {
final backgroundColor;
final overlayColor;
final int borderRadius;
final borderSideColor;
StyleButton({
required this.backgroundColor, required this.overlayColor, required this.borderRadius, required this.borderSideColor,
});
}
class StyleButtonWidget extends StatefulWidget {
StyleButtonWidget({Key? key}) : super(key: key);
#override
State<StyleButtonWidget> createState() => _StyleButtonWidgetState();
}
class _StyleButtonWidgetState extends State<StyleButtonWidget> {
final _movies = [
StyleButton(
backgroundColor: Colors.black,
overlayColor: Colors.green,
borderRadius: 300,
borderSideColor: Colors.blue,
),
];
#override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
}
How do I pass variables from my list
final _movies = [
StyleButton(
backgroundColor: Colors.black,
overlayColor: Colors.green,
borderRadius: 300,
borderSideColor: Colors.blue,
),
in the button parameters ?
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.black),
overlayColor: MaterialStateProperty.all(Colors.green),
shape: MaterialStateProperty.all<RoundedRectangleBorder>( RoundedRectangleBorder(
borderRadius: BorderRadius.circular(300.0),
side: BorderSide(color: Colors.blue, width: 3),
),
Try like this.
Alter your ButtonWidget to accept a StyleButtonParam.
class ButtonWidget extends StatelessWidget {
const ButtonWidget({Key? key, required this.buttonStyle}) : super(key: key);
final StyleButton buttonStyle;
#override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 200,
height: 200,
child: ElevatedButton(
onPressed: (() {}),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(buttonStyle.backgroundColor),
overlayColor: MaterialStateProperty.all(buttonStyle.overlayColor),
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(buttonStyle.borderRadius.toDouble()),
side: BorderSide(color: buttonStyle.borderSideColor, width: 3),
),
),
),
child: const Text(
'PRESS',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}
Access the Button widget like this.
class StyleButtonWidget extends StatefulWidget {
const StyleButtonWidget({Key? key}) : super(key: key);
#override
State<StyleButtonWidget> createState() => _StyleButtonWidgetState();
}
class _StyleButtonWidgetState extends State<StyleButtonWidget> {
List<Widget> buttons = [];
final List<StyleButton> _movies = [
StyleButton(
backgroundColor: Colors.black,
overlayColor: Colors.green,
borderRadius: 300,
borderSideColor: Colors.blue,
),
];
buildButton() {
_movies.forEach((element) {
buttons.add(ButtonWidget(buttonStyle: element));
});
setState((){});
}
#override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
}
Note: Add-on change method for the button may be by wrapping with Gesture detector and function parameter. And add it while accessing the widget.
final _movies = [
StyleButton(
backgroundColor: Colors.black,
overlayColor: Colors.green,
borderRadius: 300,
borderSideColor: Colors.blue,
),
From the following list, you can access any variable with index
_movies[0].backgroundColor
If you want to make it dynamic, use a for-loop
for(var i = 0; i < _movies > length; i++)
_movies[i].backgroundColor;
I have the CupertinoContextMenu setup and it works well. However, I have an Icon in a separate widget which when tapped (single tap) would also trigger the opening of the Context Menu.
Is anything like this possible?
I hacked something a little remotly iosy together from the material PopupMenuButton. Maybe it can help you as a basis for styling.
import 'package:flutter/material.dart';
class IosLikePopupMenuButton extends StatelessWidget {
final double borderRadius;
final Color _backgroundColor = const Color.fromARGB(209, 235, 235, 235);
final List<IosLikePopupMenuItem> Function(BuildContext) itemBuilder;
const IosLikePopupMenuButton({
Key? key,
this.borderRadius = 24.0,
required this.itemBuilder,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return PopupMenuButton<String>(
icon: const Icon(Icons.more_horiz),
itemBuilder: (context1) {
/// give the current context and map the IosLikePopupMenuItem dtos to PopupMenuEntry while splicing in dividers
return itemBuilder(context1)
.map((e) => e.popupMenuItems())
.toList()
.fold(
List<PopupMenuEntry<String>>.empty(growable: true),
(p, e) => [
...p,
...[e, const PopupMenuDivider()]
])
/// delete the last divider
..removeLast();
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(borderRadius),
),
),
color: _backgroundColor,
);
}
}
class IosLikePopupMenuItem {
final double menuItemHeight, minSpaceBtwnTxtAndIcon;
final String lableText;
final IconData icon;
final void Function() onTap;
final TextStyle _textStyle =
const TextStyle(color: Colors.black, fontSize: 22.0);
IosLikePopupMenuItem({
this.menuItemHeight = 24.0,
this.minSpaceBtwnTxtAndIcon = 48.0,
required this.lableText,
required this.icon,
required this.onTap,
});
PopupMenuItem<String> popupMenuItems() => PopupMenuItem<String>(
height: menuItemHeight,
textStyle: _textStyle,
onTap: onTap,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
lableText,
),
SizedBox(width: minSpaceBtwnTxtAndIcon),
Icon(icon),
],
),
);
}
How can I create round checkboxes with text inside ?
I want it like on the picture.
You can make a button that on pressed toggles a bool and based on if bool is true or false you can make the border be transparent or not.
This may not be the best solution but it should work
class CustomCheckbox extends StatefulWidget {
#override
State<CustomCheckbox> createState() => _CustomCheckboxState();
}
class _CustomCheckboxState extends State<CustomCheckbox> {
bool isChecked = false;
#override
Widget build(BuildContext context) {
return RawMaterialButton(
onPressed: () {
setState(() => isChecked = !isChecked);
},
splashColor: Colors.transparent,
child: Text(
'AS',
style: TextStyle(
color: isChecked ? Colors.white : Colors.grey,
fontSize: 20
)
),
padding: EdgeInsets.all(13.0),
shape: CircleBorder(
side: BorderSide(
color: isChecked ? Colors.yellowAccent : Colors.transparent
)
),
);
}
}
try using ClipOval in a row children
ClipOval(
child:
Container(
color: yourColor
height: 10.0,
width: 10.0,
))
class Checkbox extends StatefulWidget {
final String value;
final bool check;
const Checkbox({
Key? key, required this.value, required this.check,
}) : super(key: key);
#override
State<Checkbox> createState() => _CheckboxState();
}
class _CheckboxState extends State<Checkbox> {
late bool check;
#override
void initState() {
check = widget.check;
super.initState();
}
#override
Widget build(BuildContext context) {
return InkWell(
onTap: (){
setState(()=>check = !check);
},
child: Container(
padding: EdgeInsets.all(6),
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: check ? Colors.yellow : Colors.transparent,
width: 2),
),
child: Text(widget.value, style: TextStyle(color: check ? Colors.white
: Colors.grey)),
),
);
}
}
I've seen variations of this question but haven't been able to quite piece together a solution for my use case.
I have a list of CircleButton.
List<CircleButton> buttons = [
new CircleButton(onTap: () => print("Arts"), iconData: Icons.palette, label: "Arts"),
new CircleButton(onTap: () => print("Board Games"), iconData: Icons.casino, label: "Board Games"),
new CircleButton(onTap: () => print("Causes"), iconData: Icons.volunteer_activism, label: "Causes"),
];
I display these in a GridView
Widget interests() {
return Expanded(
child:
SizedBox(
height: 200.0,
child:
GridView.count(
primary: false,
padding: const EdgeInsets.all(20),
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 4,
children: <Widget>[
for (var button in buttons) Column( children: [ button, Text(button.label)],)
],
)
)
);
}
When the button is tapped I want to update how it looks. I have set up the CircleButton class to have the boolean value isSelected to determine how the coloring should be:
class CircleButton extends StatelessWidget {
final GestureTapCallback onTap;
final IconData iconData;
final String label;
bool isSelected = false;
CircleButton({Key key, this.onTap, this.iconData, this.label}) : super(key: key);
#override
Widget build(BuildContext context) {
double size = 65.0;
return new
InkResponse(
onTap: onTap,
child: new Container(
width: size,
height: size,
decoration: new BoxDecoration(
color: isSelected ? Color.fromRGBO(139, 207, 236, 1.0) : Color.fromRGBO(248, 248, 250, 1.0),
shape: BoxShape.circle,
),
child: new Icon(
iconData,
color: isSelected ? Colors.white : Color.fromRGBO(2, 78, 157, 1.0),
),
),
);
}
}
How can I update the isSelected variable from onTap?
If you want each button to manage its own selected state (meaning that tapping on a button will not unselect the others), you must use StatefulWidget :
class CircleButton extends StatefulWidget {
final GestureTapCallback onTap;
final IconData iconData;
final String label;
CircleButton({Key key, this.onTap, this.iconData, this.label})
: super(key: key);
#override
_CircleButtonState createState() => _CircleButtonState();
}
class _CircleButtonState extends State<CircleButton> {
bool isSelected = false;
#override
Widget build(BuildContext context) {
double size = 65.0;
return new InkResponse(
onTap: () {
widget.onTap;
// ADD THESE LINES
setState(() {
isSelected = !isSelected;
});
},
child: new Container(
width: size,
height: size,
decoration: new BoxDecoration(
color: isSelected
? Color.fromRGBO(139, 207, 236, 1.0)
: Color.fromRGBO(248, 248, 250, 1.0),
shape: BoxShape.circle,
),
child: new Icon(
widget.iconData,
color: isSelected ? Colors.white : Color.fromRGBO(2, 78, 157, 1.0),
),
),
);
}
}
Otherwise, if you want only one button to be selected at a time, juste create a variable in the parent widget that stores which button is selected :
int selectedButtonIndex;
List<CircleButton> buttons = [
new CircleButton(onTap: () {
print("Arts");
setState((){ selectedButtonIndex = selectedButtonIndex != 0 ? 0 : null; });
}, iconData: Icons.palette, label: "Arts",
isSelected: selectedButtonIndex == 0,
),
new CircleButton(onTap: () {
print("Board Games");
setState((){ selectedButtonIndex = selectedButtonIndex != 1 ? 1 : null; });
}, iconData: casino, label: "Board Games",
isSelected: selectedButtonIndex == 1,
),
...
];
And add isSelected as a parameter for CircleButton
I'm using this simple CircularButtonWithIcon. The one problem is that it won't respect transparency when no color is setted.
class CircularButtonWithIcon extends StatefulWidget {
CircularButtonWithIcon({
Key key,
#required this.onPress,
this.padding,
this.border,
this.active = false,
this.marginRight,
this.marginLeft,
this.icon,
this.color,
this.activeColor,
this.text,
this.splashColor,
this.image,
this.hasState,
this.borderColor,
}) : super(key: key);
final double padding;
final double border;
final double marginRight;
final double marginLeft;
final Icon icon;
final Image image;
final Color color;
final Color activeColor;
final Color borderColor;
final Widget text;
final Color splashColor;
final Function onPress;
final bool active;
final bool hasState;
#override
_CircularButtonWithIconState createState() => _CircularButtonWithIconState();
}
class _CircularButtonWithIconState extends State<CircularButtonWithIcon> {
bool active;
#override
void initState() {
super.initState();
active = widget.active;
}
#override
Widget build(BuildContext context) {
return FittedBox(
child: Container(
margin: EdgeInsets.only(left: widget.marginLeft ?? 0, right: widget.marginRight ?? 0),
child: RawMaterialButton(
onPressed: () {
setState(() {
active = !active; // don't use widget.active
});
if (widget.onPress != null) widget.onPress();
},
fillColor: active
? widget.activeColor ?? widget.color ?? Colors.transparent
: widget.color ?? Colors.transparent,
splashColor: widget.splashColor ?? Colors.grey,
child: widget.text != null
? Container(
child: widget.text,
)
: widget.image ?? widget.icon,
//padding: EdgeInsets.all(15.0),
padding: widget.padding != null ? EdgeInsets.all(widget.padding) : EdgeInsets.all(15.0),
shape: CircleBorder(
side: BorderSide(
width: widget.border ?? 2.0,
style: BorderStyle.solid,
color: widget.borderColor ?? Colors.grey),
),
),
),
);
}
}
Take a look at:
fillColor: active
? widget.activeColor ?? widget.color ?? Colors.transparent
: widget.color ?? Colors.transparent,
When no color is seted, it should be transparent.
I did this:
CircularButtonWithIcon(
padding: 10,
border: 3,
onPress: () => {},
icon: Icon(Icons.remove,
color: Color.green,
size: 60))
and it's getting a color background instead of merging:
however it looks like it's just the background but darker. Is it possible that the button is adding a shadow on top of the background color? How can I remove it? I looked at https://api.flutter.dev/flutter/material/RawMaterialButton-class.html and couldn't find anything related to shadow effects on the button