Change String value from one stateful widget to another stateful widget - flutter

I have a Stateful widget:
class PractiseInterview extends StatefulWidget {
#override
_PractiseInterviewState createState() => _PractiseInterviewState();
}
class _PractiseInterviewState extends State<PractiseInterview> {
String outputText = '0';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Practise'),
),
body: Container(
child: Column(
children: <Widget>[
Text(
outputText, //**outputText is here**
),
Expanded(
child: Divider(),
),
Column(
children: <Widget>[
Row(
children: <Widget>[
ChnageText(text: 'changed',),
],
),
],
)
],
),
),
);
}
}
NOTICE that my outputText = '0' here.
Then I have a stateless widget for my OutlineButton in stateful widget:
class ChnageText extends StatelessWidget {
const ChnageText({ this.text});
final String text;
buttonPressed(String text) {
if (text == 'changed') {
//TODO: change outputText value to 'changed' at the click of this button
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: OutlineButton(
onPressed: () {
buttonPressed(text);
},
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
text,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}
I have mentioned TODO in my stateless widget, there at the click of the button I want to change outputText = '0' to outputText = 'changed'. I don't know how to do this
I am new to flutter and I am not able to understand this. Basically I am making a calculator where when a button is clicked, then the value of the button should be displayed.

Pass a callback to ChnageText from the parent that changes outputText and calls setState.
class ChnageText extends StatelessWidget {
const ChnageText({this.text, this.callback});
final String text;
final VoidCallback callback;
buttonPressed(String text) {
if (text == 'changed') {
callback();//Call callback here
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: OutlineButton(
onPressed: () {
buttonPressed(text);
},
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
text,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
),
);
}
}
class _PractiseInterviewState extends State<PractiseInterview> {
String outputText = '0';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Practise'),
),
body: Container(
child: Column(
children: <Widget>[
Text(
outputText, //**outputText is here**
),
Expanded(
child: Divider(),
),
Column(
children: <Widget>[
Row(
children: <Widget>[
ChnageText(text: 'changed', callback: () {setState((){outputText = 'changed';});}),// Pass callback here
],
),
],
)
],
),
),
);
}
}
Ideally, you wouldn't have to do this at all. What's in ChnageText doesn't need to be in its own StatelessWidget. Putting all of that directly in the parent removes this problem.
class _PractiseInterviewState extends State<PractiseInterview> {
String outputText = '0';
buttonPressed(String text) {
if (text == 'changed') {
setState(() {
outputText = 'changed';
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Practise'),
),
body: Container(
child: Column(
children: <Widget>[
Text(
outputText, //**outputText is here**
),
Expanded(
child: Divider(),
),
Column(
children: <Widget>[
Row(
children: <Widget>[
//Put ChnageText directly into the build of the parent
Expanded(
child: OutlineButton(
onPressed: () {
buttonPressed('changed');
},
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Text(
'changed',
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
],
)
],
),
),
);
}
}

Related

Flutter nested widgets setState does not work as intended

I have the following structure:
MainBlock. main statefull widget. contains two widgets BlockA and BlockB.
BlockA. contains a text and a button.
BlockB. contains another widget, BlockBCard, which will be used several times (two times in this example).
What works as intended? When I click on the button in BlockA, the content of the text field in BlockA and BlockBCard changers as desired.
Now to my problem:
In BlockB. In order to use setState, I changed BlockB to a StatefulWidget.
Clicking on the button in the BlockBCard, changes the text field in both BlockBCard’s as desired.
But the content of the text field in BlockA does not change.
how can I implement the following:
Click on the button in one of the BlockBCard’s, both the text field in BlockA and the two text fields in BlockBCard change?
Click on the button in one of the BlockBCard’s, the text field in BlockA changes and the text field in the BlockBCard changes but the text field in the second BlockBCard does not change.
Sample Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
int testCounter = 0;
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainBlock(),
);
}
}
// ------------------------------------ Stateless Widget <<<
class MainBlock extends StatefulWidget {
#override
_MainBlockState createState() => _MainBlockState();
}
class _MainBlockState extends State<MainBlock> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF122C39),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: BlockA(
counter: testCounter,
button: () {
setState(() {
testCounter++;
});
},
),
),
Expanded(
child: BlockB(),
),
],
),
),
);
}
}
class BlockA extends StatelessWidget {
final int counter;
final Function button;
BlockA({this.counter, this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
class BlockB extends StatefulWidget {
#override
_BlockBState createState() => _BlockBState();
}
class _BlockBState extends State<BlockB> {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: BlockBCard(
counter: testCounter,
button: () {
setState(() {
testCounter++;
});
},
),
),
Expanded(
child: BlockBCard(
counter: testCounter,
),
),
],
),
);
}
}
class BlockBCard extends StatelessWidget {
final int counter;
final Function button;
BlockBCard({this.counter, this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF4C93C7),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
OK, I have now improved my code so that the button in the BlockBCard's calls a function myBrain that executes a calculation, the result of which is output in BlockA.
Now I would like to increase the text field in the BlockBCard's by 1, but only in the respective card in which the button is pressed and not in all cards. With my current code, all cards are incorrectly increased by 1.
In this example there are only two cards, but in the implementation there will be multiple cards.
Here is my current code. to simplify the example, I removed BlockB and placed the BlockBCard's directly into the MainBlock:
How can I increase the text field in the BlockBCard's by 1, but only in the respective card in which the button is pressed and not in all cards?
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainBlock(),
);
}
}
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO MainBlock >>>
class MainBlock extends StatefulWidget {
#override
_MainBlockState createState() => _MainBlockState();
}
class _MainBlockState extends State<MainBlock> {
int totalCounter = 0;
int singleCounter = 0;
void myBrain() {
totalCounter = totalCounter + 5;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF122C39),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: BlockA(
counter: totalCounter,
button: () {
setState(() {
totalCounter = 0;
});
},
),
),
Expanded(
child: BlockBCard(
counter: singleCounter,
button: () {
setState(() {
myBrain();
singleCounter++;
});
},
),
),
Expanded(
child: BlockBCard(
counter: singleCounter,
button: () {
setState(() {
myBrain();
singleCounter++;
});
},
),
),
],
),
),
);
}
}
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO MainBlock <<<
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockA >>>
class BlockA extends StatelessWidget {
final int counter;
final Function button;
BlockA({this.counter, this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockA <<<
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockBCard >>>
class BlockBCard extends StatelessWidget {
final int counter;
final Function button;
BlockBCard({this.counter, this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF4C93C7),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
// OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO BlockBCard <<<
You got the issue because when you change the value of blockB it could not rebuild ta blockA but it change the global value if you will want to change the blockA's from blockB you should pass as like blockA.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
int testCounter = 0;
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainBlock(),
);
}
}
// ------------------------------------ Stateless Widget <<<
class MainBlock extends StatefulWidget {
#override
_MainBlockState createState() => _MainBlockState();
}
class _MainBlockState extends State<MainBlock> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF122C39),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: BlockA(
counter: testCounter,
button: () {
setState(() {
testCounter++;
});
},
),
),
Expanded(
child: BlockB(
counter: testCounter,
button: () {
setState(() {
testCounter++;
});
}),
),
],
),
),
);
}
}
class BlockA extends StatelessWidget {
final int counter;
final Function button;
BlockA({this.counter, this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
class BlockB extends StatefulWidget {
final int counter;
final Function button;
BlockB({this.counter, this.button});
#override
_BlockBState createState() => _BlockBState();
}
class _BlockBState extends State<BlockB> {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: BlockBCard(
counter: testCounter,
button:widget.button,
),
),
Expanded(
child: BlockBCard(
counter: testCounter,
),
),
],
),
);
}
}
class BlockBCard extends StatelessWidget {
final int counter;
final Function button;
BlockBCard({this.counter, this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF4C93C7),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
As #Jahidul Islam explained, you have to pass your data from MainBlock to BlocB as you do from MainBlock to BlocA.
In general in Flutter, you want to avoid using mutable global variable precisely for that reason. When a variable update, flutter does not know about it. If you call setState the widget tree bellow will be rebuild.
This is why you will often hear "Lift the state up" because the state has to be contained in a high enough widget so that it can be passed down through the widget tree to every widget that needs it. In your case, since BlocA and BlockBCard need the counter, it has to be created inside there lower common ancestor: MainBlock.
Here is the concrete implementation but the most important thing is to have understood the reasoning explained above:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainBlock(),
);
}
}
class MainBlock extends StatefulWidget {
#override
_MainBlockState createState() => _MainBlockState();
}
class _MainBlockState extends State<MainBlock> {
int testCounter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF122C39),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: BlockA(
counter: testCounter,
button: () {
setState(() {
testCounter++;
});
},
),
),
Expanded(
child: BlockB(
counter: testCounter,
button: () {
setState(() {
testCounter++;
});
},
),
),
],
),
),
);
}
}
class BlockA extends StatelessWidget {
final int counter;
final VoidCallback button;
BlockA({required this.counter, required this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button,
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
class BlockB extends StatelessWidget {
final int counter;
final VoidCallback button;
BlockB({required this.counter, required this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: BlockBCard(
counter: counter,
button: button,
),
),
Expanded(
child: BlockBCard(
counter: counter,
),
),
],
),
);
}
}
class BlockBCard extends StatelessWidget {
final int counter;
final VoidCallback? button;
BlockBCard({required this.counter, this.button});
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(30.0),
color: Color(0xFF4C93C7),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Text(
counter.toString(),
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
Center(
child: GestureDetector(
onTap: button ?? (() {}),
child: Text(
'Button',
style: TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 22.0,
),
),
),
),
],
),
);
}
}
If we refresh the MainBlock it's children will also get the updated state. We can use callback for it.
On BlocKB
class BlockB extends StatefulWidget {
final VoidCallback ontap;
const BlockB({
Key? key,
required this.ontap,
}) : super(key: key);
#override
_BlockBState createState() => _BlockBState();
}
class _BlockBState extends State<BlockB> {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(10.0),
color: Color(0xFF265672),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: BlockBCard(
counter: testCounter,
button: () {
// setState(() { /// we dont need it while parent widget will refress it
testCounter++;
// });
widget.ontap();
},
),
),
Expanded(
child: BlockBCard(
counter: testCounter,
button: () {
/// you may also wanted to increment here
testCounter++;
widget.ontap();
},
),
),
],
),
);
}
}
On MainBlock
Expanded(
child: BlockB(
ontap: () {
setState(() {});
},
),
),

