Problem with commiting a String to a Stateful Widget - flutter

I want to create a ModalBottomSheet, which opens a lot of RaisedButtons. These buttons should change there Color after pressing.
Therefore I created a StatefulWidget named "Buttons".
In my MainClass i use
for (String item in myList) Buttons(item)
to commit the Strings of myList to Buttons.
But i have 2 Problems in Buttons:
String item should be final, but then i can't use it as a setter
(I want to get rid of the blue underlining of Buttons, which tell me: This class (or a class that this class inherits from) is marked as '#immutable', but one or more of its instance fields aren't final: )
The other problem is, that in my buildMethod "item" isn't defined.
Why can't I use String item in my build Method?
class Buttons extends StatefulWidget {
String item;
Buttons(String item) {
this.item = item;
}
#override
_ButtonsState createState() => _ButtonsState();
}
class _ButtonsState extends State<Buttons> {
bool pressAttention = false;
#override
Widget build(BuildContext context) {
return Container(
child: Card(
child: RaisedButton(
child: Text(item),
color: pressAttention ? Colors.grey : Colors.blue,
onPressed: () => setState(() => pressAttention = !pressAttention),
),
),
);
}
}

Replace Text(item) by Text(widget.item)
class Buttons extends StatefulWidget {
final String item;
Buttons(this.item);
#override
_ButtonsState createState() => _ButtonsState();
}
class _ButtonsState extends State<Buttons> {
bool pressAttention = false;
#override
Widget build(BuildContext context) {
return Container(
child: Card(
child: RaisedButton(
child: Text(widget.item),
color: pressAttention ? Colors.grey : Colors.blue,
onPressed: () => setState(() => pressAttention = !pressAttention),
),
),
);
}
}

Related

how to update a parent widget when a variable is changed by a child widget

I have a Stateful Parent Widget with a counter variable that is displayed in the app.
class ParentWidget extends StatefulWidget {
const ParentWidget({Key? key}) : super(key: key);
#override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
SizedBox(
height: 100,
),
Text(
'$counter',
style: TextStyle(fontSize: 20, color: kWhite),
),
Button(
isPressed: (isPressed) {
isPressed ? counter++ : counter--;
print(counter);
},
),
],
),
),
);
}
}
I then have a Stateful child widget called Button which has a toggle switch. this bool is then passed up to the parent widget and increases/decreases the counter variable.
class Button extends StatefulWidget {
final ValueChanged<bool> isPressed;
const Button({Key? key, required this.isPressed}) : super(key: key);
#override
State<Button> createState() => _ButtonState();
}
class _ButtonState extends State<Button> {
bool buttonPressed = false;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
buttonPressed = !buttonPressed;
});
widget.isPressed(buttonPressed);
},
child: Container(
color: kWhite,
height: 50,
width: 50,
),
);
}
}
the problem I'm having is the displayed counter isn't updating when i toggle the button. the counter is definitely updating because I can see in the print statement. its just not updating what's on screen.
so how do i get it to update on the screen?
the reason I don't just build the button in the parent app is because I have lots of them in my app and it would mean lots of bools and setState methods for each of them in the parent widget.
thanks so much and any help would be greatly appreciated
You can use ValueListenableBuilder to rebuild Text widget.Put a varible in globals.dart and change its value on button tap.
ValueListenableBuilder(
valueListenable:rebuild,
builder: ((context,value,child) {
return Text(
'$counter',
style: TextStyle(fontSize: 20,color:
kWhite),
);
})),
in globals.dart paste this variable
ValueNotifier rebuild = ValueNotifier(0);
put this code in button onTap
onTap: () {
setState(() {
buttonPressed = !buttonPressed;
});
widget.isPressed(buttonPressed);
rebuild.value == 0 ? rebuild.value = 1 : rebuild.value = 0;
},

How to access attributes of the widgets children

