Unable to navigate to home page in flutter - flutter

I have an app comprising of home and update screens.
I am unable to navigate back to the home screen from the update screen.
See below code for home screen
// build the list widget
Widget _buildTaskWidget(task) {
return ListTile(
leading: Icon(Icons.assignment),
title: Text(task['name']),
subtitle: Text(task['created_at']),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateTask(task: task),
),
);
}
);
}
See below code for the update screen
#override
Widget build(BuildContext context) {
// final Task task = ModalRoute.of(context).settings.arguments;
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text('Update Task'),
),
body: ListView(
children: <Widget>[
inputWidget(),
inputWidgetForVendor(),
inputWidgetForAmount(),
Container(
margin: EdgeInsets.fromLTRB(45, 1, 45, 1),
child: RaisedButton(
color: Colors.blueAccent,
child: Text('Update Task', style: TextStyle(color: Colors.white)),
onPressed: () async {
var res = await updateNewTask(_taskTextInput.text, _vendorTextInput.text, _amountTextInput.text, id);
print(res);
Navigator.pop(context);
},
),
)
],
)// This trailing comma makes auto-formatting nicer for build methods.
);
}
If I remove the current onPressed function and replace with this below, it works
onPressed: () { Navigator.pop(context); },
What am I doing wrong in the initial function?
The update function successfully updates the list items, however I am unable to navigate back.
See below error logs:
E/flutter (27123): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>'
E/flutter (27123): #0 updateNewTask (package:task/repository/services.dart:67:10)
E/flutter (27123): <asynchronous suspension>
Please help.

Maybe outsourcing your async update function is a simple solution at this point, when you want to instantly go back to your home screen. You could print the update directly in the function then.
Just leave onpressed() as it is.
onPressed: () {
updateNewTask(_taskTextInput.text, _vendorTextInput.text, _amountTextInput.text, id);
Navigator.pop(context);
},

Related

Flutter Dialog: [VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: Null check operator used on a null value

I'm trying to fetch a value when a ListView Item in Page1 is clicked:
...
child: ListTile(
title: Text(title),
onTap: () {
Navigator.pop(context, <String>[title]);
},
),
...
Here, title is a String.
This is popped into Page 0:
...
CupertinoButton(
child: Icon(CupertinoIcons.add),
onPressed: () async {
var value = await Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => const Page1(),
),
);
print(value); // Added for debugging
showNewDialog(context, value);
},
...
),
And this is my showNewDialog method:
Future<dynamic> showNewDialog(
BuildContext context, String name) {
return showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text(name),
content: ...
actions: [
CupertinoDialogAction(
child: Text("Cancel"),
isDestructiveAction: true,
onPressed: () {
Navigator.pop(context);
},
),
CupertinoDialogAction(
child: Text("Add"),
onPressed: () {
...
Navigator.pop(context, [...]);
},
),
],
);
},
);
}
tldr; When I click a button on Page0, It opens Page1 and I can click a ListView item which basically sends the title (String) of that item back to Page0 so that I can create a CupertinoAlertDialog with title as the title of that Dialog.
When I try to do this, I get the following error:
[VERBOSE-2:ui_dart_state.cc(198)] Unhandled Exception: Null check operator used on a null value
#0 StatefulElement.state (package:flutter/src/widgets/framework.dart:4926:44)
#1 Navigator.of (package:flutter/src/widgets/navigator.dart:2542:47)
#2 showCupertinoDialog (package:flutter/src/cupertino/route.dart:1291:20)
#3 showNewDialog (package:sid/utils.dart:37:10)
#4 _Page0State.build.<anonymous closure> (package:sid/page_0.dart:61:13)
The print value prints the right value, so there is no null value being passed in.
Also, I haven't used the '!' operator anywhere in my code. The error seems to point to showCupertinoDialog, which is weird.
Any help will be appreciated.
Thanks :D
You can put the variable static and put the value that you want.
And after that when return to the main page u can check if the var is not empty
And if it's not than your condition :)

Flutter - call Navigator inside switch which it is inside builder

