setState rebuilds page only just right after I call the action again - flutter

I want to add an item from the other page(class), it kind of works I see the item appearing, after I press the button again and just before the second page loads again. It's just weird, it looks like it sets the state only if I press the button again.
It works If I add the item in the _WorkoutListState class.
class WorkoutList extends StatefulWidget {
WorkoutList({Key? key}) : super(key: key);
#override
_WorkoutListState createState() => _WorkoutListState();
}
class _WorkoutListState extends State<WorkoutList> {
List<Workout> workouts = List<Workout>.empty(growable: true);
#override
void initState() {
super.initState();
workouts.add(Workout(name: 'up'));
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(children: <Widget>[
Text('Workouts'),
Expanded(
child: ListView.builder(
itemCount: workouts.length,
itemBuilder: (context, index) {
return WorkoutListItem(
excercise: workouts[index],
);
}),
),
]),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => (AddWorkout(
workout: (workout) => workouts.add(workout),
))));
setState(() {});
},
tooltip: 'Add',
child: Icon(Icons.add),
),
));
}
}
and the other page
class AddWorkout extends StatefulWidget {
final Function(Workout) workout;
AddWorkout({Key? key, required this.workout}) : super(key: key);
#override
_AddWorkoutState createState() => _AddWorkoutState();
}
class _AddWorkoutState extends State<AddWorkout> {
void _addWorkout() {
widget.workout(Workout(name: 'down'));
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: [Text('adadad')],
),
floatingActionButton: FloatingActionButton(
onPressed: _addWorkout,
),
));
}
}

I fixed it with using Navigator's ability to return value
Here is the first onPressed
onPressed: () async {
Workout workout = await Navigator.push(
context, MaterialPageRoute(builder: (context) => (AddWorkout())));
workouts.add(workout);
setState(() {});
},
and the second
onPressed: () => Navigator.pop(context, Workout(name: 'down')),

Related

How can I change the dialog in flutter?

Hi I'm trying to change the view when I do setState() in dialog in flutter. I heard that it would be fixed by wrapping contents in showDialog() by StatefulBuilder(), but it didn't work.
I don't know, but this is probably because I intend to inject contents of dialog from other class that has setState() methods.
This is MyDialog class:
class MyDialog extends StatelessWidget {
final Icon icon;
final Widget contents;
const MyDialog(this.contents, {required this.icon, Key? key}) : super(key: key);
Future<dynamic> dialog(context) {
return showDialog(
context : context,
builder : (context) {
return StatefulBuilder(
builder : (context, setState) {
return Scaffold(
backgroundColor : const Color(0xFF082562),
body : contents
);
}
);
}
);
}
#override
Widget build(BuildContext context) {
return IconButton(
color : const Color(0xFF97A7BF),
icon : icon,
onPressed : () => dialog(context)
);
}
}
And this is the class to be injected:
class _DialogContentsState extends State<AboutApp> {
int _currentPageIndex = 0;
final List<Widget> _pages = [
const FirstPage(),
const SecondPage(),
const ThirdPage()
];
#override
Widget build(BuildContext context) {
return MyDialog(
Column(
children : [
_pages.elementAt(_currentPageIndex),
ElevatedButton(
child : Text('backward'),
onPressed : () => setState(() => _currentPageIndex--)
),
ElevatedButton(
child : Text('forward'),
onPressed : () => setState(() => _currentPageIndex++)
)
]
),
icon : const Icon(Icons.info_outline)
);
}
}
(Some parts of the code have been omitted or modified to simplify the code, so there may be something wrong, sorry.)
I'd like to switch pages when pressing the buttons.
Please tell me how can I do that.
Thanks,
Firstly, I would don't prefer this pattern. Now the issue is we need to update dialog content, to do that we need to trigger StatefulBuilder's setState. We can pass it this way.
class MyDialog extends StatelessWidget {
final Icon icon;
final Widget Function(Function) contents;
const MyDialog(
this.contents, {
required this.icon,
Key? key,
}) : super(key: key);
Future<dynamic> dialog(context) {
return showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return Scaffold(
backgroundColor: const Color(0xFF082562),
body: contents(setState));
},
);
});
}
#override
Widget build(BuildContext context) {
return IconButton(
color: const Color(0xFF97A7BF),
icon: icon,
onPressed: () => dialog(context));
}
}
And usecase will be
return MyDialog((setStateDialog) {
return Column(
children: [
_pages.elementAt(_currentPageIndex),
ElevatedButton(
child: Text('backward'),
onPressed: () => setStateDialog(() => _currentPageIndex--)),
ElevatedButton(
child: Text('forward'),
onPressed: () => setStateDialog(() => _currentPageIndex++))
],
);
}, icon: const Icon(Icons.info_outline));
}

How can I pass arguments to the previous screen with pop()? [duplicate]

