alertdialog lies below webview, so the user cannot see the alertdialog - webview

I have a simple webview app built with flutter and flutter_webview_plugin library. I am trying to add push notification with firebase cloud messaging. The push notification works fine when the app is running in background or is switched off. If the app is on, I configured such that it shows an alert dialog. But this part does not work.
I removed the webview widget and instead added an appBar widget then hot reloaded the app. alert dialog was actually there working fine. It seems like the webview layer is above the alert dialog layer. So even though alert dialog did pop out correctly, the user cannot see it.
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
void main() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
),
);
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
#override
void initState() {
super.initState();
firebaseCloudMessaging_Listeners();
}
void firebaseCloudMessaging_Listeners() {
_firebaseMessaging.getToken().then((token) {
print(token);
});
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
onResume: (Map<String, dynamic> message) async {
print('on resume $message');
},
onLaunch: (Map<String, dynamic> message) async {
print('on launch $message');
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: SafeArea(
child: new WebviewScaffold(
url: "https://m.url.co.kr",
hidden: true,
),
),
),
);
}
}

You can first hide the webviewplugin using this line: webviewPlugin.hide(); and then run the code to create the alertdialog as such:
webviewPlugin.hide();
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);

This is not possible with the flutter_webview_plugin package, you have to switch to the offical webview_flutter in order to do this...
The flutter_webview_plugin devs also give you this warning message:
Warning: The webview is not integrated in the widget tree, it is a native view on top of the flutter view. you won't be able to use snackbars, dialogs ...
Good luck

Related

Flutter: Dark Theme on iOS

I am creating an app using Flutter.
On iOS however (you can also test it on Android), dark theme is not applied.
Using Android widgets, it is working fine tho.
How can I make the Cupertino widgets using the dark theme? Especially for the popups.
I am using Flutter 1.9.1+hotfix6
E.g. the Cupertino "ActionSheet":
import 'package:flutter/material.dart';
import 'home.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 Demo',
theme: ThemeData.dark(),
darkTheme: ThemeData.dark(),
home: Home(),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('test'),
onPressed: () {
Widget secondaryButton, confirmButton, popup;
secondaryButton = CupertinoActionSheetAction(
child: Text('secundary'),
onPressed: () {},
);
confirmButton = CupertinoActionSheetAction(
child: Text('test'),
onPressed: () {},
);
popup = CupertinoActionSheet(
title: Text('Title'),
message: Text('Content'),
cancelButton: secondaryButton,
actions: [confirmButton],
);
showCupertinoModalPopup(
context: context, builder: (context) => popup);
},
),
);
}
}
Screenshot:
Check this repo, you can create platform specific layouts only using a single widget that does all the platform specific boilerplate for you.
Also have support for dark mode, at least in iOS.

how to download/create pdf through webview in flutter

I have created a webviewscaffold but can't download anything from it while browsing from the webview, I made but when I click the button it does nothing. I don't know where to start, like in other browsers that can download and preview, I'm trying to achieve the same thing in here.
class AppState extends State<App> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Align(
alignment: Alignment(1, 1),
child: Container(
child: Web(), // it is a statefulwidget that have WebviewScaffold, i have created it on a new page and imported/used here
),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 20.0),
)
],
),
Align(
alignment: Alignment(0.7, -0.93),
child: FloatingActionButton(
tooltip: "Share",
child: Icon(
Icons.share,
color: Colors.amberAccent,
),
onPressed: () {
_onShareTap();
}),
),
],
),
);
}
I expect, when I click the print or download button within the webview it should work like any other browser.
You can use my plugin flutter_inappwebview, which is a Flutter plugin that allows you to add inline WebViews or open an in-app browser window and has a lot of events, methods, and options to control WebViews.
To be able to recognize downloadable files, you need to set the useOnDownloadStart: true option, and then you can listen the onDownloadStart event!
Also, for example, on Android you need to add write permission inside your AndroidManifest.xml file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Then, you need to ask permission using the permission_handler plugin. Instead, to effectively download your file, you can use the flutter_downloader plugin.
Here is a complete example using http://ovh.net/files/ (in particular, the http://ovh.net/files/1Mio.dat as URL) to test the download:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(
debug: true // optional: set false to disable printing logs to console
);
await Permission.storage.request();
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
InAppWebViewController webView;
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('InAppWebView Example'),
),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: InAppWebView(
initialUrl: "http://ovh.net/files/1Mio.dat",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
useOnDownloadStart: true
),
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
},
onLoadStop: (InAppWebViewController controller, String url) {
},
onDownloadStart: (controller, url) async {
print("onDownloadStart $url");
final taskId = await FlutterDownloader.enqueue(
url: url,
savedDir: (await getExternalStorageDirectory()).path,
showNotification: true, // show download progress in status bar (for Android)
openFileFromNotification: true, // click on notification to open downloaded file (for Android)
);
},
))
])),
),
);
}
}
Here, as you can see, I'm using also the path_provider plugin to get the folder where I want to save the file.
If you are the owner of html page, you can check workaround that works for me. In source of your html page add onclick function for specific resource links:
function downloadpdf() {
var currentHref = window.location.href;
window.history.pushState(null, null, '/app/somepdf.pdf');
setTimeout(() => window.location.replace(currentHref), 1000);
}
In Flutter code add listener:
StreamSubscription<String> _onWebViewUrlChanged;
_onWebViewUrlChanged =
FlutterWebviewPlugin().onUrlChanged.listen((String url) {
if (url.contains('.pdf')) {
launchURL(url);
}
});
launchURL will open predefined url in external browser window. So you can download pdf/etc from flutter_webview_plugin.
You should add some your app-specific js/flutter magic

