Making direct-phone call in SPEAKER mode in Flutter - android

I can make direct phone calls from app using this flutter_phone_direct_caller package. But I am trying to start the phone call in SPEAKER MODE (Handsfree) as default.
How can I achieve it (direct phone call + forced speaker mode)?
PS. Although I am testing the app on android only now, the speaker function has to work on both android and ios eventually.
My code so far:
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
void main() {
runApp(Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {_callNumber('780111000');},
child: Text('Call Number'),
),
),
));
}
_callNumber(String phoneNumber) async {
bool res = await FlutterPhoneDirectCaller.callNumber(phoneNumber);
}

You can use package https://pub.dev/packages/flutter_audio_manager to set speaker mode
code snippet
res = await FlutterAudioManager.changeToSpeaker();
full example code
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_audio_manager/flutter_audio_manager.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
AudioInput _currentInput = AudioInput("unknow", 0);
List<AudioInput> _availableInputs = [];
#override
void initState() {
super.initState();
init();
}
Future<void> init() async {
FlutterAudioManager.setListener(() async {
print("-----changed-------");
await _getInput();
setState(() {});
});
await _getInput();
if (!mounted) return;
setState(() {});
}
_getInput() async {
_currentInput = await FlutterAudioManager.getCurrentOutput();
print("current:$_currentInput");
_availableInputs = await FlutterAudioManager.getAvailableInputs();
print("available $_availableInputs");
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: <Widget>[
Text(
"current output:${_currentInput.name} ${_currentInput.port}",
),
Divider(),
Expanded(
child: ListView.builder(
itemBuilder: (_, index) {
AudioInput input = _availableInputs[index];
return Row(
children: <Widget>[
Expanded(child: Text("${input.name}")),
Expanded(child: Text("${input.port}")),
],
);
},
itemCount: _availableInputs.length,
),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
bool res = false;
if (_currentInput.port == AudioPort.receiver) {
res = await FlutterAudioManager.changeToSpeaker();
print("change to speaker:$res");
} else {
res = await FlutterAudioManager.changeToReceiver();
print("change to receiver:$res");
}
await _getInput();
},
),
),
);
}
}

Related

Take camera picture in landscape and portrait with rotation disabled in Flutter

I want to build a screen that allows me to take photos in landscape and portrait. My app only runs in portrait mode by calling the following in main:
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
Due to the fact that my screen doesn't rotate anymore, the images are always taken in portrait, even if I tilt the phone to the side. Here is a small example project where you can see it clearly:
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:flutter/services.dart';
List<CameraDescription> cameras = [];
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
cameras = await availableCameras();
runApp(CameraApp());
}
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
CameraController? controller;
bool takingPicture = false;
XFile? selectedImage;
#override
void initState() {
super.initState();
controller = CameraController(cameras[0], ResolutionPreset.max);
controller!.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (!controller!.value.isInitialized) {
return Container();
}
return MaterialApp(
home: Stack(
fit: StackFit.expand,
children: [
RotatedBox(
quarterTurns: 1 - controller!.description.sensorOrientation ~/ 90,
child: CameraPreview(controller!),
),
CameraPreview(controller!),
Align(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
onPressed: () {
if (!takingPicture) {
takingPicture = true;
controller!.takePicture().then(
(value) {
selectedImage = value;
setState(() {});
takingPicture = false;
},
);
}
},
),
),
Align(
alignment: Alignment.bottomLeft,
child: selectedImage != null
? Container(
height: 100,
width: 200,
child: Image.file(
File(selectedImage!.path),
),
)
: Container(),
),
],
),
);
}
}
When rotating the phone and taking a photo, the photo should also appear in Landscape, but it doesn't. How can I work around this without taking out setPreferredOrientations?
The normal mobile app on Android and iOS can do that too.
What I would do is to remove the constraint when entering the screen and reapplying it when entering.
Here is an example adapted from your code which features 2 screens:
A welcome screen which cannot rotate
The camera screen which does rotate
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:flutter/services.dart';
List<CameraDescription> cameras = [];
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
cameras = await availableCameras();
runApp(MaterialApp(home: WelcomeScreen()));
}
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Welcome! Try rotating the screen, you won\'t be able to!'),
SizedBox(height: 10),
ElevatedButton(
onPressed: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => CameraApp()),
),
child: Text('Press me to go to the camera'),
),
],
),
),
);
}
}
class CameraApp extends StatefulWidget {
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
CameraController? controller;
bool takingPicture = false;
XFile? selectedImage;
#override
void initState() {
super.initState();
// Remove the constraint when entering, this is ok
// not to away since the app is in portrait mode by
// default
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
controller = CameraController(cameras[0], ResolutionPreset.max);
controller!.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (!controller!.value.isInitialized) {
return Container();
}
return MaterialApp(
home: Stack(
fit: StackFit.expand,
children: [
RotatedBox(
quarterTurns: 1 - controller!.description.sensorOrientation ~/ 90,
child: CameraPreview(controller!),
),
CameraPreview(controller!),
Align(
alignment: Alignment.bottomCenter,
child: ElevatedButton(
onPressed: () async {
// Restore the constraint before navigating away
// DO await here to avoid any other screen in
// landscape mode
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => WelcomeScreen()),
);
},
child: Text('Press me to go Home'),
),
),
Align(
alignment: Alignment.bottomRight,
child: FloatingActionButton(
onPressed: () async {
if (!takingPicture) {
takingPicture = true;
controller!.takePicture().then(
(value) {
selectedImage = value;
setState(() {});
takingPicture = false;
},
);
}
},
),
),
Align(
alignment: Alignment.bottomLeft,
child: selectedImage != null
? Container(
height: 100,
width: 200,
child: Image.file(
File(selectedImage!.path),
),
)
: Container(),
),
],
),
);
}
}

