How can I change the dialog in flutter? - 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));
}

Related

Icon change provider index

Hi I want to change the icon when pressed button.
So I used provider method.
Icon Widget
class LockeryIcon extends StatelessWidget {
final String text;
LockeryIcon({required this.text});
#override
Widget build(BuildContext context) {
return IconButton(
onPressed: () {
Navigator.of(context).pushNamed(SecondView);
print(text);
},
icon: Icon(context.watch<ProviderA>().isIcon),
);
}
}
Listview builder
class Abc extends StatelessWidget {
final List _lock1 = [
'abc 1',
'abc 2',
'abc 3',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: _lock1.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
LockeryIcon(text: _lock1[index]),
],
),
);
},
),
);
}
}
Main Screen
class MainView extends StatelessWidget {
const MainView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Abc(),
);
}
Second View
class SecondView extends StatelessWidget {
const SecondView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: TextButton(
onPressed: () {
context.read<LockeryProvider>().change();
Navigator.of(context).pop();
},
child: Text('Done'),
),
);
}
}
Provider
class ProviderA with ChangeNotifier {
IconData _isIcon = Icons.access_time_filled_sharp;
IconData get isIcon => _isIcon;
void change() {
_isIcon = Icons.add_location;
notifyListeners();
}
}
My problem is when I clicked this all the icons are being changed.
Is there a way to pass index to only change the relevant button???
Or my method is not correct?
Please help me on this
Thank you
You have to store an integer in your provider:
class ProviderA with ChangeNotifier {
int _index = -1;
int get isIndex => _index;
void change(int index) {
_index = index;
notifyListeners();
}
}
and check the index in your Icon class like this:
class LockeryIcon extends StatelessWidget {
final String text;
final int listViewIndex;
LockeryIcon({required this.text,required this.listViewIndex});
#override
Widget build(BuildContext context) {
return IconButton(
onPressed: () {
Navigator.of(context).pushNamed(SecondView);
print(text);
},
icon: Icon(context.watch<ProviderA>().isIndex == listViewIndex ? firstIcon:secondIcon),
);
}
}
finally, use the change function in which you press your button.

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)),

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

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')),

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