I have a Flutter project with Bottom Navigation Bar. There are 2 tabs in the Bottom Navigation Bar. One page is webview and the other is flutter page. I add these two pages to my home page with include. I want to manage the navigation in the webview when the back button is pressed while the inside webview is on the page.
Home Page Code:
Future<bool> _onBackPressed() {
return (showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text('Sure?'),
content:
new Text('???'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: new Text('Yes'),
),
],
),
)) ??
false;
}
#override
Widget build(BuildContext context) {
List<Widget> pageList = [
MainHomePage(
text: widget.text,
),
WatchPage()
];
return WillPopScope(
onWillPop: _onBackPressed,
child: Scaffold(
//backgroundColor: Colors.transparent,
body: Container(
child: pageList[_activePage],
),
bottomNavigationBar: CurvedNavigationBar(
animationCurve: Curves.easeInOut,
animationDuration: Duration(milliseconds: 600),
color: Color(0xff1f5269),
backgroundColor: Colors.transparent,
onTap: (index) {
setState(() {
_activePage = index;
});
},
items: <Widget>[
Icon(
Icons.home,
color: Colors.white,
),
Icon(
Icons.tv,
color: Colors.white,
),
],
),
),
);
}
WebView Page:
WebViewController controller;
final Completer<WebViewController> _controller =
Completer<WebViewController>();
Future<void> _onWillPop(BuildContext context) async {
print("onwillpop");
if (await controller.canGoBack()) {
controller.goBack();
return Future.value(true);
} else {
//exit(0);
return Future.value(false);
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _onWillPop(context),
child: Scaffold(
appBar: AppBar(
toolbarHeight: 10,
flexibleSpace: Container(
color: Color(0xff1f5269),
),
),
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: widget.text,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController c) {
_controller.future.then((value) => controller = value);
_controller.complete(c);
},
onProgress: (int progress) {
//print("Webview is loading(progress: $progress&)");
},
onPageStarted: (String url) {
if (url == "https://app.testt.com/Login/Cikis") {
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(builder: (context) => new LoginPage()));
}
},
onPageFinished: (String url) {
if (url == "https://app.testt.com/Login" ) {
Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(builder: (context) => new LoginPage()));
}
},
gestureNavigationEnabled: true,
);
}),
),
);
}
Related
I have a simple webview application, when i go some pages and press back button it closes the app. I want to go previous page when back button pressed. But my code give error on
Try correcting the name to the name of an existing method, or defining a method named '_onWillPop'.
onWillPop: () => _onWillPop(context),
return Scaffold(
body: SafeArea(
child: Stack(
children: <Widget>[
WillPopScope(
onWillPop: () => onWillPop(context),
child: WebView(
javascriptMode: JavascriptMode.unrestricted,
initialUrl: 'https://google.com',
navigationDelegate: (NavigationRequest request) {
setState(() {
isLoading = true;
});
return NavigationDecision.navigate;
},
onPageFinished: (String url) {
setState(() {
isLoading = false;
});
},
// onPageFinished: (finish) {
// setState(() {
// var isLoading = false;
// });
// },
),
),
isLoading
? const Center(
child: CircularProgressIndicator(),
)
: Stack(),
],
),
),
);
you Need to create method for onWillPopScope
this is example
Future<bool> initBackButton() async {
Logger().d('back button pressed');
return await showDialog(
context: context,
builder: (context) => ElasticIn(
child: AlertDialog(
title: const Text('Exit App'),
content: const Text('Do you really want to exit ?'),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('No'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Yes'),
),
],
),
),
) ??
false;
}
after that call like this
onWillPop: () => initBackButton,
So I am facing this problem that my alert Dialog isn't displaying. I had tried every possible solution and searching here and there but nothing works. When I click on the edit button from the pop up menu nothing is displayed everything remains the same.
Calling alert Dialog
trailing: PopupMenuButton(
icon: Icon(Icons.more_vert),
itemBuilder: (context)=>[
PopupMenuItem(
value:1,
onTap: (){
//debugPrint('popup');
Navigator.pop(context);
_showMyDialog();
},
child: ListTile(
leading: Icon(Icons.edit),
title: Text('Edit'),
)),
PopupMenuItem(
value:1,
// onTap: (){
// Navigator.pop(context);
// showDialogBox();
// },
child: ListTile(
leading: Icon(Icons.delete),
title: Text('Delete'),
)),
]),
Alert Dialog Code
Future<void> showDialogBox(String title)async{
editController.text=title;
debugPrint('dialog');
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
debugPrint('alert');
return AlertDialog(
title: Text('Update'),
content: Container(
child: TextFormField(
controller: editController,
),
),
actions: [
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Update')),
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Cancel')),
],
);
}
);
}
Complete Class Code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';
import 'package:firebase_tutorial/utils/routes/routes_names.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:firebase_database/firebase_database.dart';
import '../../utils/utils.dart';
class PostScreen extends StatefulWidget {
const PostScreen({Key? key}) : super(key: key);
#override
State<PostScreen> createState() => _PostScreenState();
}
class _PostScreenState extends State<PostScreen> {
final ref=FirebaseDatabase.instance.ref('Post');
FirebaseAuth _auth=FirebaseAuth.instance;
final searchController=TextEditingController();
final editController=TextEditingController();
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: ()async{
SystemNavigator.pop();
return true;
},
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text('Post Screen'),
actions: [
GestureDetector(
onTap: (){
_auth.signOut().then((value){
Navigator.pushNamed(context, RoutesNames.loginScreen);
}).onError((error, stackTrace){
Utils().toastMessage(error.toString());
});
},
child: Icon(Icons.logout_outlined)),
SizedBox(width: 10,),
],
),
floatingActionButton: FloatingActionButton(
onPressed:(){
Navigator.pushNamed(context, RoutesNames.newPost);
},
child: Icon(Icons.add),),
body: Column(
children: [
// Expanded(
// child:FirebaseAnimatedList(
// query: ref,
// itemBuilder: (context,snapshot,animation,index){
// return ListTile(
// title: Text(snapshot.child('post').value.toString()),
// );
// }
// ),
// ),
Padding(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
onChanged: (String value){
setState(() {
});
},
controller: searchController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Search",
),
),
),
Expanded(child: StreamBuilder(
stream: ref.onValue,
builder: (context,AsyncSnapshot<DatabaseEvent> snapshot){
if(!snapshot.hasData){
return CircularProgressIndicator();
}
else{
return ListView.builder(
itemCount: snapshot.data!.snapshot.children.length,
itemBuilder: (context,index){
Map<dynamic,dynamic> map=snapshot.data!.snapshot.value as dynamic;
List<dynamic> list=[];
list.clear();
list=map.values.toList();
final title=list[index]['post'].toString();
if(searchController.text.isEmpty){
return ListTile(
title: Text(list[index]['post']),
subtitle: Text(list[index]['id'].toString()),
trailing: PopupMenuButton(
icon: Icon(Icons.more_vert),
itemBuilder: (context)=>[
PopupMenuItem(
value:1,
onTap: (){
//debugPrint('popup');
Navigator.pop(context);
_showMyDialog();
},
child: ListTile(
leading: Icon(Icons.edit),
title: Text('Edit'),
)),
PopupMenuItem(
value:1,
// onTap: (){
// Navigator.pop(context);
// showDialogBox();
// },
child: ListTile(
leading: Icon(Icons.delete),
title: Text('Delete'),
)),
]),
);
}
else if(title.toLowerCase().contains(searchController.text.toLowerCase())){
return ListTile(
title: Text(list[index]['post']),
subtitle: Text(list[index]['id'].toString()),
);
}
else{
return Container();
}
});
}
}))
],
),
),
);
}
Future<void> showDialogBox(String title)async{
editController.text=title;
debugPrint('dialog');
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
debugPrint('alert');
return AlertDialog(
title: Text('Update'),
content: Container(
child: TextFormField(
controller: editController,
),
),
actions: [
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Update')),
TextButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Cancel')),
],
);
}
);
}
}
try adding a delay before calling showDialog like this:
await Future.delayed(const Duration(milliseconds: 10));
Your dialog isnt displayed because when you select a menu item the pop() method is automatically called to close the popup menu; so if you open a dialog immediately, the dialog will get automatically popped.
hope this fixes your issue
This is the main page where the basic calculator is set up as the home page:
class BasicCalculator extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
//defines home page
theme: ThemeData(),
initialRoute: '/basic',
routes: {
'/basic': (context) => BasicScreen(),
'/prime': (context) => PrimeFactorScreen(),
'/scientific': (context) => ScientificScreen(),
},
//home: BasicScreen(),
);
}
}
This is the menu. This is set up to be a button on the calculator which brings up the menu. The menu shows up just as it's supposed to, but the routing doesn't work:
Widget buildMenuButton(buttonMargin, String buttonClass, String buttonText, textStyle, buttonDecoration) {
return Container(
margin: buttonMargin,
child: PopupMenuButton(
icon: Icon(Icons.more_horiz),
//color: Colors.cyan,
//elevation: 60,
onSelected: (value) {
setState(() {
_value = value as String;
});
},
itemBuilder: (context) => [
PopupMenuItem(
child: Text("Prime Factor"),
//value: "prime",
onTap: () {
Navigator.pushNamed(context, '/prime');
},
),
PopupMenuItem(
child: Text("Basic"),
//value: "basic",
onTap: () {
Navigator.pushNamed(context, '/basic');
},
),
PopupMenuItem(
child: Text("Scientific"),
value: "scientific",
onTap: () {
Navigator.pushNamed(context, '/scientific');
},
),
],
),
decoration: buttonDecoration,
);
}
I want to display a SnackBar in my Flutter app. I have read the docs and copyed it:
The body of my scaffold:
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("Osztályok"),
leading: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: IconButton(
icon: Icon(Icons.exit_to_app, color: Colors.white70),
onPressed: () {
authService.signOut();
authService.loggedIn = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => GoogleSignUp()));
})),
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 5.0),
child: Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.add_circle_outline,
color: Colors.white70),
onPressed: () {
createPopup(context);
}),
// IconButton(
// icon: Icon(Icons.search, color: Colors.black38),
// onPressed: null),
],
)),
],
),
The SnackBarPage class:
class SnackBarPage extends StatelessWidget {
void jelszopress(TextEditingController jelszoController, BuildContext context) async{
var jelszo;
DocumentReference docRef =
Firestore.instance.collection('classrooms').document(globals.getid());
await docRef.get().then((value) => jelszo= (value.data['Jelszo']) );
if (jelszo == jelszoController.text.toString()){
Navigator.push(context,
MaterialPageRoute(builder: (context) => InClassRoom()));
}
else{
Navigator.pop(context);
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
Scaffold.of(context).showSnackBar(snackBar);
}
}
Future<String> jelszoba(BuildContext context) {
TextEditingController jelszoController = TextEditingController();
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add meg a jelszót'),
content: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: TextField(
controller: jelszoController,
decoration: InputDecoration(hintText: "Jelszó")
)
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text('Mehet'),
onPressed: () {
jelszopress(jelszoController, context);
},
)]);
}
);
}
var nevek;
var IDS;
SnackBarPage(this.nevek, this.IDS);
#override
Widget build(BuildContext context){
return ListView.builder(
itemCount: nevek.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
onTap: () {
globals.setid(IDS[index]);
jelszoba(context);
},
title: Text(nevek[index]),
),
);
},
) ;
}
}
But my cody doesn't display the SnackBar. I tried the solution of this question: How to properly display a Snackbar in Flutter? but adding a Builder widget didn't help.
"Scaffold.of(context)" has been deprecated, will return null. Now use "ScaffoldMessenger.of(context)". As per Flutter documentation.
#override
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('snack'),
duration: const Duration(seconds: 1),
action: SnackBarAction(
label: 'ACTION',
onPressed: () { },
),
));
},
child: const Text('SHOW SNACK'),
),
),
);
}
NOTE: Make sure your main.dart overrided build() function should return "MaterialApp" as a widget, such as:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Must be MaterialApp widget for ScaffoldMessenger support.
return MaterialApp(
title: 'My App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyDashboard(),
);
}
}
So based on the error, it would seem that the context passed in Snackbar.of() is not the correct context. This would make sense based on 1 & 2; and summary copied below:
Each widget has its own BuildContext, which becomes the parent of the widget returned by the StatelessWidget.build or State.build function. (And similarly, the parent of any children for RenderObjectWidgets.)
In particular, this means that within a build method, the build context of the widget of the build method is not the same as the build context of the widgets returned by that build method.
So this means that the build context you are passing in jelszoba(context) function is not the build context you need and is actually the build context of the widget that is instantiating the Scaffold.
So How to Fix:
To fix this wrap your Card widget in your SnackbarPage in a Builder widget and pass the context from it, to the jelszoba(context) method.
An example from 1 I post below:
#override
Widget build(BuildContext context) {
// here, Scaffold.of(context) returns null
return Scaffold(
appBar: AppBar(title: Text('Demo')),
body: Builder(
builder: (BuildContext context) {
return FlatButton(
child: Text('BUTTON'),
onPressed: () {
// here, Scaffold.of(context) returns the locally created Scaffold
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Hello.')
));
}
);
}
)
);
}
You can normally use snack bar in the Bottom Navigation bar in this way. However, if you want to show it in the body, then just copy the code from Builder and paste it in the body of the scaffold.
Scaffold(bottomNavigationBar: Builder(builder: (context) => Container(child: Row(children: <Widget>[
Icon(Icons.add_alarm), Icon(Icons.map), IconButton(icon: Icon(Icons.bookmark),
onPressed:() {
Scaffold.of(context).showSnackBar(mySnackBar);
final mySnackBar = SnackBar(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.white, duration: Duration(seconds: 1),
content: Text(
'Article has been removed from bookmarks',
),);
}
),
],
),
),
),
);
Note: In the behaviour property of SnackBar, you can just leave it empty. But the problem with that is "If you have Curved Navigation Bar or you have a floating action button above the bottom navigation bar, then the snackbar will lift these icons (or FAB ) and will affect the UI". That's why SnackBar.floating is more preferred as it is more capatible with the UI.
But you can check and see on your own which suits you the best.
have a to do app which lists all todos in in a screen
This is my listview
class assignedTask extends StatefulWidget {
static const String id = 'assignedTask';
#override
_assignedTaskState createState() => _assignedTaskState();
}
class _assignedTaskState extends State<assignedTask> {
String Title;
String Summary;
var tasks;
crudMedthods crudObj = new crudMedthods();
var documentID;
var documents;
Future<bool> addDialog(BuildContext context) async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Add Data', style: TextStyle(fontSize: 15.0)),
content: Container(
height: 125.0,
width: 150.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: 'Enter Title'),
onChanged: (value) {
this.Title = value;
},
),
SizedBox(height: 5.0),
TextField(
decoration: InputDecoration(hintText: 'Enter Summary'),
onChanged: (value) {
this.Summary = value;
},
),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Add'),
textColor: Colors.blue,
onPressed: () {
Navigator.of(context).pop();
crudObj.addData({
'Title': this.Title,
'Summary': this.Summary
}).then((result) {
dialogTrigger(context);
}).catchError((e) {
print(e);
});
},
)
],
);
});
}
Future<bool> updateDialog(BuildContext context, selectedDoc) async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Update Data', style: TextStyle(fontSize: 15.0)),
content: Container(
height: 125.0,
width: 150.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: 'Title'),
onChanged: (value) {
this.Title = value;
},
),
SizedBox(height: 5.0),
TextField(
decoration: InputDecoration(hintText: 'Enter Summary'),
onChanged: (value) {
this.Summary = value;
},
),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Update'),
textColor: Colors.blue,
onPressed: () {
Navigator.of(context).pop();
crudObj.updateData(selectedDoc, {
'Title': this.Title,
'Summary': this.Summary
}).then((result) {
// dialogTrigger(context);
}).catchError((e) {
print(e);
});
},
)
],
);
});
}
Future<bool> dialogTrigger(BuildContext context) async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Job Done', style: TextStyle(fontSize: 15.0)),
content: Text('Added'),
actions: <Widget>[
FlatButton(
child: Text('Alright'),
textColor: Colors.blue,
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
#override
void initState() {
crudObj.getData().then((results) {
setState(() {
tasks = results;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Assigned Tasks'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
addDialog(context);
},
),
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
crudObj.getData().then((results) {
setState(() {
tasks = results;
});
});
},
)
],
),
body: _TaskList(),
);
}
Widget _TaskList() {
if (tasks != null) {
return StreamBuilder(
stream: tasks,
// ignore: missing_return
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.documents.length,
padding: EdgeInsets.all(7.0),
itemBuilder: (context, i) {
return ListTile(
title: Text(snapshot.data.documents[i].data["Title"]??""),
subtitle: Text(snapshot.data.documents[i].data["Summary"]??""),
/*onTap: () {
*//*
updateDialog(
context, snapshot.data.documents[i].documentID);
},*/
onTap: (){
Navigator.push(context,MaterialPageRoute(builder:(context)=>detailview(context,snapshot.data.douments[i].documentid)));
},
onLongPress: () {
crudObj.deleteData(snapshot.data.documents[i].documentID);
},
);
},
);
}
return CircularProgressIndicator();
},
);
} else {
return Text('Loading, Please wait..');
}
}
}
When i press ontap it should go to the detail page which shows details of only that particular task(note: i now have only the values Title and summary for now, i will add rest later they should show in the detail page)
My detailview is as follows
class detailview extends StatefulWidget {
static const String id = 'detailview';
detailview(BuildContext context, selectedDoc);
#override
_detailviewState createState() => _detailviewState();
}
class _detailviewState extends State<detailview> {
final TaskReference = FirebaseDatabase.instance.reference().child('Task');
crudMedthods crudObj1 = new crudMedthods();
/*TextEditingController _titleController;
TextEditingController _descriptionController;*/
var tasks;
/*#override
void initState() {
super.initState();
//_titleController = new TextEditingController(text: widget.tasks.Title);
//_descriptionController = new TextEditingController(text: widget.tasks.Summary);
}*
*/
#override
void initState() {
crudObj1.getDetail(tasks).then((results) {
setState(() {
tasks = results;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Details of Tasks'),
actions: <Widget>[
],
),
body: Column(
children: <Widget>[
TextField(
//controller: _titleController,
),
],
)
);
}
}
How could i access the values of particular tapped value in detail page.
Just pass the DocumentSnapshot to your detail page like this:
onTap: (){
Navigator.push(context,MaterialPageRoute(builder:(context)=>DetailPage(context,snapshot.data.douments[i])));
},
In your detail page receive the DocumentSnapshot and access anything you are getting from it.
class DetailPage extends StatelessWidget {
DocumentSnapshot detailDocument;
DetailPage(this.detailDocument);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(detailDocument.data["Title"]),
),
body: Center(
child: Text(detailDocument.data["Summary"]),
),
);
}
}