flutter webview navigation menu is not working

I'm trying to make a webview app for my website and use the following starter code
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter1',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter1'),
),
body: WebView(
initialUrl: "URL.com",
javascriptMode: JavascriptMode.unrestricted,
),
),
);
}
}
PROBLEM
While the app opens the page, the navigation menu (three dots) do not respond when I click.
What can I do?
EDIT
I also tried to allow all mixed content using flutter_inappwebview
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter2',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter2'),
),
body: InAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse("www.URL.com")),
initialOptions: InAppWebViewGroupOptions(
android: AndroidInAppWebViewOptions(
mixedContentMode: AndroidMixedContentMode.MIXED_CONTENT_COMPATIBILITY_MODE
)
),
),
),
);
}
}
Still the dots are not working.
you have to have a controller for your Navigation- and WebView-Menu
final Completer<WebViewController> _controller =
Completer<WebViewController>();
then in your build methode:
Scaffold(
appBar: AppBar(
title: Text('Webview'),
actions: <Widget>[
NavigationControls(_controller.future),
WebViewMenu(_controller.future),
],
}
),
body: Builder(....)...
these are the classes from https://pub.dev/packages/webview_flutter/example
class WebViewMenu extends StatelessWidget {
WebViewMenu(this.controller);
final Future<WebViewController> controller;
final CookieManager cookieManager = CookieManager();
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: controller,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (controller.hasError) {
return Text('Controller has Error');
}
return PopupMenuButton<MenuOptions>(
onSelected: (MenuOptions value) {
switch (value) {
case MenuOptions.showUserAgent:
_onShowUserAgent(controller.data!, context);
break;
case MenuOptions.listCookies:
_onListCookies(controller.data!, context);
break;
case MenuOptions.clearCookies:
_onClearCookies(context);
break;
case MenuOptions.addToCache:
_onAddToCache(controller.data!, context);
break;
case MenuOptions.listCache:
_onListCache(controller.data!, context);
break;
case MenuOptions.clearCache:
_onClearCache(controller.data!, context);
break;
case MenuOptions.navigationDelegate:
_onNavigationDelegateExample(controller.data!, context);
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
PopupMenuItem<MenuOptions>(
value: MenuOptions.showUserAgent,
child: const Text('Show user agent'),
enabled: controller.hasData,
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCookies,
child: Text('List cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCookies,
child: Text('Clear cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.addToCache,
child: Text('Add to cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCache,
child: Text('List cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCache,
child: Text('Clear cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.navigationDelegate,
child: Text('Navigation Delegate example'),
),
],
);
},
);
}
void _onShowUserAgent(
WebViewController controller, BuildContext context) async {
// Send a message with the user agent string to the Toaster JavaScript channel we registered
// with the WebView.
await controller.evaluateJavascript(
'Toaster.postMessage("User Agent: " + navigator.userAgent);');
}
void _onListCookies(
WebViewController controller, BuildContext context) async {
final String cookies =
await controller.evaluateJavascript('document.cookie');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Cookies:'),
_getCookieList(cookies),
],
),
));
}
void _onAddToCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text('Added a test entry to cache.'),
));
}
void _onListCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript('caches.keys()'
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Toaster.postMessage(caches))');
}
void _onClearCache(WebViewController controller, BuildContext context) async {
await controller.clearCache();
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text("Cache cleared."),
));
}
void _onClearCookies(BuildContext context) async {
final bool hadCookies = await cookieManager.clearCookies();
String message = 'There were cookies. Now, they are gone!';
if (!hadCookies) {
message = 'There are no cookies.';
}
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(message),
));
}
void _onNavigationDelegateExample(
WebViewController controller, BuildContext context) async {
final String contentBase64 =
base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
await controller.loadUrl('data:text/html;base64,$contentBase64');
}
Widget _getCookieList(String cookies) {
if (cookies == null || cookies == '""') {
return Container();
}
final List<String> cookieList = cookies.split(';');
final Iterable<Text> cookieWidgets =
cookieList.map((String cookie) => Text(cookie));
return Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: cookieWidgets.toList(),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture);
final Future<WebViewController> _webViewControllerFuture;
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
if (snapshot.hasData) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data!;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoBack()) {
await controller.goBack();
} else {
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(
const SnackBar(
content: Text("No back history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoForward()) {
await controller.goForward();
} else {
// ignore: deprecated_member_use
Scaffold.of(context).showSnackBar(
const SnackBar(
content: Text("No forward history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: !webViewReady
? null
: () {
controller.reload();
},
),
],
);
}
return Text('loading navigationControls');
},
);
}
}

