Flutter nested widgets setState does not work as intended - flutter

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(() {});
},
),
),

Related

Flutter counter not counting up

I have made this scorekeeper app for basketball. I need to count the score and show it on the application. But its stuck on 0. It probably has something to do with the setState().
If possible don't change the code too much since I need to show and explain this to my teacher.
inputPage:
class InputPage extends StatefulWidget {
#override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('basketball counter'),
),
body: Column(
children: <Widget>[
Expanded(
child: Row(children: <Widget>[Expanded(child: HoopDesign()), Text(Counter.counter.toString())] ),
),
Expanded(
child: Row(
children: <Widget>[
Expanded(
child: CardDesign1(),
),
Expanded(
child: CardDesign3(),
),
Expanded(
child: CardDesign2(),
),
],
)),
Expanded(
child: Row(
children: <Widget>[
Expanded(
child: CardDesign6(),
),
Expanded(
child: CardDesign4(),
),
Expanded(
child: CardDesign5(),
),
],
),
),
Expanded(
child: Row(
children: <Widget>[
Expanded(
child: HoopDesign(),
),
],
),
),
],
),
);
}
}
Containers:
import 'package:flutter/material.dart';
import 'input_page.dart';
//#region Team1 cards
class CardDesign3 extends StatefulWidget {
#override
CardDesign3State createState() => CardDesign3State();
}
class CardDesign3State extends State<CardDesign3> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
},
child: Container(
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFEF7F4D),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'Vanaf de 3 punter lijn!',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Bullpen3D',
fontSize: 20,
),
),
height: 100,
width: 100,
));
}
}
class CardDesign2 extends StatefulWidget {
#override
_CardDesign2State createState() => _CardDesign2State();
}
class _CardDesign2State extends State<CardDesign2> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() => Counter.counter += 2);
},
child: Container(
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFEF7F4D),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'Vanaf de 2 punter lijn',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Bullpen3D',
fontSize: 20,
),
),
height: 100,
width: 100,
));
}
}
class CardDesign1 extends StatefulWidget {
#override
_CardDesign1State createState() => _CardDesign1State();
}
class _CardDesign1State extends State<CardDesign1> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() => Counter.counter++);
},
child: Container(
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFEF7F4D),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'Vanaf de 1 punter lijn',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Bullpen3D',
fontSize: 20,
),
),
height: 100,
width: 100,
));
}
}
//#endregion
//#region Team2 cards
class CardDesign4 extends StatefulWidget {
#override
CardDesign4State createState() => CardDesign4State();
}
class CardDesign4State extends State<CardDesign4> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
Counter2.counter += 3 ;
}
);
},
child: Container(
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFEF7F4D),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'Vanaf de 3 punter lijn!',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Bullpen3D',
fontSize: 20,
),
),
height: 100,
width: 100,
));
}
}
class CardDesign5 extends StatefulWidget {
#override
_CardDesign5State createState() => _CardDesign5State();
}
class _CardDesign5State extends State<CardDesign5> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
Counter2.counter += 2;
});
},
child: Container(
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFEF7F4D),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'Vanaf de 2 punter lijn',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Bullpen3D',
fontSize: 20,
),
),
height: 100,
width: 100,
));
}
}
class CardDesign6 extends StatefulWidget {
#override
_CardDesign6State createState() => _CardDesign6State();
}
class _CardDesign6State extends State<CardDesign6> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
Counter2.counter++;
});
},
child: Container(
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Color(0xFFEF7F4D),
borderRadius: BorderRadius.circular(10),
),
child: Text(
'Vanaf de 1 punter lijn',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Bullpen3D',
fontSize: 20,
),
),
height: 100,
width: 100,
));
}
}
//#endregion
class HoopDesign extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 100.0,
width: 100.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/basketballhoop.jpg'),
),
shape: BoxShape.circle,
),
);
}
}
//#region Counters integers
class Counter {
static int counter = 0;
}
class Counter2 {
static int counter = 0;
}
//#endregion
Main:
import 'package:flutter/material.dart';
import 'input_page.dart';
void main() => runApp(BasketballCounter());
class BasketballCounter extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Color(0xFFEE682D),
scaffoldBackgroundColor: Color(0xFFEE682D),
),
home: InputPage(),
);
}
}
What the app looks like:
(see the 0 on the side of the top hoop)
https://i.imgur.com/JwLnVaU.png
I appreciate the help :)
You need to re-render InputPage to get the new value of the counter.
Sample: (I refactored the code, refer to HalfCourt widget)
class InputPage extends StatelessWidget {
const InputPage({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('basketball counter'),
),
body: Column(
children: <Widget>[
const Expanded(child: HalfCourt()),
const Expanded(child: HalfCourt(isUpperSide: false)),
],
),
);
}
}
class HalfCourt extends StatefulWidget {
const HalfCourt({
this.isUpperSide = true,
Key? key,
}) : super(key: key);
final bool isUpperSide;
#override
_HalfCourtState createState() => _HalfCourtState();
}
class _HalfCourtState extends State<HalfCourt> {
int counter = 0;
#override
Widget build(BuildContext context) {
final Widget hoopAndScore = Expanded(
child: Row(
children: <Widget>[
const Expanded(child: HoopDesign()),
Text(counter.toString())
],
),
);
return Column(
children: <Widget>[
if (widget.isUpperSide) hoopAndScore,
Row(
children: <Widget>[
Expanded(
child: CardDesign(
onPressed: () => setState(() => counter++),
text: 'Vanaf de 1 punter lijn',
),
),
Expanded(
child: CardDesign(
onPressed: () => setState(() => counter += 3),
text: 'Vanaf de 3 punter lijn!',
),
),
Expanded(
child: CardDesign(
onPressed: () => setState(() => counter += 2),
text: 'Vanaf de 2 punter lijn',
),
),
],
),
if (!widget.isUpperSide) hoopAndScore
],
);
}
}
class CardDesign extends StatelessWidget {
CardDesign({
required this.onPressed,
required this.text,
Key? key,
}) : super(key: key);
final VoidCallback onPressed;
final String text;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: onPressed,
child: Container(
height: 100,
width: 100,
margin: const EdgeInsets.all(15),
decoration: const BoxDecoration(
color: Color(0xFFEF7F4D),
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Text(
text,
textAlign: TextAlign.center,
style: const TextStyle(
fontFamily: 'Bullpen3D',
fontSize: 20,
),
),
),
);
}
}
class HoopDesign extends StatelessWidget {
const HoopDesign({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: 100.0,
width: 100.0,
color: Colors.orange,
);
}
}