Flutter : Get AlertDialog From Another Dart File

i need help guys.
I have 2 dart file : main.dart and alertform.dart. some cases require using this method in my application.
I want to try accessing the alerdialog from alertform.dart on the button on main.dart. is that possible? this my code:
main.dart
import 'package:flutter/material.dart';
import 'alertform.dart';
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text('Test'),
),
body: new Column(
children: <Widget>[
RaisedButton(
child: new Text('Show Alert'),
onPressed: (){
CommentForm();
},
)
],
),
);
}
}
alertform.dart
import 'package:flutter/material.dart';
class AlertForm extends StatefulWidget {
#override
_AlertFormState createState() => _AlertFormState();
}
class _AlertFormState extends State<AlertForm> {
void _showDialog() {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: new Text("Alert Dialog body"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Container(
);
}
}
I don't know why you want to call this _dialog from outside class, where you can call inside your class. But if you want to do then you can try this code.
import 'package:flutter/material.dart';
import 'alertform.dart';
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text('Test'),
),
body: new Column(
children: <Widget>[
RaisedButton(
child: new Text('Show Alert'),
onPressed: (){
AlertFormState(context).showDialogBox;
},
)
],
),
);
}
}**
import 'package:flutter/material.dart';
class AlertForm extends StatefulWidget {
#override
AlertFormState createState() => AlertFormState();
}
class AlertFormState extends State<AlertForm> {
void showDialogBox(BuildContext context) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: new Text("Alert Dialog body"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Container(
);
}
}
Create a new Class :
class AlertDemo{
void showDialog(BuildContext context) {
// flutter defined function
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Alert Dialog title"),
content: new Text("Alert Dialog body"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
And then call using the AlertDemo class instance call the showDialog Method.
RaisedButton(
child: new Text('Show Alert'),
onPressed: (){
AlertDemo().showDialog(context);
},
)
I havent tested this as i am travelling and wrote on mobile , so if it didnt worked i will edit the correct one when i reach.
It's simple.
main.dart
import 'package:flutter/material.dart';
import 'showdialog.dart';
void main() => runApp(MaterialApp(
home: HomePage(),
));
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child:
ElevatedButton(
child: Text("Show"),
onPressed: () {
showDialogBox(context);
}
),
);
}
}
showdialog.dart
import 'package:flutter/material.dart';
void showDialogBox(BuildContext context) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("title"),
content: new Text("body"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("close"),
onPressed: () {
Navigator.of(context).pop();
},
)
]
);
}
);
}

One time alert dialogue in Flutter

How do I only show an alert dialogue once? I want to have an alert dialogue appear when the user first loads a screen to show an instruction. Once the dialogue is dismissed, I do not want it to appear again.
You can use Shared Preferences
When launching the app, get the value from shared pref (Ex: isFirstLoaded). If isFirstLoaded == true then show the dialog.
When the dialog is dismissed, set isFirstLoaded = false and save to shared preferences.
Below is an example (please note that the example doesn't handle the dismiss event when tapping on Back key).
Add shared_preferences to your pubspec.yaml
shared_preferences: ^0.5.1+1
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyStatelessApp());
}
class MyStatelessApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Stateless Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: StatelessWidgetDemo(),
);
}
}
class StatelessWidgetDemo extends StatelessWidget {
final keyIsFirstLoaded = 'is_first_loaded';
#override
Widget build(BuildContext context) {
Future.delayed(Duration.zero, () => showDialogIfFirstLoaded(context));
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Flutter Stateless Demo'),
),
body: Center(
child: Text('Hello'),
)));
}
showDialogIfFirstLoaded(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isFirstLoaded = prefs.getBool(keyIsFirstLoaded);
if (isFirstLoaded == null) {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Title"),
content: new Text("This is one time dialog"),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text("Dismiss"),
onPressed: () {
// Close the dialog
Navigator.of(context).pop();
prefs.setBool(keyIsFirstLoaded, false);
},
),
],
);
},
);
}
}
}

Navigate to a new screen in Flutter