Getting Album Artwork in flutter

I am trying to create a music app. I managed to get all the songs list but I cannot seem to get album artwork from the song metadata. I am using flutter_audio_query: ^0.3.5+6 plugin for this.
I cannot get the artwork by using songs[index].albumArtwork. It always returns null instead of the image path. What is the problem with my code?
Here is my code
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_audio_query/flutter_audio_query.dart';
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final FlutterAudioQuery audioQuery = FlutterAudioQuery();
List<SongInfo> songs = [];
#override
void initState() {
super.initState();
checkPermission();
getAllSongs();
}
Future<void> getAllSongs() async {
songs = await audioQuery.getSongs();
}
Future<void> checkPermission() async {
if (await Permission.storage.request().isGranted) {
setState(() {});
} else {
_showDialog();
}
}
void _showDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Warning'),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text('The app needs storage permission in order to work'),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () async {
Navigator.of(context).pop();
await checkPermission();
},
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF3C3C3C),
appBar: AppBar(
elevation: 0.0,
centerTitle: true,
toolbarHeight: 64.0,
leading: Icon(
Icons.music_note_rounded,
),
actions: [
IconButton(
onPressed: () {},
icon: Icon(Icons.search_rounded),
),
],
backgroundColor: Colors.transparent,
title: Text(
"All Songs",
style: GoogleFonts.expletusSans(
fontWeight: FontWeight.bold,
),
),
),
body: ListView.builder(
itemCount: songs.length,
itemBuilder: (context, index) {
return ListTile(
leading: Image.asset(
songs[index].albumArtwork != null
? songs[index].albumArtwork
: "assets/placeholder.png",
),
title: Text(songs[index].title),
subtitle: Text(songs[index].artist),
);
},
),
);
}
}
Sometimes albumArtwork will return a null value. In this case you need to use [FlutterAudioQuery().getArtwork()].
Documentation
Use ResourceType.ALBUM to get album image and ResourceType.SONG to song image.
Example:
// check if artistArtPath isn't available.
(song.artwork == null)
? FutureBuilder<Uint8List>(
future: audioQuery.getArtwork(
type: ResourceType.SONG, id: song.id),
builder: (_, snapshot) {
if (snapshot.data == null)
return Container(
height: 250.0,
child: Center(
child: CircularProgressIndicator(),
),
);
return CardItemWidget(
height: 250.0,
title: artist.name,
// The image bytes
// You can use Image.memory widget constructor
// or MemoryImage image provider class to load image from bytes
// or a different approach.
rawImage: snapshot.data,
);
}) :
// or you can load image from File path if available.
Image.file( File( artist.artistArtPath ) )