I am opening a new activity with the startTimer method How do I set the cyclesVal variable to an attribute of my custom NumberSelector widget so I can pass it to the next activity. Any way to access an internal variable of the NumberSelector widget would work too since I have set the attribute to the variable.
class ExcersizeInput extends StatelessWidget {
const ExcersizeInput({super.key});
void startTimer(BuildContext context) {
int cyclesVal = 1;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TimerPage(
cycleCount: cyclesVal,
),
));
}
#override
Widget build(BuildContext context) {
return Column(children: [
Column(
children: [
NumberSelector(
title: "Cycles",
),
],
),
ElevatedButton.icon(
onPressed: (() => {startTimer(context)}),
label: const Text("Start"),
icon: const Icon(Icons.start_outlined),
)
]);
}
}
Here's the NumberSelector code:
class NumberSelector extends StatefulWidget {
final String title;
const NumberSelector(
{super.key, required this.title});
#override
State<NumberSelector> createState() => _NumberSelectorState();
}
class _NumberSelectorState extends State<NumberSelector> {
int selectorValue = 1;
void updateValue(ifAdd) {
setState(() {
if (ifAdd) {
if (selectorValue < 9999) {
selectorValue++;
}
} else {
if (selectorValue > 1) {
selectorValue--;
}
}
});
}
#override
Widget build(BuildContext context) {
final ColorScheme colors = Theme.of(context).colorScheme;
return Column(
children: [
Text(widget.title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
Row(
children: [
GestureDetector(
child: IconButton(
onPressed: () => updateValue(false),
icon: const Icon(Icons.remove),
style: styleContainedButton),
},
),
Text(selectorValue.toString()),
style: const TextStyle(fontSize: 16)),
GestureDetector(
child: IconButton(
onPressed: () => updateValue(true),
icon: const Icon(Icons.add),
style: styleContainedButton),
},
),
],
),
],
);
}
}
To get a variable from a stateful widget in flutter it needs a key that is linked to the state of the widget. Via the key, the variables can be accessed.
Define a widget you want to access from its parent and that contains any value:
class TestWidget extends StatefulWidget {
TestWidget({Key? key}) : super(key: key);
#override
State<TestWidget> createState() => TestWidgetState();
}
class TestWidgetState extends State<TestWidget> {
int? anyvalue;
#override
Widget build(BuildContext context) {
return Container();
}
}
Declare the key in the parent widget. Make sure the name of the state does not start with an underscore:
GlobalKey<TestWidgetState> _widget_key = GlobalKey();
Give the key to the widget in the build method of the parent widget:
TestWidget(
key: _widget_key,
)
Now the value of the child widget can be accessed in the parent widget via the key:
void afunction() {
print(_widget_key.currentState!.anyvalue);
}

Can we change a widgets variable with InkWell onTap function?

I have a custom written stateful widget that wrapped with InkWell and I want to change the widgets variable when onTap function gets activated. Is there any way to achieve that?
Here is my custom written widget
import 'package:flutter/material.dart';
class DrawerListTile extends StatefulWidget {
final tileIcon;
final tileText;
bool isSelected = false;
DrawerListTile({this.tileIcon, this.tileText});
#override
State<DrawerListTile> createState() => _DrawerListTileState();
}
class _DrawerListTileState extends State<DrawerListTile> {
#override
Widget build(BuildContext context) {
return ListTile(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
selected: widget.isSelected,
selectedTileColor: Colors.black12,
selectedColor: Colors.black54,
leading: Icon(widget.tileIcon),
title: Text(widget.tileText),
);
}
}
And here is my InkWell widget
InkWell(
onTap: () => setState(() {
//Here is the part that I want to change the DrawerListTile's isSelected value
}),
child: DrawerListTile(
tileText: "Some Text", tileIcon: Icons.credit_card_rounded),
),
I know that I can write the onTap function inside the DrawerListTile but it is not useful in my situation so is there any way to achieve what I want?
You can do something like the below solution ... you can use your isSelected variable for this purpose.
The parent view:
class MainView extends StatefulWidget {
const MainView({Key? key}) : super(key: key);
#override
State<MainView> createState() => _MainViewState();
}
class _MainViewState extends State<MainView> {
String text = DateTime.now().toString();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('App'),
),
body: InkWell(
child: Center(child: TargetWidget(text: text)),
onTap: () {
setState(() {
text = DateTime.now().toString();
});
},
),
);
}
}
The child view:
class TargetWidget extends StatefulWidget {
String text;
TargetWidget({Key? key, required this.text}) : super(key: key);
#override
State<TargetWidget> createState() => _TargetWidgetState();
}
class _TargetWidgetState extends State<TargetWidget> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(widget.text),
);
}
}
You should pass your variable as a parameter to your DrawerListTile(),You can create a model class that will hold all the variables you need and pass them to the widget. Thus, whenever you call the setState function, new parameters are sent to the widget and the widget is updated.
Ex:
InkWell(
onTap: () => setState(() {
//Here is the part that I want to change the DrawerListTile's isSelected value
yourFirstVariable = something;
yourSecondVariable = something;
}),
child: DrawerListTile(
tileText: "Some Text",
tileIcon: Icons.credit_card_rounded,
drawerVariables: DrawerModel(
demoVar1 = yourFirstVariable,
demoVar2 = yourSecondVariable...
),
),
),
class DrawerModel {
final var demoVar1;
final var demoVar2;
DrawerModel ({required this.demoVar1, required this.demoVar1,});
}
class DrawerListTile extends StatefulWidget {
final tileIcon;
final tileText;
final DrawerModel drawerVariables;
bool isSelected = false;
DrawerListTile({this.tileIcon, this.tileText, this.drawerVariables});
#override
State<DrawerListTile> createState() => _DrawerListTileState();
}
class _DrawerListTileState extends State<DrawerListTile> {
#override
Widget build(BuildContext context) {
return ListTile(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
selected: widget.isSelected,
selectedTileColor: Colors.black12,
selectedColor: Colors.black54,
leading: Icon(widget.tileIcon),
title: Text(widget.tileText),
);
}
}

