How to detect phone shakes in Flutter - flutter

Just started with Flutter this weekend and am making great progress.
I have an app that simply changes the image on screen when the image is tapped, or, if the phone is shaken. The first part was simple, and on tap, my images change. Struggling with the shake part.
Been looking at the Sensors package, but am not having much luck (possibly because i don't fully understand what i'm looking for!). Do I just want the app to listen for when a certain accelerometer change happens, and if so, run my change image function? If so, what is the best way to do this?
Any other ideas welcome!
Thanks all :)
Update: This is what I have so far, pretty much exactly what I read from the Shake documentation. But nothing is happening on shake.
import 'package:flutter/material.dart';
import 'package:shake/shake.dart';
void main() {
runApp(DemoPage());
}
String mytext = "Martini?";
class DemoPage extends StatefulWidget {
#override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
#override
void initState() {
super.initState();
ShakeDetector detector = ShakeDetector.waitForStart(onPhoneShake: () {
mytext = "Shaken, not stirred";
print("hello");
});
detector.startListening();
// To close: detector.stopListening();
// ShakeDetector.waitForStart() waits for user to call detector.startListening();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: Padding(padding: const EdgeInsets.all(50),child:
Text(
mytext,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
),
)))));
}
}

Use this package for identifying a shake event and for performing some action.
To listen to phone shake:
ShakeDetector detector = ShakeDetector.autoStart(
onPhoneShake: () {
// Do stuff on phone shake
}
);
OR
ShakeDetector detector = ShakeDetector.waitForStart(
onPhoneShake: () {
// Do stuff on phone shake
}
);
detector.startListening();
To stop listening:
detector.stopListening();
In order to install this package in your pubspec.yaml file, follow this link.
Samples can be found on this link.
import 'package:flutter/material.dart';
import 'package:shake/shake.dart';
class DemoPage extends StatefulWidget {
#override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
#override
void initState() {
super.initState();
ShakeDetector detector = ShakeDetector.autoStart(onPhoneShake: () {
// Do stuff on phone shake
});
// To close: detector.stopListening();
// ShakeDetector.waitForStart() waits for user to call detector.startListening();
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}

I was making a similar app like yours. You just want to change an image or do something else when we shake our phone, right?
The package 'package:shake/shake.dart' is right for this work. This package has some pre-defined functions which uses accelerometer reading from our phone and detect when we have shaked our phone and when we have stopped.
Instead of using:
ShakeDetector detector = ShakeDetector.waitForStart(onPhoneShake: () {
mytext = "Shaken, not stirred";
print("hello");
});
I have used:
ShakeDetector detector = ShakeDetector.autoStart(onPhoneShake: () {
setState(() {
//Code for changing image or something else.
});
});
This solved the problem and when I shook my phone and stopped, the image changes just after I stopped shaking.
I don't know about other functions given in this package like waitForStart() and stopListening() function. Just the above autoStart() function did what I wanted.

As per the document of Shake Plugin it depends upon Sensors Plugin
You will have to add sensors plugin in your dependencies list in pubspec.yaml.
This will make your shake plugin detect shakes.
P.S. Happy Coding ! Stay Safe.

Related

Where to put Flutter AJAX?

I come from the web dev world. I have designed a Flutter app that needs to grab some JSON from the web very early on. I want my first screen to show up, and while it is being drawn, behind the scenes I want the JSON fetch to happen. There is a start button on Page 1 that will be disabled until the JSON is fetched. (But Page 1 will have some text info to keep the reader engaged until the fetch happens.)
Where would I stick the JSON fetch? Can I put it in initState of Page 1? Or can I initiate a call at the very start of main at the root of the app? Or somewhere else?
FWIW I use Provider for state management, if that helps?
Thanks a ton!
Calling it in the initState is best since your widgets still build. Since there will be a state change, make sure you use a stateful widget. Also, don't forget to call setState to re-enable the button. Example code:
class FirstPage extends StatefulWidget {
const FirstPage({Key? key}) : super(key: key);
#override
State<FirstPage> createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
#override
void initState() {
super.initState();
jsonFetch();
}
void jsonFetch() {
// your functionality here
// on complete call:
/*
setState(() {
isJsonFetched = true;
});
*/
}
void doSomethingOnPressed(){
// Your functionality here
}
bool isJsonFetched = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: ElevatedButton(
onPressed: (){
isJsonFetched
? doSomethingOnPressed()
:null;
},
child: Text('Press Me')
)
)
);
}
}

Noticeable delay when changing an image dynamically in Flutter