Flutter app crashes on firebase phone number authentication in Android platform

I have implemented Firebase Phone authentication in my Flutter project. The process crashes after I clicked "login", the app crashes and lost connection.
Below are the codes (get from the internet):
main.dart
import 'package:flutter/material.dart';
import 'loginpage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPage(),
);
}
}
loginpage.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'dart:async';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String phoneNo, smssent, verificationId;
Future<void> verifyPhone() async {
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verID) {
this.verificationId = verID;
};
final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResent]) {
this.verificationId = verId;
smsCodeDialogue(context).then((value) {
print("Code Sent");
});
};
final PhoneVerificationCompleted verifiedSuccess = (AuthCredential auth) {};
final PhoneVerificationFailed verifyFailed = (AuthException e) {
print('${e.message}');
};
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: const Duration(seconds: 5),
verificationCompleted: verifiedSuccess,
verificationFailed: verifyFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
);
}
Future<bool> smsCodeDialogue(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: Text('Enter OTP'),
content: TextField(
onChanged: (value) {
this.smssent = value;
},
),
contentPadding: EdgeInsets.all(10.0),
actions: <Widget>[
FlatButton(
onPressed: () {
FirebaseAuth.instance.currentUser().then((user) {
if (user != null) {
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
);
} else {
Navigator.of(context).pop();
signIn(smssent);
}
});
},
child: Text(
"done",
style: TextStyle(color: Colors.white),
),
),
],
);
});
}
Future<bool> signIn(String smsCode) async {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: verificationId,
smsCode: smsCode,
);
await FirebaseAuth.instance.signInWithCredential(credential).then((user) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginPage(),
),
);
}).catchError((e) {
print(e);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Center(
child: Text('PhoneNumber Login'),
),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
decoration: InputDecoration(
hintText: "Enter your phone number",
),
onChanged: (value) {
this.phoneNo = value;
}),
),
SizedBox(
height: 10.0,
),
RaisedButton(
onPressed: verifyPhone,
child: Text(
"verify",
style: TextStyle(color: Colors.white),
),
elevation: 7.0,
color: Colors.blue,
)
],
),
);
}
}
this is the error
Lost connection to device.
Exited (sigterm)
May I know what is the problem? Anyone would like to help me to solve this...
I solved the problem by simply adding below line into app/build.gradle dependencies.
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.firebase:firebase-auth'
implementation "androidx.browser:browser:1.2.0"
}

goback in webview flutter by back button device

