How to set state via a method from another class - flutter

Using Flutter and trying to move out a Widget to another class to keep it modular.
Problem is one of the inner Widget has a setState in it. But when I move it out to another class, it is not valid as it is not stateful. Initially thought of passing in a Function and replace that with the setState function itself but that doesn't seem to work. How can I come around this?
Current method that works fine. I plan to move out everything from line 6 (child: Center) to another class as this is going to be repeated via a PageView. Going to be swiping this page to next page, each looking the same, only the text changes.
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.tealAccent,
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
"Did you hear about the claustrophobic astronaut?",
style: TextStyle(
fontSize: 20,
),
),
Text(
"He just needed a little space.",
style: TextStyle(
fontSize: 20,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
GestureDetector(
child: Icon(heartStatus),
onTap: () {
setState(() {
if (heartStatus == FontAwesomeIcons.heart) {
heartStatus = FontAwesomeIcons.solidHeart;
} else {
heartStatus = FontAwesomeIcons.heart;
}
});
},
),
GestureDetector(
child: Icon(FontAwesomeIcons.shareAlt),
onTap: (){
print('shared');
},
),
],
),
],
),
),
),
),
);
}
This is after moving it to anther class which won't work as set state is not valid here. Passing in function here throws error -> Unnecessary statement
Widget getPageData(Function function){
IconData heartStatus = FontAwesomeIcons.heart;
return Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
"Did you hear about the claustrophobic astronaut?",
style: TextStyle(
fontSize: 20,
),
),
Text(
"He just needed a little space.",
style: TextStyle(
fontSize: 20,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
GestureDetector(
child: Icon(heartStatus),
onTap: () {
setState(() {
//tried passing in function here
if (heartStatus == FontAwesomeIcons.heart) {
heartStatus = FontAwesomeIcons.solidHeart;
} else {
heartStatus = FontAwesomeIcons.heart;
}
});
},
),
GestureDetector(
child: Icon(FontAwesomeIcons.shareAlt),
onTap: (){
print('shared');
},
),
],
),
],
),
),
);
}

You can turn your function:
Widget getPageData(Function function){...}
To a Customised Widget
class CustomizeWidget extend StatefulWidget{
}
class _CustomizeWidgetState extend State<StatefulWidget>{
/// in the build function, you can call setState
}

Related

How can I resolve child duplication error?

How to resolve child duplication error:
Error: Duplicated named argument 'child'.
child: CenteredView(
This is my code:
Container(
child: RaisedButton(
onPressed: (){},
child: Text(
'Alcoholic Beverages',
style: TextStyle(
decoration:TextDecoration.underline,
fontSize: 25,
),
),
),
child: CenteredView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
NavigationBar()
]
)
)
),
]
),
);
}
}
In your Container you have two child, when Container widget can take only a single child. So you need to use some widget that can have more than one child. Column and Row allows you to do that.
Check this link to read more about multi-child widget, unlike Container, which is single-child widget, Column and Row are Multi-child widget.
Multi-Child Widget
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
class selClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: sel(),
);
}
}
class sel extends StatefulWidget {
#override
_selState createState() => _selState();
}
class _selState extends State<sel> {
String _date = "Not set";
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(child:SingleChildScrollView(
child: Container( child: Column (
children: [
RaisedButton(
onPressed: (){},
child: Text(
'Alcoholic Beverages',
style: TextStyle(
decoration:TextDecoration.underline,
fontSize: 25,
),
),
),
CenteredView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
NavigationBar()
]
),
),
]
),
);
}
}
Container can have only One Child . That's why you're getting this error. You need to Column if your view is vertical or Row if your view is horizontal. Use this to solve your problem:
Column :
Container(
child: Column(
children: [
RaisedButton(
onPressed: () {},
child: Text(
'Alcoholic Beverages',
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: 25,
),
),
),
CenteredView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[NavigationBar()])),
],
),
),
);
}
}
OR
Row :
Container(
child: Row(
children: [
RaisedButton(
onPressed: () {},
child: Text(
'Alcoholic Beverages',
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: 25,
),
),
),
CenteredView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[NavigationBar()])),
],
),
),
);
}
}
Container widget can only have one child.
To lay out multiple children, let this widget's child be a widget such as Row, Column, or Stack.