I am new to Flutter and experienced a problem while trying to make a mobile application.
When switching the image being displayed there is a noticeable delay, where nothing is shown, between the action and the new image appearing.
Adding gaplessPlayback: true to the Image widget does prevent the original image disappearing until the other is loaded but the delay remains.
I tried to precache both images but noticed no difference in load time. Is this delay to be expected or am I doing something wrong?
Issue without gaplessPlayback
class Wallpaper extends StatefulWidget {
#override
StateWallpaper createState() => StateWallpaper();
}
class StateWallpaper extends State<Wallpaper> {
late AssetImage walPath;
late AssetImage wal1;
late AssetImage wal2;
#override
void initState() {
wal1 = AssetImage('assets/wal_dark.jpg');
wal2 = AssetImage('assets/wal_light.jpg');
walPath = wal1; //Set default wallpaper
super.initState();
}
#override
void didChangeDependencies() {
precacheImage(wal1, context);
precacheImage(wal2, context);
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Column(children: [
FloatingActionButton(onPressed: () {
setState(() {
if (walPath == wal2) {
walPath = wal1;
} else {
walPath = wal2;
}
});
}),
Image(image: walPath, gaplessPlayback: true)
]);
}
}
How big are your images (width/height in pixels, and file size)?
Perhaps they are too large, and they are being resized before being displayed?
Try with images that are close to the pixel width/height of the device and see if it is faster...
Also, is it a release build?

How to detect mock location in flutter for ios and android

I am using package of location and google maps flutter in my screen and I want to detect wether user using fake gps or not.. Is there a package that can detect mock location in flutter that available in android and ios? I have tried using TrustFall package but my app always close unexpectedly.. is there another way to detect mock location in flutter?
Use Geolocator and check the Position object's isMocked property.
you can use TrustLocation
permissions:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
usage :
import 'package:trust_location/trust_location.dart';
/* Assuming in an async function */
/// query the current location.
LatLongPosition position = await TrustLocation.getLatLong;
/// check mock location on Android device.
bool isMockLocation = await TrustLocation.isMockLocation;
using steam:
// input seconds into parameter for getting location with repeating by timer.
// this example set to 5 seconds.
TrustLocation.start(5);
/// the stream getter where others can listen to.
TrustLocation.onChange.listen((values) =>
print('${values.latitude} ${values.longitude} ${values.isMockLocation}')
);
/// stop repeating by timer
TrustLocation.stop();
Example:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:trust_location/trust_location.dart';
import 'package:location_permissions/location_permissions.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _latitude;
String _longitude;
bool _isMockLocation;
/// initialize state.
#override
void initState() {
super.initState();
requestLocationPermission();
// input seconds into parameter for getting location with repeating by timer.
// this example set to 5 seconds.
TrustLocation.start(5);
getLocation();
}
/// get location method, use a try/catch PlatformException.
Future<void> getLocation() async {
try {
TrustLocation.onChange.listen((values) => setState(() {
_latitude = values.latitude;
_longitude = values.longitude;
_isMockLocation = values.isMockLocation;
}));
} on PlatformException catch (e) {
print('PlatformException $e');
}
}
/// request location permission at runtime.
void requestLocationPermission() async {
PermissionStatus permission =
await LocationPermissions().requestPermissions();
print('permissions: $permission');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Trust Location Plugin'),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: Column(
children: <Widget>[
Text('Mock Location: $_isMockLocation'),
Text('Latitude: $_latitude, Longitude: $_longitude'),
],
)),
),
),
);
}
}
for more information you can see https://pub.dev/packages/trust_location
github link : https://github.com/wongpiwat/trust-location
I think better to use safe_device. It's working on both Android and IOS

How to bring an app from background to foreground in flutter