I want to navigate to QrScan screen once the icons get pressed, instead, I got an error!!
setState() or markNeedsBuild() called during build
I want to navigate to that screen and get data from QR Codes, after that I want this data to be shown on another screen!
It says:
This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets.
A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building.
This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built.
Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
Overlay- [LabeledGlobalKey#a5a46]
The widget which was currently being built when the offending call wasmade was: builder
class MainTabsScreen extends StatefulWidget {
#override
_MainTabsScreenState createState() => _MainTabsScreenState();
}
class _MainTabsScreenState extends State<MainTabsScreen> {
int page = 3;
void _openScanner() {
Navigator.push(context, MaterialPageRoute(builder: (context) => QrScan()));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Builder(
builder: (context) {
switch (page) {
case 0:
return ExploreScreen();
case 1:
return OffersScreen();
case 2:
_openScanner();
break;
case 3:
return AltersScreen();
case 4:
return ChatsScreen();
default:
return ExploreScreen();
}
},
),
),
bottomNavigationBar: ConvexAppBar(
top: -20.0,
backgroundColor: Colors.white,
activeColor: Color(0xBB0BCC83),
color: Color(0xBB0BCC83),
height: 53.0,
elevation: 0.0,
initialActiveIndex: 3,
items: [
TabItem(
icon: Icons.home,
title: 'Home',
),
TabItem(
icon: Icons.list,
title: 'Offers',
),
TabItem(
icon: Icons.qr_code,
title: 'Scan',
),
TabItem(
icon: Icons.add_alert,
title: 'Notification',
),
TabItem(
icon: Icons.chat,
title: 'Chats',
),
],
onTap: (id) {
setState(() => page = id);
},
),
);
}
}
As discussed in comments, a solution was to call the navigator.push when id == 2 within the onTap function.

Showing snackbar from alert dialog

I'm at a loss with this one. So I know that to show a snack bar, you have to have access to a build context whose ancestor is a scaffold. To solve this I usually just make a separate widget within the scaffold within which a new build context can be called. However, I can't seem to get this to work when I use an alert dialog.
The 'child' widget i've made under the scaffold looks like this:
class DeleteButton extends StatelessWidget {
DeleteButton({#required this.vm, #required this.popCallback});
final AddJobVM vm;
final Function popCallback;
#override
Widget build(BuildContext context) {
final continueCallBack = () async {
print("deleting ${vm.jobName}");
ToasterBundle toast;
toast = await vm.deleteJob();
print(toast.success);
Scaffold.of(context).showSnackBar(generateSnackBar(toast));
await Future.delayed(
Duration(seconds: 2),
);
if (toast.success) {
popCallback();
}
};
return Padding(
padding: EdgeInsets.only(right: kStandardPadding),
child: GestureDetector(
onTap: () {
showDialog(
context: context,
builder: (context) {
return AlertDialogueBlurredBG(
title: 'Delete Job',
content: 'Are you sure you want to delete this job?',
continueCallBack: continueCallBack,
);
});
},
child: Icon(
Icons.delete_outline,
color: kColorWhite,
size: 28,
),
),
);
}
}
But I'm getting an error when I call the 'continueCallBack':
[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
#0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3781:9)
#1 Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3795:6)
#2 Element.findAncestorStateOfType (package:flutter/src/widgets/framework.dart:3914:12)
#3 Scaffold.of (package:flutter/src/material/scaffold.dart:1453:42)
#4 DeleteButton.build.<anonymous closure> (package:upworkv2/screens/jobs/add_edit_job_screen.dart:615:16)
<asynchronous suspension>
#5 DeleteButton.build.<anonymous closure> (package:upworkv2/screens/jobs/add_edit_job_scree<…>
I would have thought that using a call back which references the build context outside of the alert dialog would have worked but no dice. Any ideas on where I'm going wrong here?
Builder Widget will help in this case, just see How I use & implement it,
body: Builder(
builder: (BuildContext innerContext) {
return RaisedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Are you sure?'),
content: Text('Do you want to go to background?'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('NO')),
FlatButton(
onPressed: () {
Scaffold.of(innerContext).showSnackBar(SnackBar(
content: Text('Added added into cart'),
duration: Duration(seconds: 2),
action:
SnackBarAction(label: 'UNDO', onPressed: () {}),
));
},
child: Text('YES'))
],
),
);
},
);
},
),
This exception happens because you are using the context of the widget that instantiated Scaffold. Not the context of a child of Scaffold.
Output:

Flutter SnackBar or Toast show only after each Hot Reload