This question already has answers here:
Passing data between screens in Flutter
(13 answers)
Closed 10 months ago.
On the screen, the user enters a message in the field and when the button is clicked, pop() fires.
How can I pass the data from the field to the previous screen and display it? For implementation, I need to use pop()
Screen with TextField:
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult() {
Navigator.pop(context, textController.text);
}
ElevatedButton(
onPressed: getResult, child: const Text('Display result'))
ResultScreen:
class ResultScreen extends StatefulWidget {
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
#override
Widget build(BuildContext context) {
// navigation to text_screen
void _navToTextScreen() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _navToTextScreen,
child: const Text('Enter data'),
),
const SizedBox(
height: 50
),
Text('User Message:'),
const SizedBox(
height: 20
),
],
)),
);
}
}
await the pop and the result
Future<void> _navToTextScreen() async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
This is a working proof of concept:
import 'package:flutter/material.dart';
class FirstWidget extends StatefulWidget {
const FirstWidget({Key? key}) : super(key: key);
#override
State<FirstWidget> createState() => _FirstWidgetState();
}
class _FirstWidgetState extends State<FirstWidget> {
String text = 'Hello';
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(text),
//textbutton widget that waits for value from a new page, and when it's popped and sets it to the variable 'text'.
TextButton(
child: const Text('Click me'),
onPressed: () async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const FooWidget(),
),
);
setState(() {
text = result;
});
},
),
],
);
}
}
class FooWidget extends StatelessWidget {
const FooWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return
//text widget that passes it's value to the previous page.
Column(
children: [
const Text('Hello'),
TextButton(
child: const Text('Click me'),
onPressed: () {
Navigator.pop(context, 'Hello from FooWidget');
},
),
],
);
}
}

How do I update the list after adding values?

help me figure out how to make the list update immediately after adding values (pressing the ElevatedButton - Navigator.of(context).pop();).
I have this code in the main file:
import 'package:flutter/material.dart';
import 'data.dart';
void main() {
runApp(const MaterialApp(home: HomeScreen()));
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: const Color(0xff313131),
body: ListView.builder(
shrinkWrap: true,
itemCount: tasks.length,
itemBuilder: (context, index) {
var task = tasks[index];
return ListTile(
title: Text(
tasks[index].taskName,
style: const TextStyle(color: Colors.white),
),
subtitle: Row(
children: [
task.tagOne,
task.tagTwo,
],
),
);
}),
floatingActionButton: FloatingActionButton(
child: const Text('Add'),
onPressed: () {
showDialog(context: context, builder: (context) => AlertClass());
},
),
),
);
}
}
class AlertClass extends StatefulWidget {
const AlertClass({Key? key}) : super(key: key);
#override
State<AlertClass> createState() => _AlertClassState();
}
class _AlertClassState extends State<AlertClass> {
late String _textValue;
late bool _active;
#override
void initState() {
_active = false;
_textValue = 'Empty';
super.initState();
}
#override
Widget build(BuildContext context) {
return AlertDialog(
content: TextField(onChanged: (String value) {
_textValue = value;
}),
actions: [
TextButton(
onPressed: () {
setState(() {
_active = !_active;
});
},
child: _active ? tagOneContainer[0] : tagOneContainer[1],
),
ElevatedButton(
onPressed: () {
setState(() {
tasks.addAll({
TaskData(
taskName: _textValue,
tagOne:
_active ? tagOneContainer[0] : tagOneContainer[1],
tagTwo: tagTwoContainer[0]),
});
});
Navigator.of(context).pop();
},
child: const Icon(Icons.add)),
],
);
}
}
Essentially, when you click the ElevatedButton Alert should close and the list is updated, but it is not.
The list is only updated if you click on HotReload in Android Studio.
The tasks and other variables are taken from another file.
You can do await to your showDialog Widget. If it returns true, you can setState in your HomeScreen Class.
See this code:
bool result = await showDialog(context: context, builder: (context) => AlertClass());
if (result== true) {
setState(() {
});
}
Then in your ElevatedButton in AlertClass, pop with true parameter.
ElevatedButton(
onPressed: () {
setState(() {
tasks.addAll({
TaskData(
taskName: _textValue,
tagOne:
_active ? tagOneContainer[0] : tagOneContainer[1],
tagTwo: tagTwoContainer[0]),
});
});
Navigator.of(context).pop(true);//This will change the state
//of your homescreen class.
},
child: const Icon(Icons.add)),

How to pass a boolean value from one class to another and back?

