How do I update the list after adding values? - flutter

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

Related

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

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

How to get the page is not disposed

I have application which has mappage using location
class _MapPageState extends State<MapPage> {
LocationData currentLocation;
Location _locationService = new Location();
#override
void initState(){
super.initState();
_locationService.onLocationChanged().listen((LocationData result) async {
setState(() {
print(result.latitude);
print(result.longitude);
currentLocation = result;
});
});
}
In this case, setState() works well when mappage is shown.
However after mappage is disposed, there comes error like this.
E/flutter ( 6596): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter ( 6596): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
So, I have two ideas.
Remove onLocationChanged() listener when page is disposed.
Check if State is disposed or not before setState()
How can I solve this??
You can copy paste two files below and directly replace official example's code
https://github.com/Lyokone/flutterlocation/tree/master/location/example/lib
After Navigate to ListenLocationWidget page,
you can call _stopListen() in dispose()
code snippet
class _MyHomePageState
...
RaisedButton(
child: Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => ListenLocationWidget()),
);
},
),
PermissionStatusWidget(),
Divider(height: 32),
ServiceEnabledWidget(),
Divider(height: 32),
GetLocationWidget(),
Divider(height: 32),
//ListenLocationWidget()
class _ListenLocationState extends State<ListenLocationWidget> {
...
StreamSubscription<LocationData> _locationSubscription;
String _error;
#override
void initState() {
print("initState");
super.initState();
_listenLocation();
}
#override
void dispose() {
print("stopListen");
_stopListen();
super.dispose();
}
Future<void> _listenLocation() async {
_locationSubscription =
location.onLocationChanged.handleError((dynamic err) {
setState(() {
_error = err.code;
});
_locationSubscription.cancel();
}).listen((LocationData currentLocation) {
setState(() {
print("setState");
_error = null;
_location = currentLocation;
});
});
}
Future<void> _stopListen() async {
_locationSubscription.cancel();
}
working demo
full code ListenLocationWidget
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
class ListenLocationWidget extends StatefulWidget {
const ListenLocationWidget({Key key}) : super(key: key);
#override
_ListenLocationState createState() => _ListenLocationState();
}
class _ListenLocationState extends State<ListenLocationWidget> {
final Location location = Location();
LocationData _location;
StreamSubscription<LocationData> _locationSubscription;
String _error;
#override
void initState() {
print("initState");
super.initState();
_listenLocation();
}
#override
void dispose() {
print("stopListen");
_stopListen();
super.dispose();
}
Future<void> _listenLocation() async {
_locationSubscription =
location.onLocationChanged.handleError((dynamic err) {
setState(() {
_error = err.code;
});
_locationSubscription.cancel();
}).listen((LocationData currentLocation) {
setState(() {
print("setState");
_error = null;
_location = currentLocation;
});
});
}
Future<void> _stopListen() async {
_locationSubscription.cancel();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Listen location: ' + (_error ?? '${_location ?? "unknown"}'),
style: Theme.of(context).textTheme.body2,
),
Row(
children: <Widget>[
Container(
margin: const EdgeInsets.only(right: 42),
child: RaisedButton(
child: const Text('Listen'),
onPressed: _listenLocation,
),
),
RaisedButton(
child: const Text('Stop'),
onPressed: _stopListen,
)
],
),
],
),
),
);
}
}
full code main.dart
import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'package:url_launcher/url_launcher.dart';
import 'get_location.dart';
import 'listen_location.dart';
import 'permission_status.dart';
import 'service_enabled.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Location',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: const MyHomePage(title: 'Flutter Location Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final Location location = Location();
Future<void> _showInfoDialog() {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Demo Application'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
const Text('Created by Guillaume Bernos'),
InkWell(
child: Text(
'https://github.com/Lyokone/flutterlocation',
style: TextStyle(
decoration: TextDecoration.underline,
),
),
onTap: () =>
launch('https://github.com/Lyokone/flutterlocation'),
),
],
),
),
actions: <Widget>[
FlatButton(
child: const Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.info_outline),
onPressed: _showInfoDialog,
)
],
),
body: Container(
padding: const EdgeInsets.all(32),
child: Column(
children: <Widget>[
RaisedButton(
child: Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => ListenLocationWidget()),
);
},
),
PermissionStatusWidget(),
Divider(height: 32),
ServiceEnabledWidget(),
Divider(height: 32),
GetLocationWidget(),
Divider(height: 32),
//ListenLocationWidget()
],
),
),
);
}
}

TextField TextController not working inside Stateful widget

I have created a Stateful widget to show my Alert Dialog which contains a searchbar , however the search bar text doesn't update on typing and stays blank. I have set the controller of the textfield as TextEditingController() however its still not working.
code
import 'package:flutter/material.dart';
import 'package:flutter_convertor/Data Models/Society.dart';
class MyDialogContent extends StatefulWidget {
#override
_MyDialogContentState createState() => new _MyDialogContentState();
}
class _MyDialogContentState extends State<MyDialogContent> {
#override
void initState(){
super.initState();
}
#override
Widget build(BuildContext context) {
//Search bar
Container searchBar = Container(
child: Padding(
padding: const EdgeInsets.only(left: 0.0, right: 0.0),
child: TextField(
onChanged: (value) {
filterSearchResults(value);
},
controller: TextEditingController(),
decoration: InputDecoration(
labelText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)))),
),
),
);
AlertDialog dialog = AlertDialog(
title: searchBar,
content: Container(
....
),
);
return dialog;
}
}
The controller works in my main dart file. but not when i put in my Alert Dialog stateful Widget
this one is the same way that you use, this will not work
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
int _selectedCountryIndex = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_buildList(){
if (countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedCountryIndex,
title: new Text(countries[index]),
onChanged: (int value) {
setState((){
_selectedCountryIndex = value;
});
},
);
}
)
);
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: _buildList(),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
you should do it like below
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: new MyDialogContent(countries: countries),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
class MyDialogContent extends StatefulWidget {
MyDialogContent({
Key key,
this.countries,
}): super(key: key);
final List<String> countries;
#override
_MyDialogContentState createState() => new _MyDialogContentState();
}
class _MyDialogContentState extends State<MyDialogContent> {
int _selectedIndex = 0;
#override
void initState(){
super.initState();
}
_getContent(){
if (widget.countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
widget.countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedIndex,
title: new Text(widget.countries[index]),
onChanged: (int value) {
setState((){
_selectedIndex = value;
});
},
);
}
)
);
}
#override
Widget build(BuildContext context) {
return _getContent();
}
}