Navigate the page

I am new to Flutter. I know how to navigate from a page to another page, but the problem is, my navigation does not work. In my short experience, I realized that you cannot navigate from the stateless to the stateful widget or the opposite one.
I will share my code below. So could you please tell me what kind of mistake I am making?
Thank you. :)
Actually I was planning to remove the button because this page could be the intro page like it could show the image and text I want and after 3 seconds it could go to the MainApp() page. Maybe if you have any idea how to do it. I would like to hear that too.:)
void main() {
runApp(Intro());
}
class Intro extends StatefulWidget {
#override
_IntroState createState() => _IntroState();
}
class _IntroState extends State<Intro> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.yellow.shade300,
body: SafeArea(
child: (Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Image.asset(
'assets/dinoapp2.png',
height: 200.0,
width: 200.0,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Text(
'Big Title',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Russo One Regular',
fontSize: 30.0,
color: Colors.blueAccent),
),
),
),
Container(
margin: EdgeInsets.fromLTRB(120.0, 20, 120, 100),
child: RaisedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MainApp()),
);
},
padding: EdgeInsets.all(20.0),
textColor: Colors.black,
color: Colors.white,
child: Text(
"Start",
style: TextStyle(fontSize: 20.0),
),
),
)
],
)),
),
),
);
}
}
class MainApp extends StatefulWidget {
#override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.blue,
body: SafeArea(
child: Center(
child: Text('This page I want to go after I press Start button'),
),
),
),
);
}
}
Try This
void main() {
runApp(MaterialApp(
home: Intro(),
));
}
class Intro extends StatefulWidget {
#override
_IntroState createState() => _IntroState();
}
class _IntroState extends State<Intro> {
#override
void initState() {
Future.delayed(Duration(seconds: 3), () {
Route route = MaterialPageRoute(builder: (context) => MainApp());
Navigator.push(context, route);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.yellow.shade300,
body: SafeArea(
child: (Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Image.asset(
'assets/dinoapp2.png',
height: 200.0,
width: 200.0,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Text(
'Big Title',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Russo One Regular',
fontSize: 30.0,
color: Colors.blueAccent),
),
),
),
Center(
child: CircularProgressIndicator(),
)
],
)),
),
);
}
}
class MainApp extends StatefulWidget {
#override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue,
body: SafeArea(
child: Center(
child: Text('This page I want to go after I press Start button'),
),
),
);
}
}
Just Add this function in your intro class
#override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 2), () {
Navigator.push( context, MaterialPageRoute(builder: (context) =>
MainApp()),
); });
}
I don't know why but I copied your code into Intro class. It didn't work. :(

Change String value from one stateful widget to another stateful widget

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,
),
),
),
),
),
],
),
],
)
],
),
),
);
}
}

