Flutter two finger gestures - android

I'm working on a launcher app with gestures settings and I'm looking for a way to achieve two-finger swipe up and down gestures, i found out that it can be achieved by using RawGestureDetector and MultiDragGestureRecognizer , but I have no idea how to do it, can anyone share a code example or explain how to do it
i tried this code sample but it doesn't seem to work :
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class TwoFingerPointerWidget extends StatelessWidget {
final Widget child;
final OnUpdate onUpdate;
TwoFingerPointerWidget({required this.child, required this.onUpdate});
#override
Widget build(BuildContext context) {
return RawGestureDetector(
gestures: <Type, GestureRecognizerFactory>{
CustomVerticalMultiDragGestureRecognizer:
GestureRecognizerFactoryWithHandlers<
CustomVerticalMultiDragGestureRecognizer>(
() => CustomVerticalMultiDragGestureRecognizer(debugOwner: null),
(CustomVerticalMultiDragGestureRecognizer instance) {
instance.onStart = (Offset position) {
return CustomDrag(events: instance.events, onUpdate: onUpdate);
};
},
),
},
child: child,
);
}
}
typedef OnUpdate(DragUpdateDetails details);
class CustomDrag extends Drag {
final List<PointerDownEvent> events;
final OnUpdate onUpdate;
CustomDrag({required this.events, required this.onUpdate});
#override
void update(DragUpdateDetails details) {
super.update(details);
final delta = details.delta;
if (delta.dy.abs() > 0 && events.length == 2) {
onUpdate.call(DragUpdateDetails(
sourceTimeStamp: details.sourceTimeStamp,
delta: Offset(0, delta.dy),
primaryDelta: details.primaryDelta,
globalPosition: details.globalPosition,
localPosition: details.localPosition,
));
}
}
#override
void end(DragEndDetails details) {
super.end(details);
}
}
class CustomVerticalMultiDragGestureRecognizer
extends MultiDragGestureRecognizer {
final List<PointerDownEvent> events = [];
CustomVerticalMultiDragGestureRecognizer({required Object? debugOwner})
: super(debugOwner: debugOwner);
#override
createNewPointerState(PointerDownEvent event) {
events.add(event);
return _CustomVerticalPointerState(event.position, onDisposeState: () {
events.remove(event);
});
}
#override
String get debugDescription => 'custom vertical multidrag';
}
typedef OnDisposeState();
class _CustomVerticalPointerState extends MultiDragPointerState {
final OnDisposeState onDisposeState;
_CustomVerticalPointerState(Offset initialPosition,
{required this.onDisposeState})
: super(initialPosition, PointerDeviceKind.touch, null);
#override
void checkForResolutionAfterMove() {
if (pendingDelta!.dy.abs() > kTouchSlop) {
resolve(GestureDisposition.accepted);
}
}
#override
void accepted(GestureMultiDragStartCallback starter) {
starter(initialPosition);
}
#override
void dispose() {
onDisposeState.call();
super.dispose();
}
}

So i just found a solution , not the best one but it still works ,for anyone looking
you have to work around the listener class , here is my code :
final events = [];
Listener(
onPointerDown: (event) {
events.add(event.pointer);
},
onPointerUp: (event) {
events.clear();
},
onPointerMove: (event) {
if (events.length == 2) {
int sensitivity = 8;
if (event.delta.dy > sensitivity) {
// code for two finger swipe up event
} else if (event.delta.dy < -sensitivity) {
// code for two finger swipe down event
}
}
},

Related

How to detect a phone shake in Flutter?

i tried to get run the ShakeDetector in my Flutter app. I got a sample app here. Can somebody help me to fix the problem?
This codesample below is from the shake.dart github example. I dont get it why it is not working.
import 'package:flutter/material.dart';
import 'package:shake/shake.dart';
void main() {
runApp(DemoPage());
}
class DemoPage extends StatefulWidget {
#override
_DemoPageState createState() => _DemoPageState();
}
var phoneShakes;
class _DemoPageState extends State<DemoPage> {
#override
void initState() {
super.initState();
ShakeDetector detector = ShakeDetector.autoStart(onPhoneShake: () {
print('shaking');
});
// To close: detector.stopListening();
// ShakeDetector.waitForStart() waits for user to call detector.startListening();
phoneShakes = detector.mShakeCount;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: AppBar(
title: Text(phoneShakes.toString()),
),
);
}
}
It's because there was no call to setState. Try this code:
class _DemoPageState extends State<DemoPage> {
late ShakeDetector _detector;
#override
void initState() {
super.initState();
_detector = ShakeDetector.autoStart(onPhoneShake: () {
setState(() {}); // Call setState every time phone shakes.
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Count: ${_detector.mShakeCount}'),
),
);
}
}