when i use this method to resolve
setState() or markNeedsBuild() called during build problem
issue, SnackBar, EdgeAlert, Toast don't show on multiple click on FloatingActionButton but, after each Hot Reload, theme appear on screen,
_onWidgetDidBuild(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
for example:
body: Builder(
builder: (scaffoldContext) => Column(children: <Widget>[
Container(
child: Text('aaa'),
),
BindingWidget<_HomePageViewModel>(
bindings: <Binding>[
Binding('registerStatus', bindableBase,
_HomePageViewModel.registerStatusProperty,
bindingDirection: BindingDirection.TwoWay,
valueConverter: _NumberValueConverter())
],
builder: (bc) {
_onWidgetDidBuild(() {
var registerStatus =
BindingWidget.of<_HomePageViewModel>(bc)
.getValue('registerStatus') as String;
print('2) $registerStatus');
switch(int.parse(registerStatus)){
case 1:
EdgeAlert.show(context, title: 'Title', description: 'Description', gravity: EdgeAlert.TOP);
/*
Scaffold.of(scaffoldContext).showSnackBar(
SnackBar(
content: Text('dddddd'),
backgroundColor: Colors.red,
),
);*/
/* Toast */
break;
}
});
return Container();
}),
])),
how can i resolve this problem for this implementation my code?
thanks in advance

Flutter call back to originating widget on back button

I have a Future Builder that builds a ListView.builder, The builder ListTiles are build by another widget that have an ontap function. I have figured out how to get something to run on the final widget by using the back button, but have not been able to figure out how to call something on the original widget on back button. For instance, I need to refresh the top level data when the user clicks back button and not just the data in the secondary widget which is already working.
I hope this makes sense, any help would be great.
UPDATE Here is the code. I am simplifying what I am showing because making a simple example will lose the context. Below you see that I have a FutureBuilder that returns a ListBuilder that returns a new ChatWidget. This is the top level, this Future needs to be refreshed when I click on the back button. However the onTap to trap the callback is in the ChatWidget.
new Expanded(
child: new RefreshIndicator(
child: new FutureBuilder<List<Map>>(
future: chatlist,
builder: (BuildContext context, AsyncSnapshot<List<Map>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: return new Text('Waiting to start');
case ConnectionState.waiting: return new Text('Loading...');
default:
if (snapshot.hasError) {
return new Text('Error: ${snapshot.error}');
} else {
return new ListView.builder(
itemBuilder: (context, index) {
ChatServerList mychat = new ChatServerList(snapshot.data[index]['msgkey'],snapshot.data[index]['refid'],snapshot.data[index]['referralname'], snapshot.data[index]['oid'],snapshot.data[index]['sendname'],snapshot.data[index]['pid'],snapshot.data[index]['receivename'],snapshot.data[index]['pgrpid'],snapshot.data[index]['prid'],snapshot.data[index]['message'],);
bool isgroup = true;
if (mychat.grpid == 0) {
isgroup = false;
}
return new ChatWidget(mychat: mychat,threaded: threaded, isgroup: isgroup);
},
itemCount: snapshot.data.length,
);
}
}
},
),
onRefresh: _onRefresh
),
)
This is built in the ChatWidget, you notice the _onTap:
new MyListTile(
leading: new CircleAvatar(
child: _chatAvatar(),
//child: !isgroup ? _indMsg() : _grpMsg(), radius: 18.0,
),
//subtitle: new Text(widget.mychat.oname),
title: new Text(widget.mychat.referralname),
trailing: new Text(widget.mychat.oname, textAlign: TextAlign.right,),
//isThreeLine: true,
//onTap: doTap(threaded),
onTap: _onTap,
onLongPress: _doArchive,
),
new MyListTile(
leading: new Text(' '),
title: new Text(submymessage, textAlign: TextAlign.left,
style: new TextStyle(fontSize: 15.0,
color: Colors.grey, fontStyle: FontStyle.italic),),
trailing: _unreadBabdge(),
onTap: _onTap,
onLongPress: _doArchive,
),
That _onTap is below and has the code to handle the back button.
_onTap() async {
ChatDB.instance.updateRead(widget.mychat.msgkey);
if (threaded) {
//TODO
} else {
Route route = new MaterialPageRoute(
settings: new RouteSettings(name: "/ChatServerDivided"),
builder: (BuildContext context) => new ChatServerDivided(mychat: widget.mychat, title: "Chat Messages",),
);
//Navigator.of(context).push(route);
var nav = await Navigator.of(context).push(route);
if(nav==true||nav==null){
unread = ChatDB.instance.getUnread(widget.mychat.msgkey);
}
}
}
So what I am trying to find is if this code can somehow commmunicate up to the originating widget so that I can run the original Future again. I hope this makes more sense and is easier to understand.
Yes you can do that. Couldn't see exactly where to fit it into your code but I'll give you the way to handle this. The navigator calls are all Futures which means you can await them on the calling side. It seems like you're just missing passing a value to the .pop call. Below is an example.
Where you navigate you can await for your result
var navigationResult = await Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => Page2()));
Then you can check the navigationResult with a simple if.
if(navigationResult == 'rerun_future') {
uploadFiles(); // Perform your custom functionality here.
}
The way you pass that information back is that when you do a pop call (to navigate back) you'll pass the value 'rerun_future' in there.
Navigator.of(context).pop('rerun_future')
Additionally if you also want to add this functionality to the back button you should surround your Scaffold with WillPopScope, return false to onWillPop and supply a leading item to the appBar where you perform your custom pop call. Example below from this post
#override
Widget build(BuildContext context) {
return new WillPopScope(
onWillPop: () async => false,
child: new Scaffold(
appBar: new AppBar(
title: new Text("data"),
leading: new IconButton(
icon: new Icon(Icons.ac_unit),
onPressed: () => Navigator.of(context).pop('rerun_future'), //<----- pass value here too
),
),
),
);
}