I have an app that is managing audio calls. When a call is made to the add and the app is running in the background I need to bring the app in the foreground state. I tried to use Navigator. push but without any result.
You can use the package bringtoforeground. It's fairly in the early stages with respect to its version but it works.
iOS
But this only works on android, you have to keep in mind that iOS apps that are on the background are destroyed. you can read this do
see details here
Android
So this implementation will only work on Android.
The best thing with this package is that you can use it with Firebase Cloud Messaging (FCM) or any other for that matter.
This is their example, Bringtoforeground.bringAppToForeground(); this is the piece of code you use to bring your app to the foreground.
import 'dart:async';
import 'package:bringtoforeground/bringtoforeground.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
Timer.periodic(Duration(seconds: 10), (t) {
Bringtoforeground.bringAppToForeground(); //This is the only thing that matters
});
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Running on: $_platformVersion\n'),
),
),
);
}
}
Install flutter_foreground_task package here is
and use FlutterForegroundTask.minimizeApp() for app to background
and use FlutterForegroundTask.launchApp() for app to foreground that's all.
I think it helps.
if you work with flutter_local_notifications package
you can add this argument to AndroidNotificationAction
from here
hope this help (:

Listening For Device Orientation Changes In Flutter

I am looking for a way of listening for changes to the phones orientation, with the intent to hide something if the phone is Landscape.
My Layout is currently only displayed in portrait, as intended, but I want my app to do something if the device is rotated to Landscape, while keeping the layout in Portrait.
I have tried using a OrientationBuilder, but this only works if the layout changes to Landscape.
I have also tried using MediaQuery.of(context).orientation, but it continues to return portrait once the device is rotated, again only using the layouts orientation.
You can listen to screen size changes, but MediaQuery.of(...) should work as well and should cause rebuilds of your widget when orientation changes
https://stephenmann.io/post/listening-to-device-rotations-in-flutter/
import 'dart:ui';
import 'package:flutter/material.dart';
class WidthHeight extends StatefulWidget {
WidthHeight({ Key key }) : super(key: key);
#override
WidthHeightState createState() => new WidthHeightState();
}
class WidthHeightState extends State
with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
double width = 0.0;
double height = 0.0;
#override void didChangeMetrics() {
setState(() {
width = window.physicalSize.width;
height = window.physicalSize.height;
});
}
#override
Widget build(BuildContext context) {
return new Text('Width: $width, Height $height');
}
}
Using MediaQuery directly in didChangeMetrics() returns previous values. To get the latest values after orientation change. Use WidgetsBinding.instance.addPostFrameCallback() inside it.
https://github.com/flutter/flutter/issues/60899
class OrientationSample extends StatefulWidget {
const OrientationSample({Key? key}) : super(key: key);
#override
_OrientationSampleState createState() => _OrientationSampleState();
}
class _OrientationSampleState extends State<OrientationSample> with WidgetsBindingObserver {
Orientation? _currentOrientation;
#override
void initState() {
super.initState();
WidgetsBinding.instance?.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
#override
void didChangeMetrics() {
_currentOrientation = MediaQuery.of(context).orientation;
print('Before Orientation Change: $_currentOrientation');
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
setState(() {
_currentOrientation = MediaQuery.of(context).orientation;
});
print('After Orientation Change: $_currentOrientation');
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _currentOrientation != null ? Text('$_currentOrientation') : Text('Rotate Device to Test'),
),
);
}
}
You can wrap your widget with visibility and set the opacity parameter to getOpacityForOrientation() and in your Screen you can add the function:
double getOpacityForOrientation() {
if (MediaQuery.of(context).orientation == Orientation.landscape) {
return 0;
} else {
return 1;
}
}
when the orientation changes the widget will rebuild and the opacity will change and hide/show
Both OrientationBuilder and MediaQuery.of(context).orientation should be able to get the job done. But you're saying that the device's orientation never changes which makes me think that you have not enabled auto-rotate in your device.
Can you enable auto-rotate from quick settings and give it a try?
Using provider and OrientationBuilder:
orientation_provider.dart
import 'package:flutter/material.dart';
class OrientationProvider extends ChangeNotifier {
Orientation _orientation = Orientation.portrait;
Orientation get getOrientation {
return _orientation;
}
void changeOrientation(Orientation newOrientation) {
print("CHANGE ORIENTATION CALLED: old: $_orientation, new: $newOrientation");
bool hasChanged = _orientation != newOrientation;
_orientation = newOrientation;
if(hasChanged) notifyListeners();
}
}
In parent widget, use OrientationBuilder and set orientation in provider
.
.
.
child: OrientationBuilder(
builder: (context, orientation){
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
context.read<OrientationProvider>().changeOrientation(orientation);
});
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark().copyWith(
textTheme: ThemeData.dark().textTheme.apply(
fontFamily: 'Nunito',
),
.
.
.
Where orientation change need to be listened
child: context.watch<OrientationProvider>().getOrientation == Orientation.portrait ? WIDGET_PORTRAIT
: WIDGET_LANDSCAPE
So Im posting this here to try to help maybe a little, but I will be honest in that it feels like a hack. The problem that Matthew is describing is -- if you specifically lock your app to portrait mode at compile time, MediaQuery and OrientationBuilder don't help because they are never triggered. In my case, I have an app which is locked to portrait, but I am trying to add one screen that streams video, which I would like to play full screen when the phone is rotated. As above, because it is locked at compile time MediaQuery and OrientationBuilder won't work.
For my 'hack' in my screen controller, I listen to a stream subscription from the accelerometer events API. If the stream event.y is < 3, then the phone is close to being horizontal, I can then use this to change an int that controls the number of rotations for an Expanded rotating box that houses the video player.
This does not work for working out if it is right or left-handed rotation... and as I say, it feels like a hack, but it's a start...
just using code like this, if you wanna detect orientation of device
String type_orien = "potrait";
Future<void> detect_change_orientation() async {
await Future.delayed(const Duration(seconds: 1), () async {
if (MediaQuery.of(context).orientation == Orientation.landscape) {
if (type_orien ==
"potrait") // if orien change and before is potrait then reload again
{
print("landscape ss");
await reload_set_scren();
type_orien = "landscape";
}
} else {
if (type_orien ==
"landscape") // if orien change and before is landscape then reload again
{
print("potrait ss");
await reload_set_scren();
type_orien = "potrait";
}
}
});
}
Future<void> reload_set_scren() {
//... do whats ever u want
}
#override
Widget build(BuildContext context) {
detect_change_orientation(); // call function here <--
return Scaffold(
);
}