I was trying to code a TikTakToe game in FLutter but failed at the point where I tried to make a button which resets the fields.
class TikTakToe extends StatefulWidget {
const TikTakToe({Key? key}) : super(key: key);
#override
State<TikTakToe> createState() => _TikTakToeState();
}
class _TikTakToeState extends State<TikTakToe> {
int currentindex = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.teal[100],
appBar: AppBar(
title: const Text("Tik Tak Toe"),
backgroundColor: Colors.teal[400],
),
...
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () {
setState(() {
gameOver = true;
});
}, child: const Icon(Icons.refresh))
],
),
],
),
),
),
);
}
}
bool gameOver = false;
class Field extends StatefulWidget {
const Field({Key? key, required this.fieldnumber}) : super(key: key);
final int fieldnumber;
#override
State<Field> createState() => _FieldState();
}
class _FieldState extends State<Field> {
String playersign = "";
#override
Widget build(BuildContext context) {
return TextButton(
child: Text(
gameOver ? "" : signlist[widget.fieldnumber],
style: const TextStyle(fontSize: 60),
),
style: TextButton.styleFrom(
shape: const RoundedRectangleBorder(
side: BorderSide(color: Color.fromARGB(255, 88, 221, 208), width: 2),
borderRadius: BorderRadius.all(Radius.zero),
),
enableFeedback: false,
primary: Colors.amber[800],
fixedSize: const Size(120.0, 120.0),
backgroundColor: Colors.white,
),
onPressed: () {
setState(() {
... Winner evaluation
}
}
}
);
},
);
}
}
Now I have the problem that the buttons (Fields) are actually resetting but only after clicking on them and not instant after clicking the reset button In the TikTakToe class.
The only thing that worked was adding
(context as Element).reassamble(); to the onPressed of the Elevated button
setState(() {
gameOver = true;
(context as Element).reassemble();
});
but I got this warning:
The member 'reassemble' can only be used within instance members of subclasses of 'package:flutter/src/widgets/framework.dart'.
Thanks!
Change gameover variable place into _FieldState class.
class Field extends StatefulWidget {
const Field({Key? key, required this.fieldnumber}) : super(key: key);
final int fieldnumber;
#override
State<Field> createState() => _FieldState();
}
class _FieldState extends State<Field> {
String playersign = "";
bool gameover = false;
#override
Widget build(BuildContext context) {
return TextButton(
child: Text(
gameover ? "" : signlist[widget.fieldnumber],
style: const TextStyle(fontSize: 60),
),: const TextStyle(fontSize: 60),
),
You need to put gameOver variable inside of Field widget and dont forget to use camelCase in naming variables, on the other hand you have to pass onPressed to TextButton if you dont want to be clickable just pass onPressed: null
class Field extends StatefulWidget {
const Field({Key key}) : super(key: key);
#override
State<Field> createState() => _FieldState();
}
class _FieldState extends State<Field> {
String playerSign = "";
bool gameOver = false; // put var inside
#override
Widget build(BuildContext context) {
return Column(
children: [
TextButton(
onPressed: null,
child: Text(
gameOver ? "" : signlist[widget.fieldnumber],
style: const TextStyle(fontSize: 60),
),
),
TextButton(
onPressed: () {
setState(() {
setState(() {
gameOver = true;
});
});
},
child: const Text(
"reset",
style: TextStyle(fontSize: 60),
),
),
],
);
}
}
suggest to read this doc
Related
I have something like that, I tried so many ways and can't call the function like I want.
class PageTest extends StatefulWidget {
PageTest({Key? key}) : super(key: key);
#override
State<PageTest> createState() => PageTestState();
}
class PageTestState extends State<PageTest> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: topMyBar(),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [PageTestList()],
),
),
floatingActionButton: floatingButton(),
);
}
Widget floatingButton() {
return FloatingActionButton.extended(
onPressed: () {
CALL pageTestListFunction() AND SETSTATE ON _PageTestListState;
},
backgroundColor: AppColors.status_ready,
label: Text(
'OK',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
));
}
}
-- END HERE --
class PageTestList extends StatefulWidget {
const PageTestList({Key? key}) : super(key: key);
#override
State<PageTestList> createState() => _PageTestListState();
}
class _PageTestListState extends State<PageTestList> {...
Future<bool> pageTestListFunction() async {...}
}
Is there a way to call the function from PageTestList when onPressed on floatingActionButton is called and then setState on PageTestList?
Thank's!
Use a global key in your widget.
class PageTest extends StatefulWidget {
const PageTest({Key? key}) : super(key: key);
#override
State<PageTest> createState() => PageTestState();
}
class PageTestState extends State<PageTest> {
final _globalkey = GlobalKey<PageTestListState>();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: topMyBar(),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [PageTestList(key:_globalkey)],
),
),
floatingActionButton: floatingButton(),
);
}
Widget floatingButton() {
return FloatingActionButton.extended(
onPressed: () {
//CALL pageTestListFunction() AND SETSTATE ON _PageTestListState;
_globalkey.currentState?.pageTestListFunction();
},
backgroundColor: AppColors.status_ready,
label: const Text(
'OK',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
));
}
}
class PageTestList extends StatefulWidget {
const PageTestList({Key? key}) : super(key: key);
#override
State<PageTestList> createState() => PageTestListState();
}
class PageTestListState extends State<PageTestList> {
var color = Colors.red;
void pageTestListFunction() async {
setState(() {
color = Colors.green;
});
}
#override
Widget build(BuildContext context) {
return Container(
color: color,
width: 100,
height: 100,
);
}
}
I have a variable that is supposed to be populated when an if statement is true.
The if statement is supposed to be true after a value is updated from a stateful widget when pressed.
The stateful widget is _BudgetCategoryCard and it has a bool that is set true when pressed. After being pressed, the value changes and the color of the card turns green as shown in this line: color: widget.hasBeenPressed ? Colors.green : Colors.white
However, after the value of hasBeenPressed has been set true, this if statement should be true but it isn't
if (budgetCategoryCards[index].getHasBeenPressed()) {
setState(() {
selectedBudget = budgetCategoryCards[index].getBudgetName();
});
}
I'm not sure if there is a better way/practice for retrieving values from a stateful widget or if parts of this code should be re-written for improvement but if anyone could recommend changes that would also be tremendously appreciated.
I tried simplifying the code and apologies for the bad code.
Does anyone know why this variable selectedBudget is not getting populated?
main
import 'package:flutter/material.dart';
import 'package:stackoverflow/home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
List<String> budgets = ["gas", "food", "clothes"];
return MaterialApp(
home: CreateExpenseCardScreen(budgetsList: budgets),
);
}
}
home page
import 'package:flutter/material.dart';
class CreateExpenseCardScreen extends StatefulWidget {
const CreateExpenseCardScreen({
Key? key,
required this.budgetsList,
}) : super(key: key);
final List<String> budgetsList;
#override
State<CreateExpenseCardScreen> createState() =>
_CreateExpenseCardScreenState();
}
class _CreateExpenseCardScreenState extends State<CreateExpenseCardScreen> {
String selectedBudget = "";
#override
Widget build(BuildContext context) {
List<_BudgetCategoryCard> budgetCategoryCards = List.generate(
widget.budgetsList.length,
(index) {
return _BudgetCategoryCard(
budgetName: widget.budgetsList[index],
hasBeenPressed: false,
);
},
);
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 100),
...List.generate(
budgetCategoryCards.length,
(index) {
// why is if statement never true even after pressing card?
//
// as a result, selectedBudget doesnt get assigned a value
if (budgetCategoryCards[index].getHasBeenPressed()) {
setState(() {
selectedBudget = budgetCategoryCards[index].getBudgetName();
});
}
return budgetCategoryCards[index];
},
),
Padding(
padding: const EdgeInsets.all(16.0),
child: GestureDetector(
onTap: () {
if (selectedBudget.isEmpty) {
// send error
} else {
Navigator.pop(
context,
[
selectedBudget,
],
);
}
},
child: Container(
height: 50,
color: Colors.green,
child: const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(
"Create",
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
),
),
),
),
),
],
),
),
);
}
}
class _BudgetCategoryCard extends StatefulWidget {
_BudgetCategoryCard({
Key? key,
required this.budgetName,
required this.hasBeenPressed,
}) : super(key: key);
final String budgetName;
bool hasBeenPressed;
bool getHasBeenPressed() {
return hasBeenPressed;
}
String getBudgetName() {
return budgetName;
}
#override
State<_BudgetCategoryCard> createState() => _BudgetCategoryCardState();
}
class _BudgetCategoryCardState extends State<_BudgetCategoryCard> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: GestureDetector(
onTap: () {
setState(() {
widget.hasBeenPressed = true;
});
},
child: Container(
height: 50,
color: widget.hasBeenPressed ? Colors.green : Colors.white,
child: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
widget.budgetName,
style: TextStyle(
color: widget.hasBeenPressed
? Colors.white
: Colors.black.withOpacity(0.5),
),
),
),
),
),
),
);
}
}
I have created a row of 3 buttons and each of them will change the color independently when clicked, each button works independently which means you could make all buttons clicked, but what i want to achieve is that i can only make one button clicked at one time, which means if i click button 2 after clicking button1, button2 will change the state to clicked, and button1 will change the state to unclicked, just like radio buttons. How can this can be done?
Below is the code of the buttons
class _MyHomePageBodyState extends State<MyHomePageBody> {
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Dis(),
SizedBox(width:5),
Price(),
SizedBox(width:5),
New(),
],
),
);
}
}
class Gcolor {
static Color background1 = Colors.white;
static Color font1 = Colors.blue;
static Color background2 = Colors.white;
static Color font2 = Colors.blue;
static Color background3 = Colors.white;
static Color font3 = Colors.blue;
static int times2=0;
static int times3=0;
static var pressed =true;
}
class Dis extends StatefulWidget {
const Dis({Key? key,}) : super(key: key);
#override
State<Dis> createState() => _DisState();
}
class _DisState extends State<Dis> {
var clicked=false;
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background1),
foregroundColor: MaterialStateProperty.all(Gcolor.font1),
),
onPressed: (){
setState((){
clicked=!clicked;
if(clicked){
Gcolor.background1=Colors.blue;
Gcolor.font1=Colors.white;
}
else{
Gcolor.background1=Colors.white;
Gcolor.font1=Colors.blue;
}
});
},
child: Row(
children: [
Text("距離 "),
Icon(Icons.add_location_outlined,
size: 20,),
],
)
),
);
}
}
class Price extends StatefulWidget {
const Price({Key? key}) : super(key: key);
#override
State<Price> createState() => _PriceState();
}
class _PriceState extends State<Price> {
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background2),
foregroundColor: MaterialStateProperty.all(Gcolor.font2),
),
onPressed: (){
setState((){
if(Gcolor.pressed){
Gcolor.times2++;
if(Gcolor.times2%3==1){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==2){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==0){
Gcolor.background2=Colors.white;
Gcolor.font2=Colors.blue;
}
}
});
},
child: Row(
children: [
Text("價格"),
Icon(Icons.attach_money_outlined,
size: 15,),
if(Gcolor.times2%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times2%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
class New extends StatefulWidget {
const New({Key? key}) : super(key: key);
#override
State<New> createState() => _NewState();
}
class _NewState extends State<New> {
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background3),
foregroundColor: MaterialStateProperty.all(Gcolor.font3),
),
onPressed: (){
setState((){
Gcolor.times3++;
if(Gcolor.times3%3==1){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==2){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==0){
Gcolor.background3=Colors.white;
Gcolor.font3=Colors.blue;
}
});
},
child: Row(
children: [
Text("最新"),
Icon(Icons.access_time,
size:15 ,),
if(Gcolor.times3%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times3%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
I have updated your code to manage it to radio button. For price and new classes, I don't find any proper selection there. So just put it in initstate and you can change color based on selection.
Button classes:
class Dis extends StatefulWidget {
Dis({required this.isSelected, required this.onClicked, Key? key,}) : super(key: key);
Function() onClicked;
bool isSelected;
#override
State<Dis> createState() => _DisState();
}
class _DisState extends State<Dis> {
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(widget.isSelected ? Colors.blue : Colors.white),
foregroundColor: MaterialStateProperty.all(widget.isSelected ? Colors.white : Colors.blue),
),
onPressed: (){
widget.onClicked();
},
child: Row(
children: [
Text("距離 "),
Icon(Icons.add_location_outlined,
size: 20,),
],
)
),
);
}
}
class Price extends StatefulWidget {
Price({required this.isSelected, required this.onClicked,Key? key}) : super(key: key);
Function() onClicked;
bool isSelected;
#override
State<Price> createState() => _PriceState();
}
class _PriceState extends State<Price> {
#override
void initState() {
super.initState();
if(widget.isSelected){
Gcolor.times2++;
if(Gcolor.times2%3==1){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==2){
Gcolor.background2=Colors.blue;
Gcolor.font2=Colors.white;
}
else if(Gcolor.times2%3==0){
Gcolor.background2=Colors.white;
Gcolor.font2=Colors.blue;
}
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background2),
foregroundColor: MaterialStateProperty.all(Gcolor.font2),
),
onPressed: (){
widget.onClicked();
},
child: Row(
children: [
Text("價格"),
Icon(Icons.attach_money_outlined,
size: 15,),
if(Gcolor.times2%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times2%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
class New extends StatefulWidget {
New({required this.isSelected, required this.onClicked,Key? key}) : super(key: key);
Function() onClicked;
bool isSelected;
#override
State<New> createState() => _NewState();
}
class _NewState extends State<New> {
#override
void initState() {
super.initState();
Gcolor.times3++;
if(Gcolor.times3%3==1){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==2){
Gcolor.background3=Colors.blue;
Gcolor.font3=Colors.white;
}
else if(Gcolor.times3%3==0){
Gcolor.background3=Colors.white;
Gcolor.font3=Colors.blue;
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: ElevatedButton(
style: ButtonStyle(
side:MaterialStateProperty.all(BorderSide(color: Colors.blue),),
backgroundColor: MaterialStateProperty.all(Gcolor.background3),
foregroundColor: MaterialStateProperty.all(Gcolor.font3),
),
onPressed: (){
widget.onClicked();
},
child: Row(
children: [
Text("最新"),
Icon(Icons.access_time,
size:15 ,),
if(Gcolor.times3%3==1 )
Icon(Icons.arrow_downward,size: 15,color: Colors.yellow,),
if(Gcolor.times3%3==2 )
Icon(Icons.arrow_upward,size: 15,color: Colors.yellow,)
],
)
),
);
}
}
class Gcolor {
static Color background1 = Colors.white;
static Color font1 = Colors.blue;
static Color background2 = Colors.white;
static Color font2 = Colors.blue;
static Color background3 = Colors.white;
static Color font3 = Colors.blue;
static int times2=0;
static int times3=0;
static var pressed =true;
}
Row:
Row(
children: [
Dis(isSelected: _selectedIndex == 0, onClicked: (){
setState((){
_selectedIndex = 0;
});
}),
SizedBox(width:5),
Price(isSelected: _selectedIndex == 1, onClicked: (){
setState((){
_selectedIndex = 1;
});
}),
SizedBox(width:5),
New(isSelected: _selectedIndex == 2, onClicked: (){
setState((){
_selectedIndex = 2;
});
}),
],
)
Please let me know if this don't work for you.
I have this error when I navigate from one page to another page using getx library, in first page I use indexed stack and second page using scaffold, I don't know what to do. Please help me solve this problem, I found many solutions in here but these does not same my problem.
First page:
class HomePage extends StatefulWidget {
HomePage({Key? key}) : super(key: key);
static String route = '/home';
final ScrollController _scrollController = ScrollController();
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _isVisible = true;
void _hideFloationgButton(UserScrollNotification notification) {
final ScrollDirection direction = notification.direction;
setState(() {
if (direction == ScrollDirection.reverse) {
_isVisible = false;
} else if (direction == ScrollDirection.forward) {
_isVisible = true;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFEDF0F3),
appBar: HomeAppBar('DAK'),
body: NotificationListener<UserScrollNotification>(
onNotification: (notification) {
_hideFloationgButton(notification);
return true;
},
child: SingleChildScrollView(
controller: widget._scrollController,
child: Column(
children: const [
StoryList(),
SizedBox(
height: 30,
),
SocialList(),
SizedBox(
height: 30,
),
MiddleNavList(),
SizedBox(
height: 30,
),
PostList(),
],
),
),
),
floatingActionButton: Visibility(
visible: _isVisible,
child: FloatingActionButton(
backgroundColor: prototypeColor,
onPressed: () {
Get.toNamed(Routes.POST);
},
child: const Icon(
Icons.add_box,
color: Colors.white,
),
)));
}
}
And the second page:
class PostPage extends StatelessWidget {
const PostPage({Key? key}) : super(key: key);
static String route = '/post';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
leading: InkWell(
onTap: () {
Get.back();
},
child: const Icon(
Icons.arrow_back_ios,
color: accentColor,
),
),
title: const Text(
'Create post',
style: TextStyle(color: accentColor),
),
backgroundColor: backgroundColor,
elevation: 0,
),
body: const PostBody(),
);
}
}
I am trying to run method doAnimation from another widget by clicking on a FloatingActionButton.
Please tell me how to do this with this simple example. I know how to do this using the Provider package, but the code is cumbersome. How can I do this using Flutter's native methods?
Or, most likely, it can be done nicely with the Provider, but I don't know how.
A similar question has already been asked, but the second version of flutter and dart has already been released.
State management difficulties are probably the biggest newbies problem.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: MyAnimation(),
floatingActionButton: FloatingActionButton(
onPressed: () {
//TODO! error is here
_MyAnimationState().doAnimation();
//MyAnimation().createState().doAnimation(); // ?
},
child: Icon(Icons.play_arrow),
),
);
}
}
class MyAnimation extends StatefulWidget {
const MyAnimation({Key? key}) : super(key: key);
#override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> {
double _height = 250;
bool _isOpen = true;
void doAnimation() {
_isOpen = !_isOpen;
setState(() {
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: AnimatedContainer(
padding: EdgeInsets.all(20),
duration: Duration(milliseconds: 250),
width: 250,
height: _height,
color: Colors.lightBlueAccent,
child: Center(
child: Text(
'My Test String',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
),
),
),
ElevatedButton(
onPressed: () {
doAnimation(); // works as it should
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
}
}
This error occurs when I try to use the class methods in the usual way.
This happens when you call setState() on a State object for a widget that hasn't been inserted into the widget tree yet. It is not necessary to call setState() in the constructor, since the state is already assumed to be dirty when it is initially created.
This is actually quite simple and doesn't require any kind of package. You can do this with the help of global keys. First create a global key like this GlobalKey<_MyAnimationState> _key = GlobalKey<_MyAnimationState>();. Then pass this key while using MyAnimation class like this MyAnimation(key: _key). Now use this key in the onPressed function to call the doAnimation method like this _key.currentState!.doAnimation();
Here is the complete implementation.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
GlobalKey<_MyAnimationState> _key = GlobalKey<_MyAnimationState>(); // declaration of the key
#override
Widget build(BuildContext context) {
return Scaffold(
body: MyAnimation(key: _key), // passing the key
floatingActionButton: FloatingActionButton(
onPressed: () {
_key.currentState!.doAnimation(); // calling the method from child widget
},
child: Icon(Icons.play_arrow),
),
);
}
}
class MyAnimation extends StatefulWidget {
const MyAnimation({Key? key}) : super(key: key);
#override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> {
double _height = 250;
bool _isOpen = true;
void doAnimation() {
_isOpen = !_isOpen;
setState(() {
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: AnimatedContainer(
padding: EdgeInsets.all(20),
duration: Duration(milliseconds: 250),
width: 250,
height: _height,
color: Colors.lightBlueAccent,
child: Center(
child: Text(
'My Test String',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
),
),
),
ElevatedButton(
onPressed: () {
doAnimation(); // works as it should
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
}
}
You can try to write the "doAnimation()" in the initState() instead the use of the floatingActionButton in order to trigger this action when the widget be initialized.
double _height;
bool _isOpen;
#override
void initState() {
super.initState();
bool _isOpen = true;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
}
Then use the ElevatedButton as the setter of the bool and setState to re-rendered the widgets:
ElevatedButton(
onPressed: () {
setState(() {
_isOpen = !_isOpen;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
Your code will look like this:
class MyAnimation extends StatefulWidget {
#override
_MyAnimationState createState() => _MyAnimationState();
}
class _MyAnimationState extends State<MyAnimation> {
double _height;
bool _isOpen;
#override
void initState() {
super.initState();
bool _isOpen = true;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: AnimatedContainer(
padding: EdgeInsets.all(20),
duration: Duration(milliseconds: 250),
width: 250,
height: _height,
color: Colors.lightBlueAccent,
child: Center(
child: Text(
'My Test String',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
),
),
),
ElevatedButton(
onPressed: () {
setState(() {
_isOpen = !_isOpen;
if (_isOpen) {
_height = 250;
} else {
_height = 0;
}
});
},
child: (_isOpen)? Text('Close Widget') : Text('Open Widget'))
],
);
}
}
You can also use the Provider to set the _isOpen value with setter function and avoid all these validations on the widget buttons.