How do you navigate to a new screen in Flutter?
These questions are similar, but are asking more than I am.
Flutter - Navigate to a new screen, and clear all the previous screens
Flutter: How do I navigate to a new screen using DropDownMenuItems
Flutter: Move to a new screen without back
flutter navigation to new screen not working
I am adding an answer below.
Navigate to a new screen:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
where context is the BuildContext of a widget and NewScreen is the name of the second widget layout.
Code
main.dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: ElevatedButton(
child: const Text(
'Navigate to a new screen >>',
style: TextStyle(fontSize: 24.0),
),
onPressed: () {
_navigateToNextScreen(context);
},
),
),
);
}
void _navigateToNextScreen(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
}
}
class NewScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('New Screen')),
body: const Center(
child: Text(
'This is a new screen',
style: TextStyle(fontSize: 24.0),
),
),
);
}
}
See also
Documentation
Navigator and Routes and Transitions... Oh, My! - Simon Lightfoot | Flutter Europe
To load new screens with Flutter pre-canned animations, use their respective transition classes. For example:
Container Transformation
Basically we have the first widget or screen transform into the next screen. For this we need to use OpenContainer. The following code illustrates an item in a ListView transformed to its details page.
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
elevation: 2.0,
child: OpenContainer(
transitionType: ContainerTransitionType.fadeThrough,
closedColor: Theme.of(context).cardColor,
closedElevation: 0.0,
openElevation: 4.0,
transitionDuration: Duration(milliseconds: 1500),
openBuilder: (BuildContext context, VoidCallback _) => THENEXTSCREEN(),
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return ListTile(
leading: Icon(Icons.album),
title: Text("ITEM NAME"),
);
},
),
);
}
Shared Axis
This transition is similar to that in Tab or Stepper. We need SharedAxisTransition, PageTransitionSwitcher, along with a state to model transition between active and previous page. If we only switch between two pages we can use a simple boolean isFirstPage for it. Here's the snippet with Provider as state management:
#override
Widget build(BuildContext context) {
return Consumer<YourState>(
builder: (context, state, child) {
return PageTransitionSwitcher(
duration: const Duration(milliseconds: 1500),
reverse: !state.isFirstPage, // STATE
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
);
},
child: state.isFirstPage? FIRSTPAGE() : SECONDPAGE(), // STATE
);
},
);
}
Note that in all these scenarios we don't use Navigator and MaterialPageRoute. All these codes are derived from animations repo so you may want to check it out first.
Navigate to next screen with back using Navigator.push()
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),);
Navigate to next screen without back using Navigator.pushReplacement()
Navigator.pushReplacement(
context,MaterialPageRoute(builder: (context) => SecondRoute()),);
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => NextScreenName()));
}
If you are familiar with web development this approach is similar to routing.
main.dart
void main() {
setupLocator();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
'/' : (BuildContext context)=>HomePage(),
'/register' : (BuildContext context)=>RegisterPage(),
},
);
}
}
You can add button onPressed event from the homepage.dart to navigate register.dart as follows.
onPressed: (){
Navigator.pushReplacementNamed(context, '/register');
},
Here is a full example of routes push / pop:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Routes',
routes: {
'/login': (BuildContext context) => Login(),
// add another route here
// '/register': (BuildContext context) => Register(),
},
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Routes'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: () {
// This gives the back button:
Navigator.of(context).pushNamed('/login');
// This doesn't give the back button (it replaces)
//Navigator.pushReplacementNamed(context, '/login');
},
child: Text('Login'),
),
],
),
),
);
}
}
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login Page'),
),
body: Center(
child: RaisedButton(
onPressed: () {
// This will only work for pushNamed
Navigator.of(context).pop();
},
child: Text('Go back'),
),
));
}
}
you can use that way in your build widget
onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => NewScreen()));},
In formal method :
Navigator.push(context, MaterialPageRoute(builder: (context)=>Second()));
In GetX method :
Get.to(Second());
If we can navigate screen into another page and delete current page from stack then we can use method which is define below :
Get.off(Third());
If we can navigate screen into another page and delete all route or page from stack then we can use the method which is define below :
Get.offAll(Third());
If we want to use Navigator.pop() then GetX give a Method which is define below :
Get.back();
You can try with the following code
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => YourNextScreen())),
I found a good tutorial that I have followed along, it is very comprehensive with screenshots and step by step, you can also download the code and just run it. Very helpful for me learning Flutter especially I am totally a begineer.
https://medium.com/#misterflutter/lesson-5-creating-new-screens-f740994190c7
https://medium.com/#misterflutter/lesson-6-creating-new-screens-part-2-4997085a43af?sk=d2a0fb723af42b78800f7cf19b312b62
With the Get plugin, you can navigate to a new page by simply calling
Get.to(Page());
This way you can present the next screen
Navigator.of(context).push(
MaterialPageRoute(fullscreenDialog: true,
builder: (context) => const NewScreen(),
),
);
FloatingActionButton(
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const AddUser()));
},
child: const Icon(Icons.add),
),

Resources