Passing stateless widget from one screen to another screen in Flutter

I am trying to create a form builder, In one screen I want have small container with icon and text with gesture detector, when I tap on the container it should contruct a new DatePicker Widget. I know how to do it in the same screen, but onTap I want construct the Datepicker in another screen.
This is Form Component Class.
class FormComponents extends StatefulWidget {
#override
_FormComponentsState createState() => _FormComponentsState();
}
class _FormComponentsState extends State<FormComponents> {
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
int _count = 0;
final formData = FormWidgetData(widget: DatePicker());
#override
Widget build(BuildContext context) {
List<Widget> datePicker = List.generate(_count, (index) => DatePicker());
return Scaffold(
appBar: AppBar(
title: Text('Form Components'),
),
body: Container(
margin: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
FormBuilder(
key: _fbKey,
initialValue: {
'date': DateTime.now(),
'accept_terms': false,
},
autovalidate: true,
child: Column(
children: <Widget>[
Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_count = _count + 1;
});
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Date Picker'),
duration: Duration(seconds: 2),
),
);
},
child: Container(
margin: EdgeInsets.all(16.0),
padding: EdgeInsets.all(8.0),
height: 100.0,
width: 120.0,
color: Colors.black,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Icon(
Icons.date_range,
color: Colors.white,
),
Text(
'Date Picker',
style: TextStyle(
color: Colors.white, fontSize: 18.0),
),
],
),
),
);
},
),
Container(
height: 200.0,
margin: EdgeInsets.all(8.0),
child: Expanded(
child: ListView(
children: datePicker,
),
),
),
],
),
),
],
),
),
);
}
}
This is my Date Picker Class
class DatePicker extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: FormBuilderDateTimePicker(
attribute: 'date',
inputType: InputType.date,
format: DateFormat('yyyy-MM-dd'),
decoration: InputDecoration(labelText: 'AppointmentTime'),
),
);
}
}
This is my Form Builder Class, How to do I Construct the above Date Picker class here.
class MyFormBuilder extends StatefulWidget {
final FormWidgetData data;
MyFormBuilder({this.data});
#override
_MyFormBuilderState createState() => _MyFormBuilderState();
}
class _MyFormBuilderState extends State<MyFormBuilder> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Form Builder'),
),
body: Container(
height: 300.0,
margin: EdgeInsets.all(8.0),
child: Expanded(
child: ListView(
children: <Widget>[],
),
),
),
);
}
}