Unable to SetState() in Flutter

AppScreenC will give all the installed apps in a ListView
class AppScreenC extends StatefulWidget {
#override
_AppScreenCState createState() => _AppScreenCState();
}
List<Application> apps;
getApps() async {
if (apps == null) {
apps = await DeviceApps.getInstalledApplications(
onlyAppsWithLaunchIntent: true,
includeSystemApps: true,
includeAppIcons: true);
apps.sort((a, b) => a.appName.compareTo(b.appName));
}
}
ListView with all the installed apps are getting displayed in screen. I'm trying to change the icons based on onTap event.
But clicking on a list, icons are not changing.
class _AppScreenCState extends State<AppScreenC> {
final _app = <Application>[];
#override
Widget build(BuildContext context) {
return _buildApps();
}
Widget _buildApps() {
getApps();
return ListView.builder(itemBuilder: (BuildContext context, int index) {
_app.addAll(apps);
return _buildRow(_app[index]);
});
}
Widget _buildRow(ApplicationWithIcon app) {
bool selected = false;
return ListTile(
leading: Image.memory(app.icon, height: 40),
trailing:
Icon(selected ? Icons.check_circle : Icons.check_circle_outline),
title: Text(app.appName),
onTap: () {
selected = !selected;
// print("$selected");
// print("${app.apkFilePath}");
setState(() {});
},
);
}
}
itemCount: missing in ListView.Builder
Widget _buildApps() {
getApps();
return ListView.builder(itemBuilder: (BuildContext context, int index) {
_app.addAll(apps);
return _buildRow(_app[index]);
}, itemCount: _app.length);
}
Also,
class _AppScreenCState extends State<AppScreenC> {
bool selected = false; // this should be at the top as it will persist the value
class _AppScreenCState extends State<AppScreenC> {
bool selected = false;
Widget _buildRow(ApplicationWithIcon app) {
//bool selected = false; not here

Flutter Shake Detector doesn't detect phone's shake

I'm using Shake Flutter package in order to detect shakes on my application, but it doesn't seem to work either on my physical device (Samsung Galaxy A71) or on my Android Emulator.
Here is how I initialise the ShakeDetector:
class _HomePageState extends State<HomePage> {
bool _hasShaked;
ShakeDetector _shakeDetector;
#override
void initState() {
super.initState();
_hasShaked = false;
_shakeDetector = ShakeDetector.autoStart(
onPhoneShake: () {
print('shake')
setState(() {
_hasShaked = true;
_shakeDetector.stopListening();
});
},
);
}
#override
Widget build(BuildContext context) {
// ...
}
}
I tried to use ShakeDetector#waitForStart() instead but it doesn't work either:
class _HomePageState extends State<HomePage> {
bool _hasShaked;
ShakeDetector _shakeDetector;
#override
void initState() {
super.initState();
_hasShaked = false;
_shakeDetector = ShakeDetector.waitForStart(
onPhoneShake: () {
print('shake')
setState(() {
_hasShaked = true;
_shakeDetector.stopListening();
});
},
);
_shakeDetector.startListening();
}
#override
Widget build(BuildContext context) {
// ...
}
}
Here is my dependencies in my pubspec.yaml:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.3
shake: ^0.1.0
Thanks for the help.

Flutter: SearchDelegate Stream is empty when pressing Search

I‘m using a Stream with SearchDelegate.
When I enter something in the search field, it's working fine. But as soon as I press the search symbol, it displays nothing, because the stream is empty. How is that possible?
Here is some code:
List<Notes> notes;
...
void search() {
showSearch(
context: context,
delegate: Search(
Stream.value(UnmodifiableListView<Note>(notes)).asBroadcastStream()
)
}
class Search extends SearchDelegate{
final Stream<UnmodifiableListView<Note>> notes;
Search(this.notes);
...
#override
Widget buildResults(BuildContext context){
return _buildStreamBuilder();
}
#override
Widget buildSuggestions(BuildContext context){
return _buildStreamBuilder();
}
StreamBuilder<UnmodifiableListView<Note>> _buildStreamBuilder() {
return StreamBuilder< UnmodifiableListView<Note>> (
stream: notes
builder: (context, AsyncSnapshot< UnmodifiableListView<Note>>) {
final results = snapshot.data.where((note){
....
}
}
Why is the stream empty, when buildResults() is called, but not when buildSuggestions() is called?
Edit in the abstract class SearchDelegate:
void showResults(BuildContext context) {
_focusNode?.unfocus();
_currentBody = _SearchBody.results;
}
Replace:
void showResults(BuildContext context) {
if(_queryTextController.text.isNotEmpty) {
_focusNode?.unfocus();
_currentBody = _SearchBody.results;
}else{
_focusNode?.unfocus();
}
}
It worked for me

How to get Android Activity Lifecycle Events in Flutter Plugin? [duplicate]

Are there any Activity lifecycle methods in Flutter apps?
Like:
onCreate()
onResume()
onDestroy()
Or:
viewDidload()
viewWillAppear()
How to handle application lifecycle when making an app with Flutter?
There is a method called when the system put the app in the background or return the app to foreground named didChangeAppLifecycleState.
Example with widgets:
class _AppLifecycleReactorState extends State<AppLifecycleReactor> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
AppLifecycleState _notification;
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() { _notification = state; });
}
#override
Widget build(BuildContext context) {
return new Text('Last notification: $_notification');
}
}
Also there are CONSTANTS to know the states that an application can be in, eg:
inactive
paused
resumed
suspending
The usage of these constants would be the value of the constant e.g:
const AppLifecycleState(state)
Run the following code, press the home button and then reopen the app to see it working. There are 4 AppLifecycleState:
resumed: The application is visible and responding to user input.
inactive: The application is in an inactive state and is not receiving user input.
paused: The application is not currently visible to the user, not responding to user input, and running in the background.
detached: The application is still hosted on a flutter engine but is detached from any host views.
Null safe code:
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
print('Current state = $state');
}
#override
Widget build(BuildContext context) => Scaffold();
}
To be notified when app goes to foreground or route popped you can inherit LifecycleState class and override onResume() and onPause() methods. LifecycleState class:
/// Inherit this State to be notified of lifecycle events, including popping and pushing routes.
///
/// Use `pushNamed()` or `push()` method to track lifecycle events when navigating to another route.
abstract class LifecycleState <T extends StatefulWidget> extends State<T>
with WidgetsBindingObserver {
ResumeResult resumeResult = new ResumeResult();
bool _isPaused = false;
AppLifecycleState lastAppState = AppLifecycleState.resumed;
void onResume() {}
void onPause() {}
/// Use instead of Navigator.push(), it fires onResume() after route popped
Future<T> push<T extends Object>(BuildContext context, Route<T> route, [String source]) {
_isPaused = true;
onPause();
return Navigator.of(context).push(route).then((value) {
_isPaused = false;
resumeResult.data = value;
resumeResult.source = source;
onResume();
return value;
});
}
/// Use instead of Navigator.pushNamed(), it fires onResume() after route popped
Future<T> pushNamed<T extends Object>(BuildContext context, String routeName, {Object arguments}) {
_isPaused = true;
onPause();
return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments).then((value) {
_isPaused = false;
resumeResult.data = value;
resumeResult.source = routeName;
onResume();
return value;
});
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
if (!_isPaused) {
onPause();
}
} else if (state == AppLifecycleState.resumed &&
lastAppState == AppLifecycleState.paused) {
if (!_isPaused) {
onResume();
}
}
lastAppState = state;
}
}
class ResumeResult {
dynamic data;
String source;
}
Also make sure to start push new routes using push() or pushNamed() method.

Categories

Resources