Related
in the Widget _button it says:
Undefined name '_selected'.
Try correcting the name to one that is defined, or defining the name.dartundefined_identifier
But I defined at in Widget section please help
or say me what do i wrong
it is the same with _setState
class NavBar extends StatefulWidget {
const NavBar({Key? key}) : super(key: key);
#override
_NavBarState createState() => _NavBarState();
}
class _NavBarState extends State<NavBar> {
final _palette = AppTheme.palette;
int _selected = 0;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: _palette.primaryColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_button(
index: 0,
icon: Icons.home,
selectedIndex: _selected,
),
_button(
index: 1,
icon: Icons.favorite_border_outlined,
selectedIndex: _selected,
),
],
),
);
}
}
on the same page is the Widget _button
Widget _button({
required int index,
required IconData icon,
VoidCallback? onPressed,
int selectedIndex: 0,
}) {
bool isSelected = selectedIndex == index;
return Material(
color: isSelected ? AppTheme.palette.buttonOverlay : Colors.transparent,
borderRadius: BorderRadius.circular(13),
clipBehavior: Clip.antiAlias,
child: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(
icon,
color: isSelected
? AppTheme.palette.secondaryColor
: AppTheme.palette.buttonOverlay,
),
onPressed: () {
_selected = index;
onPressed?.call();
setState(() {});
},
),
);
}
...on the same page is the Widget _button
You need to make sure that _button is within your NavBar class. It should look like this:
class NavBar extends StatefulWidget {
const NavBar({Key? key}) : super(key: key);
#override
_NavBarState createState() => _NavBarState();
}
class _NavBarState extends State<NavBar> {
final _palette = AppTheme.palette;
int _selected = 0;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: _palette.primaryColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_button(
index: 0,
icon: Icons.home,
selectedIndex: _selected,
),
_button(
index: 1,
icon: Icons.favorite_border_outlined,
selectedIndex: _selected,
),
],
),
);
}
Widget _button({
required int index,
required IconData icon,
VoidCallback? onPressed,
int selectedIndex: 0,
}) {
bool isSelected = selectedIndex == index;
return Material(
color: isSelected ? AppTheme.palette.buttonOverlay : Colors.transparent,
borderRadius: BorderRadius.circular(13),
clipBehavior: Clip.antiAlias,
child: IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(
icon,
color: isSelected
? AppTheme.palette.secondaryColor
: AppTheme.palette.buttonOverlay,
),
onPressed: () {
_selected = index;
onPressed?.call();
setState(() {});
},
),
);
}
}
Make sure your _button method is inside the _NavBarState class, otherwise you can't access the global data inside it.
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,
),
)
I am trying to create a responsive chatbot with quick replies. I want to make a button on pressed function call to another class's function. I tried using the callback. But i think i am doing something wrong. Kindly help me.
typedef void mycallback(String label);
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
User? user = FirebaseAuth.instance.currentUser;
UserModel loggedInUser = UserModel();
late DialogFlowtter dialogFlowtter;
final TextEditingController messageController = TextEditingController();
#override
void initState() {
super.initState();
DialogFlowtter.fromFile().then((instance) => dialogFlowtter = instance);
}
#override
Widget build(BuildContext context) {
var themeValue = MediaQuery.of(context).platformBrightness;
Body(
hi: sendMessage,
);
return Scaffold(
backgroundColor: themeValue == Brightness.dark
? HexColor('#262626')
: HexColor('#FFFFFF'),
appBar: AppBar(
//app bar ui
),
actions: [
//list if widget in appbar actions
PopupMenuButton(
icon: Icon(Icons.menu),
color: Colors.blue,
itemBuilder: (context) => [
PopupMenuItem<int>(
value: 0,
child: Text(
"Log out",
style: TextStyle(color: Colors.white),
),
),
],
onSelected: (item) => {logout(context)},
),
],
),
body: SafeArea(
child: Column(
children: [
Expanded(child: Body(messages: messages)),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
child: Row(
children: [
Expanded(
child: TextFormField(
controller: messageController,
style: TextStyle(
color: themeValue == Brightness.dark
? Colors.white
: Colors.black,
fontFamily: 'Poppins'),
decoration: new InputDecoration(
enabledBorder: new OutlineInputBorder(
borderSide: new BorderSide(
color: themeValue == Brightness.dark
? Colors.white
: Colors.black),
borderRadius: BorderRadius.circular(15)),
hintStyle: TextStyle(
color: themeValue == Brightness.dark
? Colors.white54
: Colors.black54,
fontSize: 15,
fontStyle: FontStyle.italic,
),
labelStyle: TextStyle(
color: themeValue == Brightness.dark
? Colors.white
: Colors.black),
hintText: "Type here...",
),
),
),
IconButton(
color: themeValue == Brightness.dark
? Colors.white
: Colors.black,
icon: Icon(Icons.send),
onPressed: () {
sendMessage(messageController.text);
messageController.clear();
},
),
],
),
),
],
),
),
);
}
void sendMessage(String text) async {
if (text.isEmpty) return;
setState(() {
//do main function
});
}
}
The class from where i want to call the function
class Body extends StatelessWidget {
final List<Map<String, dynamic>> messages;
final mycallback? hi;
const Body({
Key? key,
this.messages = const [],
this.buttons = const [],
this.hi,
this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (context, i) {
var obj = messages[messages.length - 1 - i];
Message message = obj['message'];
bool isUserMessage = obj['isUserMessage'] ?? false;
String label = obj['label'];
return Row(
mainAxisAlignment:
isUserMessage ? MainAxisAlignment.end : MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_MessageContainer(
message: message,
isUserMessage: isUserMessage,
),
ElevatedButton(
child: Text(label),
onPressed: () => {hi ?? (label)},//This is where i want to call
style: ElevatedButton.styleFrom(
primary: Colors.blueAccent,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
textStyle: TextStyle(fontWeight: FontWeight.bold)),
),
],
);
},
separatorBuilder: (_, i) => Container(height: 10),
itemCount: messages.length,
reverse: true,
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 20,
),
);
}
}
The code runs without errors but nothing happens when i press the buttons.
This is how I'd implement something like that. You're basically asking for a void as parameter inside your widget. Almost like a TextButton or another widget like that.
You can use this with two stateful widets as well, since you're borrowing the function from one to another.
Also I think this would be done better with provider so I suggest you look into it. (I don't have enough experience with it)
https://pub.dev/packages/provider
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int x = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('An app'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('$x'),
TestWidget(onTap: () {
setState(() {
x++;
});
})
],
),
),
);
}
}
class TestWidget extends StatelessWidget {
final VoidCallback onTap;
const TestWidget({Key? key, required this.onTap}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(20),
color: Colors.blue,
child: Text('test')),
);
}
}
I found the error.
In the class HomeScreen, I missed this line.
child: Body(
messages: messages,
hi: (text) => {sendMessage(text)}, //this line
)
After adding this line, the callback worked fine!
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 can disable the tooltip statically.
But I want to disable tooltip dynamically when i click flatbutton.But Couldnt disable dynamically and i have no idea to do that.
This is my code:
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(home: HelloWorld(),debugShowCheckedModeBanner: false,));
}
class HelloWorld extends StatefulWidget {
#override
_HelloWorldState createState() => _HelloWorldState();
}
class _HelloWorldState extends State<HelloWorld> {
bool check = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
TopToolbar toolbar = new TopToolbar();
toolbar.showTooltip = false;
});
},
),
]),
),
));
}
}
class TopToolbar extends StatefulWidget {
bool showTooltip;
final Color backgroundColor;
final double height;
bool isVisible;
TopToolbar({
this.height = 55,
this.isVisible = true,
this.backgroundColor = const Color(0xFFEEEEEE),
Key key,this.showTooltip=true,
}) : super(key: key);
#override
_TopToolbarState createState() => _TopToolbarState();
}
class _TopToolbarState extends State<TopToolbar> {
#override
Widget build(BuildContext context) {
if (widget.isVisible) {
return Container(
foregroundDecoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey,
),
),
),
margin: EdgeInsets.only(bottom: 1),
color: widget.backgroundColor,
height: widget.height,
child: Stack(
children: <Widget>[
Positioned(
top: 7,
right: 60,
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0.0,
fillColor: widget.backgroundColor,
splashColor: Colors.grey[300],
child: IconButton(
icon: Icon(
Icons.bookmark,
color: Colors.grey[500],
size: 25,
),
onPressed: (){},
tooltip: widget.showTooltip ? "Bookmark" : null,
),
onPressed: (){},
),
),
],
),
);
} else {
return Container();
}
}
}
If I give statically false. it works fine.
For example : If add child like TopToolbar(showTooltip : false),it works fine,
But If i give toolbar.showTooltip = false in Flatbutton onPressed method,it doesnt work.
I want to disble it in dynamically. please help me to do that.
we can hide or deactivate tooltip programmatically like below,
Future.delayed(
Duration(seconds: 2),
() {
tooltip?.deactivate();
}
);
Here, we can set time according to your requirement.(Currently, we are set 2 sec.)
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: HelloWorld(),
debugShowCheckedModeBanner: false,
));
}
class HelloWorld extends StatefulWidget {
#override
_HelloWorldState createState() => _HelloWorldState();
}
class _HelloWorldState extends State<HelloWorld> {
bool check = false;
bool showTooltip = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: <Widget>[
TopToolbar(showTooltip: showTooltip),
FlatButton(
child: Text("Disable Tooltip"),
onPressed: () {
setState(() {
showTooltip = false;
});
},
),
]),
),
));
}
}
class TopToolbar extends StatefulWidget {
final bool showTooltip;
final Color backgroundColor;
final double height;
final bool isVisible;
TopToolbar({
this.height = 55,
this.isVisible = true,
this.backgroundColor = const Color(0xFFEEEEEE),
Key key,
this.showTooltip = true,
}) : super(key: key);
#override
_TopToolbarState createState() => _TopToolbarState();
}
class _TopToolbarState extends State<TopToolbar> {
#override
Widget build(BuildContext context) {
if (widget.isVisible) {
return Container(
foregroundDecoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey,
),
),
),
margin: EdgeInsets.only(bottom: 1),
color: widget.backgroundColor,
height: widget.height,
child: Stack(
children: <Widget>[
Positioned(
top: 7,
right: 60,
height: 40,
width: 40,
child: RawMaterialButton(
elevation: 0.0,
fillColor: widget.backgroundColor,
splashColor: Colors.grey[300],
child: IconButton(
icon: Icon(
Icons.bookmark,
color: Colors.grey[500],
size: 25,
),
onPressed: () {},
tooltip: widget.showTooltip ? 'Bookmark' : null,
),
onPressed: () {},
),
),
],
),
);
} else {
return Container();
}
}
}
I've used this method to hide tooltips:
Tooltip(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0),
),
textStyle: TextStyle(color: Colors.white.withOpacity(0)),
message: 'Certificates',
child: Container()
);
Make the property message='' // empty string
setState((){messageText=''});
Tooltip(
message: messageText,
...
)