how to change "text" to "text field" when clicking a button in Flutter

There is a regular "Text" and it changes to "TextField" when I click the button I would like to know how to do this
IconButton(
icon: Icon(
Icons.edit,
color: Color(0xFF8D8D8D),
),
onPressed: null),
try this
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool isTextFild = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
isTextFild ? TextField() : Text('some text'),
FlatButton(
child: Text('Show Text Field'),
onPressed: () {
setState(() {
isTextFild = true;
});
},
)
],
),
),
);
}
}
This should be enough, a TextFormField that toggles between readOnly and not. This way, you'll ensure that your view doesn't "jump" by switching widgets and you'll be always using the same object, only toggling properties.
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool _isEditing = false;
void _edit() {
setState(() => _isEditing = true);
}
#override
Widget build(BuildContext context) {
return TextFormField(
readOnly: _isEditing,
);
}
}
You can do something like this:
class Sample extends StatefulWidget {
#override
_SampleState createState() => _SampleState();
}
class _SampleState extends State<Sample> {
bool _editMode = false;
#override
Widget build(BuildContext context) {
return Row(children: <Widget>[
_editMode ? TextField(...) : Text(...),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
_editMode = !_editMode;
});
})
]);
}
}
Note: If you want to set an initial value for textfield you should use textformfield instead.

statfulWidget with key concept

i am studying key in flutter. and in explanation, when i want swap widget in statefulWidget i need to add key value. because when flutter check element structure if type, state are not same they don't response. this is how i understand.
void main() => runApp(new MaterialApp(home: PositionedTiles()));
class PositionedTiles extends StatefulWidget {
#override
State<StatefulWidget> createState() => PositionedTilesState();
}
class PositionedTilesState extends State<PositionedTiles> {
List<Widget> tiles = [
StatefulColorfulTile(key: UniqueKey()), // Keys added here
StatefulColorfulTile(key: UniqueKey()),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(children: tiles),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
);
}
swapTiles() {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
}
}
class StatefulColorfulTile extends StatefulWidget {
StatefulColorfulTile({Key key}) : super(key: key); // NEW CONSTRUCTOR
#override
ColorfulTileState createState() => ColorfulTileState();
}
class ColorfulTileState extends State<ColorfulTile> {
Color myColor;
#override
void initState() {
super.initState();
myColor = UniqueColorGenerator.getColor();
}
#override
Widget build(BuildContext context) {
return Container(
color: myColor,
child: Padding(
padding: EdgeInsets.all(70.0),
));
}
}
but i saw this code.
Widget build(BuildContext context) {
return Column(
children: [
value
? const SizedBox()
: const Placeholder(),
GestureDetector(
onTap: () {
setState(() {
value = !value;
});
},
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
!value
? const SizedBox()
: const Placeholder(),
],
);
}
this code is also use statefulWidget. in this code when user taps Box it's changed but i think there're no key value and in element structure there are different type(one is SizedBox and the other is placeHolder) so i think there aren't changed. why they're changed? what i misunderstand?