I am trying to use the Visibility widget to show and hide my page. Here is my logic when at the first page the booean isVisible is true showing the Container widget but as I go to another screen I set the boolean isVisiblevis to false such that my container hides and maintains it state. When I come back from the second screen I want to set the boolean back to true hence showing my container.
First page
class MainScreen extends StatefulWidget {
bool isVisible = true;
MainScreen({this.isVisible});
...
#override
Widget build(BuildContext context) {
body: Container(
//change the margin
margin: EdgeInsets.fromLTRB(0, 0, 0, 300),
child: isVisible ?
Visibility(
maintainAnimation: true,
maintainState: true,
child: (Container(
Text ('first page')
): Container ()
.....
GestureDetector(
onTap: () {
isVisible= false; //set the visibility false
Navigator.push(
//send to search screen
context,
MaterialPageRoute(
builder: (context) => (SecondScreen())));
},
Now on the second page when I pop how do I set the boolean isVisible back to true on first page ?
GestureDetector(
onTap: () {
Navigator.pop(
//send back data
context,
dropOffTextEditingController.text,
);
MainScreen(mapVisible: true,); //doesn't work
},
See what is happening here, when you are setting the isVisible to false you have to use it on the second page means that you have to pass the isVisible data from one page to another. You can refer here:
first.dart
class MainScreen extends StatefulWidget {
bool isVisible = true;
MainScreen({this.isVisible});
}
Navigator.push(context,MaterialPageRoute(builder: (context) => Second(data: isVisible)));
second.dart
class Second extends StatefulWidget {
final String data;
MyPosts({this.data});
}
you can use as widget.data
Refer title and function parameters.
screenone.dart
class ScreenOne extends StatefulWidget {
ScreenOne({Key key = const Key("ScreenOne")}) : super(key: key);
#override
_ScreenOneState createState() => _ScreenOneState();
}
class _ScreenOneState extends State<ScreenOne> {
bool checkScreenOneValue = true;
#override
void initState() {
checkScreenOneValue = true;
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Screen One',
),
),
body: Container(
color: Colors.white,
padding: EdgeInsets.all(15),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ScreenTwo(
testFunction: testFunction, title: "Screen two")));
},
child: Center(
child: Text(
"Screen Two",
),
),
),
),
);
}
testFunction(bool checkValue) {
checkScreenOneValue = checkValue;
print("****TestFunction $checkScreenOneValue");
}
}
screentwo.dart
class ScreenTwo extends StatefulWidget {
final Function testFunction;
final String title;
const ScreenTwo({required this.testFunction, required this.title});
#override
_ScreenTwoState createState() => _ScreenTwoState();
}
class _ScreenTwoState extends State<ScreenTwo> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: InkWell(
child: Center(child: Text("Back")),
onTap: () {
Navigator.pop(context);
widget.testFunction(false);
},
),
);
}
}

Flutter setState public var to another page?

how to setState public var to another page?
int x = 1;
that was in public
in the first page text(x) i want to setstate from the other page
my first page is
class AddFullRequest extends StatefulWidget {
#override
_AddFullRequestState createState() => _AddFullRequestState();
}
class _AddFullRequestState extends State<AddFullRequest> {
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Text(x),
GestureDetector(
onTap: (){
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => AddItemScr()));
},
child: Text('goto'),
),
],
),
);
}
in the other page button to ++ the var in the first page
my other page is
class AddItemScr extends StatefulWidget {
#override
_AddItemScrState createState() => _AddItemScrState();
}
class _AddItemScrState extends State<AddItemScr> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: WillPopScope(onWillPop: (){
Navigator.of(context).pop();
},
child: Column(
children: <Widget>[
FlatButton(onPressed: (){setState(() {
x++;
});}, child: Text('pluss'),)
],
),
),
);
}
}
please help me with this
You can use the callback pattern. In this example, a function (onPressed) is passed to the child. The child calls the function when a button is pressed:
class AddFullRequest extends StatefulWidget {
#override
_AddFullRequestState createState() => _AddFullRequestState();
}
class _AddFullRequestState extends State<AddFullRequest> {
int _x = 0;
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Text("$_x"),
GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => AddItemScr(
onPressed: () => setState(() => _x++),
),
),
);
},
child: Text('goto'),
),
],
),
);
}
}
class AddItemScr extends StatelessWidget {
final VoidCallback onPressed;
const AddItemScr({
Key key,
#required this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
FlatButton(
onPressed: onPressed,
child: Text('Increment'),
),
],
),
);
}
}
You can pass variables between screens. NavigatorState#pop supports passing objects that you can await in the previous screen and set it to it's value.
class AddFullRequest extends StatefulWidget {
#override
_AddFullRequestState createState() => _AddFullRequestState();
}
class _AddFullRequestState extends State<AddFullRequest> {
int x = 0;
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Text('$x'),
GestureDetector(
onTap: () async {
final result = await Navigator.of(context).push<int>(
MaterialPageRoute(
builder: (_) => AddItemScr(variable: x),
),
);
x = result;
setState(() {});
},
child: Text('goto'),
),
],
),
);
}
}
class AddItemScr extends StatefulWidget {
final int variable;
AddItemScr({this.variable});
#override
_AddItemScrState createState() => _AddItemScrState();
}
class _AddItemScrState extends State<AddItemScr> {
int _variable;
#override
void initState() {
_variable = widget.variable;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
FlatButton(
onPressed: () {
setState(() {
_variable++;
});
},
child: Text('pluss'),
),
FlatButton(
onPressed: () {
Navigator.of(context).pop(_variable);
},
child: Text('go back'),
),
],
),
);
}
}