Listview with Checkbox using StatefulWidget(setState)

I am trying to develop an app in flutter, that has topics that the user can select and check box state will change when i scroll on listview check box state will not collapse and finally user give the submit the value are will bring out.i tried i am not able do that.
the error message shows:
The method 'setState' isn't defined for the class 'ItemDepletionList'.
Try correcting the name to the name of an existing method, or defining a method named 'setState'
class ItemDepletion extends StatefulWidget {
#override
_GetShaftsState createState() => _GetShaftsState();
}
class _GetShaftsState extends State<ItemDepletion> {
ItemDepletionBloc _bloc;
String json =
'{"RECORD_ID": "0", "REQTYPE": "ITEMDEPELTION", "CLINIC_ID": "1012"}';
#override
void initState() {
super.initState();
_bloc = ItemDepletionBloc(json);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
automaticallyImplyLeading: false,
title: Text('Chucky Categories',
style: TextStyle(color: Colors.white, fontSize: 20)),
backgroundColor: Color(0xFF333333),
),
backgroundColor: Color(0xFF333333),
body: RefreshIndicator(
onRefresh: () => _bloc.fetchCategories(json),
child: StreamBuilder<Response<List<Idepeltion>>>(
stream: _bloc.chuckListStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
switch (snapshot.data.status) {
case Status.LOADING:
return Loading(loadingMessage: snapshot.data.message);
break;
case Status.COMPLETED:
return ItemDepletionList(
itemdepletionlst: snapshot.data.data);
break;
case Status.ERROR:
return Error(
errorMessage: snapshot.data.message,
onRetryPressed: () => _bloc.fetchCategories(json),
);
break;
}
}
return Container();
},
),
),
);
}
#override
void dispose() {
_bloc.dispose();
super.dispose();
}
}
class ItemDepletionList extends StatelessWidget {
// final Itemdepeltion categoryList;
final List<Idepeltion> itemdepletionlst;
const ItemDepletionList({Key key, this.itemdepletionlst}) : super(key: key);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new Myappbar(title: new Text("Home Page")),
body: Column(children: [
Expanded(
child: ListView.builder(
itemCount: itemdepletionlst.length,
itemBuilder: (context, index) {
return ListTile(
title: new Container(
child: Row(
children: <Widget>[
new Checkbox(
value: itemdepletionlst[index].isCheck,
onChanged: (bool value) {
setState(() {
itemdepletionlst[index].isCheck = value;
});
}),
new Expanded(
child: new Container(
padding: new EdgeInsets.only(left: 8.0, right: 8.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new Text(
'${itemdepletionlst[index].itemName}',
style: new TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 16.0,
),
),
new Text(
'${itemdepletionlst[index].category}',
style: new TextStyle(color: Colors.grey),
),
],
),
),
),
new Expanded(
child: GestureDetector(
onTap: () {
selectedItem(
context, itemdepletionlst[index].suggQtyUnit);
},
child: new Container(
padding: new EdgeInsets.only(left: 8.0, right: 8.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new Text(
'${itemdepletionlst[index].suggReorderQty} ${itemdepletionlst[index].suggQtyUnit}',
style: new TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 16.0,
),
),
new Text(
'${itemdepletionlst[index].manuf}',
style: new TextStyle(color: Colors.grey),
),
],
),
),
)),
],
)));
},
),
),
RaisedButton(
// onPressed: getCheckboxItems,
textColor: Colors.white,
padding: const EdgeInsets.all(0.0),
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
Color(0xFF09a3c8),
Color(0xFF39B9B4),
Color(0xFF0fb188),
],
),
),
padding: const EdgeInsets.all(10.0),
child: const Text('Submit',
style: TextStyle(fontSize: 20, color: Colors.white)),
),
),
])
);
}
}
Your ItemDepletionList class is stateless and You are trying to call setstate in it because of that you are getting that error. make it Stateful then it will work.
replace Following line.
class ItemDepletionList extends StatelessWidget {
With this
class ItemDepletionList extends StatefulWidget {
final List<Idepeltion> itemdepletionlst;
ItemDepletionList({this.itemdepletionlst});
#override
_ItemDepletionListState createState() => _ItemDepletionListState();
}
class _ItemDepletionListState extends State<ItemDepletionList> {
And now to access itemdepletionlst you have use widget.
widget.itemdepletionlst

Flutter/Dart: update some variables but not others when using setState()

I'm trying to improve my understanding of Flutter, and am struggling with an issue. I am trying to create an app that presents a list of cards which pull data from a list. There is a button on the bottom that creates a new card when the user clicks it.
I am trying to get each card to display a unique 'Round number'. So for example each time the user clicks the button, a card will be added that displays sequential numbers next to the word 'Round'. Round 1 > Round 2 > Round 3 and so on.
Right now everything works except that every time the button is pressed all the cards are updated with the latest number. And so instead of getting a sequential list of cards, every card is updated to the latest round number.
What am I doing wrong? Thank you.
Here is my code:
import 'package:flutter/material.dart';
class Round {
int roundNumber;
int firstNumber;
int secondNumber;
Round({ this.roundNumber, this.firstNumber, this.secondNumber, });
}
int uid = 1;
List<Round> roundsList = [
Round(roundNumber: uid, firstNumber: 1, secondNumber: 2),
];
class createLevels extends StatefulWidget {
#override
_createLevelsState createState() => _createLevelsState();
}
class _createLevelsState extends State<createLevels> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Create Rounds",)
),
body: Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20.0, 20),
child: Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: roundsList.length,
itemBuilder: (BuildContext context, int whatIsThisVariable) {
return roundCard(Round(roundNumber: uid, firstNumber: 2, secondNumber: 3,));
}
),
),
Text("$roundsList"),
RaisedButton(
onPressed: () {
uid++;
roundsList.add(Round(roundNumber: uid));
setState(() {});
},
child: Text("Add Round"),
),
],
),
),
),
);
}
}
class roundCard extends StatelessWidget {
final Round round;
roundCard(this.round);
// roundsList.add(Round(roundNumber: 1));
#override
Widget build(BuildContext context) {
return Container(
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Spacer(
flex: 1,
),
Expanded(
child: Text('Round ${round.roundNumber}:'),
flex: 12,
),
Expanded(
child: TextFormField(
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: '${round.firstNumber}',
),
),
flex: 3,
),
Spacer(
flex: 1,
),
Expanded(
child: TextFormField(
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: '${round.secondNumber}',
),
),
flex: 3,
),
],
),
)
),
);
}
}
You can copy paste run full code below
You need to pass index not uid
You can adjust first and second number you want
code snippet
itemBuilder: (BuildContext context, int index) {
return roundCard(roundsList[index]);
}),
...
onPressed: () {
uid++;
firstNumber++;
secondNumber++;
roundsList.add(
Round(roundNumber: uid, firstNumber: firstNumber, secondNumber: secondNumber));
setState(() {});
}
working demo
full code
import 'package:flutter/material.dart';
class Round {
int roundNumber;
int firstNumber;
int secondNumber;
Round({
this.roundNumber,
this.firstNumber,
this.secondNumber,
});
}
int uid = 1;
int firstNumber = 2;
int secondNumber = 3;
List<Round> roundsList = [
Round(roundNumber: uid, firstNumber: 1, secondNumber: 2),
];
class createLevels extends StatefulWidget {
#override
_createLevelsState createState() => _createLevelsState();
}
class _createLevelsState extends State<createLevels> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
"Create Rounds",
)),
body: Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20.0, 20),
child: Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: roundsList.length,
itemBuilder: (BuildContext context, int index) {
return roundCard(roundsList[index]);
}),
),
Text("$roundsList"),
RaisedButton(
onPressed: () {
uid++;
firstNumber++;
secondNumber++;
roundsList.add(
Round(roundNumber: uid, firstNumber: firstNumber, secondNumber: secondNumber));
setState(() {});
},
child: Text("Add Round"),
),
],
),
),
),
);
}
}
class roundCard extends StatelessWidget {
final Round round;
roundCard(this.round);
// roundsList.add(Round(roundNumber: 1));
#override
Widget build(BuildContext context) {
return Container(
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Spacer(
flex: 1,
),
Expanded(
child: Text('Round ${round.roundNumber}:'),
flex: 12,
),
Expanded(
child: TextFormField(
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: '${round.firstNumber}',
),
),
flex: 3,
),
Spacer(
flex: 1,
),
Expanded(
child: TextFormField(
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: '${round.secondNumber}',
),
),
flex: 3,
),
],
),
)),
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: createLevels(),
);
}
}
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 _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