I have 4 bottomNavigationBar that each one has a webview.
but when tap in a one bottomNavigationBar and select a section in webview page, I can't back to previous page in webview by button back device.
I want back in webview just by back button device.
it is now when the press back button exit the app.
but I use double_back_to_close_app.dart to exit the app.
the home class :
import 'package:webview_flutter/webview_flutter.dart';
import 'package:fancy_bar/fancy_bar.dart';
import 'placeholder_widget.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex = 0;
final List<Widget> _children = [
MyPlaceholderWidget('https://googel.com'),
MyPlaceholderWidget('https://googel.com'),
MyPlaceholderWidget('https://googel.com'),
MyPlaceholderWidget('https://googel.com')
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(40.0),
child: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.grey[200],
elevation: 0.0,
)
),
body: DoubleBackToCloseApp(
child: _children[_currentIndex],
snackBar: const SnackBar(
content: Text(
'برای خروج دو بار کلیک کنید',
style: TextStyle(fontFamily: "Shabnam"),
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
),
),
),
bottomNavigationBar: FancyBottomBar(
onItemSelected: onTabTapped,
selectedIndex: _currentIndex, // this will be set when a new tab is tapped
items: [
FancyItem(
textColor: Colors.green,
title: 'حساب',
icon: Icon(
LineIcons.user,
size: 29,
),
),
FancyItem(
textColor: Colors.green,
title: 'وبلاگ',
icon: Icon(
LineIcons.file_text,
size: 27,
),
),
FancyItem(
textColor: Colors.green,
title: 'سبد خرید',
icon: Icon(
LineIcons.shopping_cart,
size: 31,
),
),
FancyItem(
textColor: Colors.green,
title: 'صفحه اصلی',
icon: Icon(
LineIcons.home,
size: 28,
),
),
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
and the webview class in placeholder_widget.dart:
WebViewController controller;
class MyPlaceholderWidget extends StatelessWidget {
var url = 'https://www.mayehtaj.ir' ;
final key = UniqueKey();
MyPlaceholderWidget(String url){
this.url = url ;
}
#override
Widget build(BuildContext context) {
return WebView(
key: key,
javascriptMode: JavascriptMode.unrestricted,
initialUrl: url,
onWebViewCreated: (WebViewController webViewController){
controller = webViewController;
});
}
}
How to detect press back button device and go back to previous page in one webview ?
thank you for help !
You can copy paste run full code below
You can wrap Scaffold with WillPopScope
When user click device back button, you can execute WebView Controller goback
code snippet onwillpop
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _exitApp(context),
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView example'),
// This drop down menu demonstrates that Flutter widgets can be shown over the web view.
actions: <Widget>[
NavigationControls(_controller.future),
SampleMenu(_controller.future),
],
),
code snippet for exit app
WebViewController controllerGlobal;
Future<bool> _exitApp(BuildContext context) async {
if (await controllerGlobal.canGoBack()) {
print("onwill goback");
controllerGlobal.goBack();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("No back history item")),
);
return Future.value(false);
}
}
working demo
full code
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MaterialApp(home: WebViewExample()));
const String kNavigationExamplePage = '''
<!DOCTYPE html><html>
<head><title>Navigation Delegate Example</title></head>
<body>
<p>
The navigation delegate is set to block navigation to the youtube website.
</p>
<ul>
<ul>https://www.youtube.com/</ul>
<ul>https://www.google.com/</ul>
<ul>https://nodejs.org/en</ul>
</ul>
</body>
</html>
''';
class WebViewExample extends StatefulWidget {
#override
_WebViewExampleState createState() => _WebViewExampleState();
}
WebViewController controllerGlobal;
Future<bool> _exitApp(BuildContext context) async {
if (await controllerGlobal.canGoBack()) {
print("onwill goback");
controllerGlobal.goBack();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("No back history item")),
);
return Future.value(false);
}
}
class _WebViewExampleState extends State<WebViewExample> {
final Completer<WebViewController> _controller =
Completer<WebViewController>();
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _exitApp(context),
child: Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView example'),
// This drop down menu demonstrates that Flutter widgets can be shown over the web view.
actions: <Widget>[
NavigationControls(_controller.future),
SampleMenu(_controller.future),
],
),
// We're using a Builder here so we have a context that is below the Scaffold
// to allow calling Scaffold.of(context) so we can show a snackbar.
body: Builder(builder: (BuildContext context) {
return WebView(
initialUrl: 'https://flutter.dev',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// ignore: prefer_collection_literals
javascriptChannels: <JavascriptChannel>[
_toasterJavascriptChannel(context),
].toSet(),
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
if (request.url.startsWith('https://flutter.dev/docs')) {
print('blocking navigation to $request}');
return NavigationDecision.prevent;
}
print('allowing navigation to $request');
return NavigationDecision.navigate;
},
onPageFinished: (String url) {
print('Page finished loading: $url');
},
);
}),
floatingActionButton: favoriteButton(),
),
);
}
JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
return JavascriptChannel(
name: 'Toaster',
onMessageReceived: (JavascriptMessage message) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(message.message)),
);
});
}
Widget favoriteButton() {
return FutureBuilder<WebViewController>(
future: _controller.future,
builder: (BuildContext context,
AsyncSnapshot<WebViewController> controller) {
if (controller.hasData) {
return FloatingActionButton(
onPressed: () async {
final String url = await controller.data.currentUrl();
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Favorited $url')),
);
},
child: const Icon(Icons.favorite),
);
}
return Container();
});
}
}
enum MenuOptions {
showUserAgent,
listCookies,
clearCookies,
addToCache,
listCache,
clearCache,
navigationDelegate,
}
class SampleMenu extends StatelessWidget {
SampleMenu(this.controller);
final Future<WebViewController> controller;
final CookieManager cookieManager = CookieManager();
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: controller,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
return PopupMenuButton<MenuOptions>(
onSelected: (MenuOptions value) {
switch (value) {
case MenuOptions.showUserAgent:
_onShowUserAgent(controller.data, context);
break;
case MenuOptions.listCookies:
_onListCookies(controller.data, context);
break;
case MenuOptions.clearCookies:
_onClearCookies(context);
break;
case MenuOptions.addToCache:
_onAddToCache(controller.data, context);
break;
case MenuOptions.listCache:
_onListCache(controller.data, context);
break;
case MenuOptions.clearCache:
_onClearCache(controller.data, context);
break;
case MenuOptions.navigationDelegate:
_onNavigationDelegateExample(controller.data, context);
break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
PopupMenuItem<MenuOptions>(
value: MenuOptions.showUserAgent,
child: const Text('Show user agent'),
enabled: controller.hasData,
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCookies,
child: Text('List cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCookies,
child: Text('Clear cookies'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.addToCache,
child: Text('Add to cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.listCache,
child: Text('List cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.clearCache,
child: Text('Clear cache'),
),
const PopupMenuItem<MenuOptions>(
value: MenuOptions.navigationDelegate,
child: Text('Navigation Delegate example'),
),
],
);
},
);
}
void _onShowUserAgent(
WebViewController controller, BuildContext context) async {
// Send a message with the user agent string to the Toaster JavaScript channel we registered
// with the WebView.
controller.evaluateJavascript(
'Toaster.postMessage("User Agent: " + navigator.userAgent);');
}
void _onListCookies(
WebViewController controller, BuildContext context) async {
final String cookies =
await controller.evaluateJavascript('document.cookie');
Scaffold.of(context).showSnackBar(SnackBar(
content: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Cookies:'),
_getCookieList(cookies),
],
),
));
}
void _onAddToCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text('Added a test entry to cache.'),
));
}
void _onListCache(WebViewController controller, BuildContext context) async {
await controller.evaluateJavascript('caches.keys()'
'.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
'.then((caches) => Toaster.postMessage(caches))');
}
void _onClearCache(WebViewController controller, BuildContext context) async {
await controller.clearCache();
Scaffold.of(context).showSnackBar(const SnackBar(
content: Text("Cache cleared."),
));
}
void _onClearCookies(BuildContext context) async {
final bool hadCookies = await cookieManager.clearCookies();
String message = 'There were cookies. Now, they are gone!';
if (!hadCookies) {
message = 'There are no cookies.';
}
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(message),
));
}
void _onNavigationDelegateExample(
WebViewController controller, BuildContext context) async {
final String contentBase64 =
base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
controller.loadUrl('data:text/html;base64,$contentBase64');
}
Widget _getCookieList(String cookies) {
if (cookies == null || cookies == '""') {
return Container();
}
final List<String> cookieList = cookies.split(';');
final Iterable<Text> cookieWidgets =
cookieList.map((String cookie) => Text(cookie));
return Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: cookieWidgets.toList(),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data;
controllerGlobal = controller;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoBack()) {
controller.goBack();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(content: Text("No back history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () async {
if (await controller.canGoForward()) {
controller.goForward();
} else {
Scaffold.of(context).showSnackBar(
const SnackBar(
content: Text("No forward history item")),
);
return;
}
},
),
IconButton(
icon: const Icon(Icons.replay),
onPressed: !webViewReady
? null
: () {
controller.reload();
},
),
],
);
},
);
}
}

Categories

Resources