Updating part of a stateful widget

i'm very new to Flutter.
I have a stateful widget. Every time i click a counter, the whole build function runs and the results are updated.
But is there a way to make it a stateless widget and update only the relevant part somehow so i could avoid rebuilding the screen?
I have a stateful widget. Every time i click a counter, the whole build function runs and the results are updated.
But is there a way to make it a stateless widget and update only the relevant part somehow so i could avoid rebuilding the screen?
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home:Home(),
));
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State {
int long = 0;
void increaseCounter(){
setState(() {
long++;
});
}
void decreaseCounter(){
if(long > 0) {
setState(() {
long--;
});
}
}
Text numberOfDeliveries(){
return Text(
'$long deliveries today',
style: TextStyle(
color:Colors.white,
fontSize: 24.0,
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title:Text('Deliveries'),
backgroundColor: Colors.green,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
color: Colors.blue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child:Padding(
padding: const EdgeInsets.symmetric(vertical: 32.0, horizontal: 0.0),
child: Text(
'Long',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
),
Expanded(
child:
// is it possible to update this without running the whole build again?
numberOfDeliveries(),
),
Expanded(
child: Row(
children: [
Expanded(
child: Container(
padding: EdgeInsets.symmetric(vertical: 32.0, horizontal: 0.0),
color:Colors.blue[100],
child: FlatButton(
onPressed: (){
decreaseCounter();
},
child: Text('-'),
),
),
),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(vertical: 32.0, horizontal: 0.0),
color:Colors.blue[500],
),
),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(vertical: 32.0, horizontal: 0.0),
color:Colors.blue[900],
child: FlatButton(
onPressed: (){
increaseCounter();
},
child: Text('+'),
),
),
),
],
),
),
],
),
),
),
Expanded(
child: Container(
color: Colors.blue[800],
child: Row(
children: [
Text('Bottom'),
],
),
),
),
],
),
);
}
}

flutter setState isn't defined

I have a Widget to create a sidebar menu:
Widget dashboard(context) {
return Material(
elevation: 8,
color: backgroundColor,
child: Container(
padding: const EdgeInsets.only(left: 16, right: 16, top: 48),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
InkWell(child: Icon(Icons.menu, color: Colors.white), onTap: () {
setState(() {
isCollapsed = !isCollapsed;
});
},),
Text("Corona App", style: TextStyle(fontSize: 24.0, color: Colors.white)),
Icon(Icons.settings, color: Colors.white),
])
],
),
)
);
}
But I keep getting the error that my setState function isn't defined. My main app is a statefullwidget so this is pretty weird. Can anyone help me?
Here is the full code!
import 'package:flutter/material.dart';
final Color backgroundColor = Color(0xFF4A4A58);
class MenuDashboard extends StatefulWidget {
#override
_MenuDashboardState createState() => _MenuDashboardState();
}
class _MenuDashboardState extends State<MenuDashboard> {
bool isCollapsed = true;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor,
body: Stack(
children: <Widget>[
menu(context),
dashboard(context),
],
),
);
}
}
Widget menu(context) {
return Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Align(
alignment: Alignment.centerLeft,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Home", style: TextStyle(color: Colors.white, fontSize: 20),),
SizedBox(height: 10.0),
Text("Brochures", style: TextStyle(color: Colors.white, fontSize: 20),),
SizedBox(height: 10.0),
Text("Visitekaartje", style: TextStyle(color: Colors.white, fontSize: 20),),
SizedBox(height: 10.0),
]
)
)
);
}
Widget dashboard(context) {
return Material(
elevation: 8,
color: backgroundColor,
child: Container(
padding: const EdgeInsets.only(left: 16, right: 16, top: 48),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
InkWell(child: Icon(Icons.menu, color: Colors.white), onTap: () {
setState(() {
isCollapsed = !isCollapsed;
});
},),
Text("Corona App", style: TextStyle(fontSize: 24.0, color: Colors.white)),
Icon(Icons.settings, color: Colors.white),
])
],
),
)
);
}
with the error message: 'The function setState isn't defined'
setState is a member of State class, which means it can only be called inside a class that extends a State class of a StatefulWidget. If your class is extending StatelessWidget, you can't called state members/methods provided by the super class, including setState, initState and dispose methods.
For the answer, you can't call setState outside of the State class, if possible, put the widget methods inside the class, or pass setState function as a parameter to those methods.
setState can only called inside StatefulWidget. So if I got it right, you use setState in Widget dashboard or menu widgets. It can't work, because those widgets in outside of class. Try to put widgets inside the class