How to animate hiding AppBar in Flutter?

I need help with animating my AppBar.
My AppBar hides on DoubleTap, but there's no animation in it, it hides immediately. I want it to be animated. I tried to wrap my AppBar with SlideTransition and AnimatedContainer widget, but none of these work, because the error says I need a PreferredSize widget.
I would be extremely glad, if someone helped me!
I already checked out this answer, but the guy, who answered to this question, has the same problem. There's no animation. Show (slide in) or hide (slide out) flutter AppBar on screen tap
Here's the video of my AppBar:
https://streamable.com/it7ib
Here's the photo how my AppBar looks like:
Code:
import 'package:flutter/material.dart';
class GeneratedCouponScreen extends StatefulWidget {
#override
_GeneratedCouponScreenState createState() => _GeneratedCouponScreenState();
}
class _GeneratedCouponScreenState extends State<GeneratedCouponScreen> {
bool showAppBar = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: showAppBar ? AppBar() : null ,
backgroundColor: Colors.white,
body: GestureDetector(
onDoubleTap: () {
if (showAppBar) {
setState(() {
showAppBar = false;
});
}
else {
setState(() {
showAppBar = true;
});
}
},
child: SafeArea(
child: Container(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('DATA WYDANIA:', style: TextStyle(color: Colors.black),),
Text('10/09/2019', style: TextStyle(color: Colors.black))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('UNIKALNY KOD:', style: TextStyle(color: Colors.black)),
Text('e-86-tC-9', style: TextStyle(color: Colors.black))
],
)
],
),
Column(
children: [
SizedBox(height: 8.0),
Image.asset('assets/images/coupon_hamburger.png',)
],
)
],
)
),
)));
}
}
One way of doing it is by using stack and AnimatedBuilder.
class GeneratedCouponScreen extends StatefulWidget {
#override
_GeneratedCouponScreenState createState() => _GeneratedCouponScreenState();
}
class _GeneratedCouponScreenState extends State<GeneratedCouponScreen>
with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) => Stack(
children: <Widget>[
Transform.translate(
offset: Offset(0, -_controller.value * 64),
child: Container(
height: 56.0,
child: AppBar(
title: Text('Title'),
leading: Icon(
Icons.arrow_back,
),
),
),
),
GestureDetector(
onDoubleTap: () {
if (_controller.isCompleted) {
_controller.reverse();
} else {
_controller.forward();
}
},
child: Container(
margin: const EdgeInsets.only(top: 56.0),
padding: EdgeInsets.all(16.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'DATA WYDANIA:',
style: TextStyle(color: Colors.black),
),
Text('10/09/2019',
style: TextStyle(color: Colors.black))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('UNIKALNY KOD:',
style: TextStyle(color: Colors.black)),
Text('e-86-tC-9',
style: TextStyle(color: Colors.black))
],
)
],
),
Column(
children: [
SizedBox(height: 8.0),
Image.network(
'http://via.placeholder.com/640x360',
)
],
)
],
),
),
),
],
),
),
),
);
}
}
First you have to create a new class
class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
num height = 70.0;
#override
_MyAppBarState createState() => _MyAppBarState();
#override
Size get preferredSize => Size.fromHeight(height);
}
class _MyAppBarState extends State<MyAppBar> {
#override
Widget build(BuildContext context) {
return Container(
child: GestureDetector(
onDoubleTap: () {
// Your logic here
// You can do any stuff like animating your this appBar
//e.g: using Opacitiy or Transform and any other Widget depending on what kind of animation you want
// You can also descrease height by calling widget.height
},
child: AppBar(
//Your logic here
),
),
);
}
}
Then call it like this
return Scaffold(
appBar: MyAppBar(),);