How to change price on button pressed in flutter

SCENARIO
I have a dummy image, under image there is price and under price there three widget
Two widget are buttons (+) and (-) and one widget is Text('1') which is a QTY.
QUESTION
When i click on the button it increment and decrement the number but how do i change the price based on QTY (if QTY is 2 then price must be double)
class Quantities extends StatefulWidget {
final productprice;
final productimage;
Quantities({this.productprice, this.productimage});
#override
_QuantitiesState createState() => _QuantitiesState(productprice,productimage);
}
class _QuantitiesState extends State<Quantities> {
int counter = 1;
final productprice;
final productimage;
_QuantitiesState(this.productprice, this.productimage);
void increment() {
setState(() {
counter++;
});
}
void decrement() {
setState(() {
counter--;
});
}
#override
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: PlatformAppBar(
backgroundColor: Colors.lightBlue[900],
title: Text('Quantities'),
),
body: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: ListView(
children: <Widget>[
new Container(
height: 200.0,
child: GridTile(
child: Container(
color: Colors.white,
child: Image.asset(productimage),),),),
Center(
child: Text(
productprice,),),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: PlatformButton(
onPressed: () {
decrement();
if (counter < 1) {
counter = 1;
}
},
child: Text(
'-',
),
androidFlat: (_) =>
MaterialFlatButtonData(color: Colors.cyan),
ios: (_) => CupertinoButtonData(
color: Colors.cyan
)),
),
Text(
'$counter',
),
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: PlatformButton(
onPressed: () {
increment();
},
child: Text(
'+',
style: TextStyle(color: Colors.white, fontSize: 30.0),
),
androidFlat: (_) =>
MaterialFlatButtonData(color: Colors.cyan),
ios: (_) => CupertinoButtonData(
color: Colors.cyan
)),
this is the full code example:
import 'package:flutter/material.dart';
import 'package:project_app/proyects/home.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Product price',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Quantities(productimage: "",productprice: "20$",),
);
}
}
class Quantities extends StatefulWidget {
final productprice;
final productimage;
Quantities({this.productprice, this.productimage});
#override
_QuantitiesState createState() =>
_QuantitiesState(productprice, productimage);
}
class _QuantitiesState extends State<Quantities> {
int counter = 1;
final productprice;
double finalprice;
final productimage;
final productp =productprice.replaceAll(new RegExp(r'\$'), '');
_QuantitiesState(this.productprice, this.productimage);
void increment() {
setState(() {
counter++;
finalprice=double.parse(productp)*counter;
});
}
void decrement() {
setState(() {
counter--;
finalprice=double.parse(productp)*counter;
});
}
#override
void initState() {
super.initState();
finalprice= double.parse(productp);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.lightBlue[900],
title: Text('Quantities'),
),
body: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: ListView(
children: <Widget>[
Center(
child: Text(
finalprice.toString(),
),
),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: FlatButton(
onPressed: () {
if (counter > 1) {
decrement();
}
},
child: Text(
'-',
),
),
),
Text(
'$counter',
),
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: FlatButton(
onPressed: () {
increment();
},
child: Text(
'+',
style:
TextStyle(color: Colors.white, fontSize: 30.0),
),
),
),
]),
),
],
),
));
}
}
In this case you need to multiply the value of product price in
setstate(){
productprice=productprice*counter;
}
or
productprice*=counter;

Change color of Title text in ExpansionTile widget when it is expanded

I am using an ExpansionTile and overwriting the default text color when in a collapsed state. I would like to change the color of the title text when the widget is expanded. I noticed that the default behavior if no custom text color is used - is to change the color however as soon as I use a custom color this no longer works. Here is my code.
class TestScreen extends StatefulWidget {
#override
_TestScreenState createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 100.0),
child: Card(
child: Center(
child: Column(
children: <Widget>[
ExpansionTile(
title: Text(
'Text 1',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(112, 190, 115, 1),
),
),
children: <Widget>[Text('Text 2'), Text('Text 3')],
),
],
),
),
),
),
);
}
}
You need to declare the color outside the build method first. And then update it using the onExpansionChanged event of the ExpanstionTile. Like this:
class TestScreen extends StatefulWidget {
#override
_TestScreenState createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
Color _textColor = Colors.green;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 100.0),
child: Card(
child: Center(
child: Column(
children: <Widget>[
ExpansionTile(
onExpansionChanged: (expanded) {
setState(() {
if (expanded) {
_textColor = Colors.blue;
} else {
_textColor = Colors.green;
}
});
},
title: Text(
'Text 1',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: _textColor,
),
),
children: <Widget>[Text('Text 2'), Text('Text 3')],
),
],
),
),
),
),
);
}
}