Align text in centre of screen

I am trying to use Expand widget in Column so user can tap in any position of screen to increase counter, but there is an issue in alignment of text in crossAxisAlignment and mainAxisAlignment of the column it didn't apply on Expand widget as the following
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black12,
body: SafeArea(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
i++;
print(i);
});
},
child: Text(
i.toString(),
style: TextStyle(fontSize: 50.0, color: Colors.blueAccent),
textAlign: TextAlign.center,
),
),
),
],
),
),
),
),
);
You went with the wrong approach because you can't Center an Expanded because Expanded takes the entire available space inside Row or Column in your case Column with single Widget inside children the GestureDetector. Here is one of a solution for what you want to achieve
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black12,
body: SafeArea(
child: Center(
child: Stack(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
setState(() => i++;);
},
),
),
],
),
Align(
alignment: Alignment.center,
child: Text(
'$i',
style: TextStyle(fontSize: 50.0, color: Colors.blueAccent),
),
),
],
),
),
),
);
}
another solution using FlatButton inside SizedBox.expand()
so you don't need to fight with a single child column.
class _MyAppState extends State<MyApp> {
int i = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black12,
body: SafeArea(
child: SizedBox.expand(
child: FlatButton(
onPressed: () {
setState(() {
i++;
print(i);
});
},
child: Text(
i.toString(),
style: TextStyle(fontSize: 50.0, color: Colors.blueAccent),
),
),
),
),
),
);
}
}

Is it possible to use a DropDownButton within an IconButton in Flutter?

Update:
#override
Widget build(BuildContext context) {
return new Container(
height: MediaQuery.of(context).size.height,
child: SingleChildScrollView(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Container(
height: 220.0,
width: MediaQuery.of(context).size.width,
child: new GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: Column(
children: <Widget>[
SizedBox(height: 40.0),
Row(
children: <Widget>[
Expanded(
child: Stack(
children: [
Center(
child: Text(
'Profile',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Lato',
color: Colors.white,
fontSize: 50.0,
fontWeight: FontWeight.w700,
),
),
),
Positioned(
right: 8,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 400)),
PopupMenuButton<String>(
icon: Icon(
Icons.settings,
color: Colors.white,
size: 30.0,
),
onSelected: choiceAction,
itemBuilder: (BuildContext context) {
return Constants.choices.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
),
),
],
),
),
],
),
I am trying to implement a DropDownButton inside the OnPressed command of an IconButton, so that when the icon is pressed, a drop down menu is shown.
Update: I've updated my code with the suggestion made, however the icon does not appear.
I'm not sure if this is a problem with my widget tree.
Updated Answer
Please check this code:
class DropdownMenu extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 400)),
PopupMenuButton<String>(
icon: Icon(Icons.settings),
onSelected: choiceAction,
itemBuilder: (BuildContext context) {
return Constants.choices.map((String choice) {
return PopupMenuItem<String>(
value: choice,
child: Text(choice),
);
}).toList();
},
),
],
));
}
}
class Constants {
static const String FirstItem = 'First Item';
static const String SecondItem = 'Second Item';
static const String ThirdItem = 'Third Item';
static const List<String> choices = <String>[
FirstItem,
SecondItem,
ThirdItem,
];
}
void choiceAction(String choice) {
if (choice == Constants.FirstItem) {
print('I First Item');
} else if (choice == Constants.SecondItem) {
print('I Second Item');
} else if (choice == Constants.ThirdItem) {
print('I Third Item');
}
}
Note: This is not dropdown menu but i think this is what you want.
Old answer
You can try using showDialog
child: Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.settings,
color: Colors.black,
size: 30.0,
),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Country List'),
content: new ListView(
children: <Widget>[
new Column(
children: <Widget>[
new DropdownButton<String>(
items: <String>['A', 'B', 'C', 'D', 'E', 'F', 'G'].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (_) {},
),
],
),
],
),
);
});
})
],
)