| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 |
- ar vr 차이
- 멤버변수
- jvm이란
- 2022 플러터 안드로이드 스튜디오
- 스택 큐 차이
- 안드로이드 유닛 테스트 예시
- 서비스 vs 쓰레드
- 스택 자바 코드
- 자바 다형성
- Rxjava Observable
- rxjava hot observable
- 안드로이드 유닛테스트란
- rxjava cold observable
- 클래스
- 안드로이드 레트로핏 crud
- android ar 개발
- 플러터 설치 2022
- 안드로이드 유닛 테스트
- 2022 플러터 설치
- jvm 작동 원리
- rxjava disposable
- 큐 자바 코드
- 서비스 쓰레드 차이
- android retrofit login
- 안드로이드 라이선스 종류
- ANR이란
- 안드로이드 라이선스
- 안드로이드 os 구조
- 안드로이드 레트로핏 사용법
- 객체
- Today
- Total
나만을 위한 블로그
[Flutter] 핸드폰 흔들기 감지 - shake 라이브러리 사용법 본문
플러터 앱 구현 중 핸드폰을 흔들었음을 감지해야 하는 경우 shake 라이브러리로 구현할 수 있다.
아래는 pub.dev 링크와 예시다.
https://pub.dev/packages/shake
shake | Flutter package
A flutter package to detect phone shakes. Adjustable G-force and reset periods.
pub.dev
flutter pub add shake
추가만 하면 별도의 설정 없이 바로 흔들기를 감지할 수 있다.
아래는 pub.dev에 있는 예시다.
import 'package:flutter/material.dart';
import 'package:shake/shake.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Shake 예시",
theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
home: const ShakeDemo(),
);
}
}
class ShakeDemo extends StatefulWidget {
const ShakeDemo({super.key});
@override
State<ShakeDemo> createState() => _ShakeDemoState();
}
class _ShakeDemoState extends State<ShakeDemo> {
ShakeDetector? _shakeDetector;
String _lastShakeInfo = "아직 안 흔듦";
double _shakeThreshold = 2.7;
bool _useFilter = false;
int _minShakeCount = 1;
@override
void initState() {
super.initState();
_startDetector();
}
void _startDetector() {
_shakeDetector?.stopListening();
_shakeDetector = ShakeDetector.autoStart(
onPhoneShake: (ShakeEvent event) {
setState(() {
_lastShakeInfo =
"흔들기 감지!!!\n"
"방향 : ${event.direction}\n"
"힘 : ${event.force.toStringAsFixed(2)}\n"
"시간 : ${event.timestamp.toString()}";
});
},
minimumShakeCount: _minShakeCount,
shakeSlopTimeMS: 500,
shakeCountResetTime: 3_000,
shakeThresholdGravity: _shakeThreshold,
useFilter: _useFilter,
);
}
@override
void dispose() {
_shakeDetector?.stopListening();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Shake 테스트")),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"마지막 흔들기 정보 :",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
const SizedBox(height: 8),
Text(_lastShakeInfo),
],
),
),
),
const SizedBox(height: 24),
const Text(
"Settings : ",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
const SizedBox(height: 8),
ListTile(
title: const Text("민감도"),
subtitle: Slider(
value: _shakeThreshold,
min: 1.0,
max: 5.0,
divisions: 20,
label: _shakeThreshold.toStringAsFixed(1),
onChanged: (value) {
setState(() {
_shakeThreshold = value;
});
},
),
trailing: Text('${_shakeThreshold.toStringAsFixed(1)} g'),
),
SwitchListTile(
title: const Text("노이즈 필터 사용하기"),
subtitle: const Text("가속도 데이터를 부드럽게 처리"),
value: _useFilter,
onChanged: (value) {
setState(() {
_useFilter = value;
});
},
),
ListTile(
title: const Text("최소 흔들기 카운트"),
subtitle: Slider(
value: _minShakeCount.toDouble(),
min: 1,
max: 5,
divisions: 4,
label: _minShakeCount.toString(),
onChanged: (value) {
setState(() {
_minShakeCount = value.toInt();
});
},
),
trailing: Text('$_minShakeCount'),
),
const SizedBox(height: 24),
Center(
child: ElevatedButton(
onPressed: _startDetector,
child: const Text("설정 적용하기"),
),
),
const Spacer(),
const Center(
child: Text(
"기기를 흔들어 보세요",
style: TextStyle(
fontStyle: FontStyle.italic,
color: Colors.grey,
),
),
),
],
),
),
);
}
}
이걸 빌드하면 아래 화면이 표시된다.

화면이 표시되자 즉시 흔들어도 반영되지 않는다. 설정 적용하기 버튼을 누르고 좌우로 몇 번 흔들면 아래 화면이 나온다.

x, y, z축으로 흔들면 방향이 흔든 축으로 바뀌고 힘, 시간도 계속 바뀐다.
이제 shake 라이브러리로 흔들기를 어떻게 감지하는지 확인한다.
@override
void initState() {
super.initState();
_startDetector();
}
void _startDetector() {
_shakeDetector?.stopListening();
_shakeDetector = ShakeDetector.autoStart(
onPhoneShake: (ShakeEvent event) {
setState(() {
_lastShakeInfo =
"흔들기 감지!!!\n"
"방향 : ${event.direction}\n"
"힘 : ${event.force.toStringAsFixed(2)}\n"
"시간 : ${event.timestamp.toString()}";
});
},
minimumShakeCount: _minShakeCount,
shakeSlopTimeMS: 500,
shakeCountResetTime: 3_000,
shakeThresholdGravity: _shakeThreshold,
useFilter: _useFilter,
);
}
@override
void dispose() {
_shakeDetector?.stopListening();
super.dispose();
}
stateful 위젯을 쓰기 때문에 흔들기 감지에 사용할 ShakeDetector를 초기화하기 위해 initState를 썼다.
private 함수인 startDetector를 호출하는데 이 함수 안에서 이미 흔들기를 감지하고 있던 디텍터가 있다면 없앤 후 가속도계 이벤트를 수신하는 startListening()을 호출하는 autoStart()로 초기화한다. 이렇게 해서 _shakeDetector 인스턴스를 만들면서 동시에 흔들기를 감지한다.
이 방식은 내가 startListening()을 호출하지 않아도 자동으로 startListening()을 호출하는데 수동으로 startListening()을 호출하려면 waitForStart()로 바꾸고 원하는 위치에서 startListening()을 호출하면 된다.
그리고 흔들 때마다 언제 어떤 방향으로 얼마나 세게 흔들었는지 보기 위해 ShakeEvent 객체에서 값을 받아온다.
ShakeEvent는 onPhoneShake에서 처리하며 여기서 direction 같은 값으로 방향, 힘, 흔든 시간을 받아온다.
minimumShakeCount는 콜백을 발생시키기 위한 최소 흔들기 횟수를 의미한다. 기본값은 1이고 앱에서 1~5까지 중 하나를 선택할 수 있다.
shakeSlopTimeMS는 흔들기 이벤트 사이의 최소 간격을 의미한다. 첫 흔들기와 다음 흔들기 사이의 간격을 말하는데 흔들림 1번이 여러 이벤트로 감지되는 걸 막기 위한 값이다.
shakeCountResetTime은 몇 초 동안 흔들지 않으면 카운트를 리셋할지 정하는 시간이다. shakeThresholdGravity는 2.7로 설정됐는데 흔들기로 인식하는 임계값을 말한다. 낮을수록 약한 흔들림도 감지하고 높을수록 강한 흔들림만 감지한다.
useFilter는 노이즈를 줄이기 위해 필터링된 접근 방식을 사용할지 여부다. 가속도 센서 데이터에 노이즈 필터를 적용할지 여부를 결정한다. true면 센서 데이터를 부드럽게 처리해서 이동 평균 필터를 적용하고 false면 센서 원시 데이터를 그대로 쓴다. 뭔 소린지 ai에 물어보니 최근 5개의 평균을 계산해서 정제된 값을 쓴다고 한다. 그냥 날 것 그대로의 값을 쓰겠다면 false로 설정하고 좀 다듬어서 쓰겠다면 true로 설정한다.
'Flutter' 카테고리의 다른 글
| [Flutter] fatal: detected ownership in repository at '플러터 설치 경로' 에러 해결 (0) | 2025.11.15 |
|---|---|
| [Flutter] 리스트뷰 구현 시 shrinkWrap 프로퍼티를 꼭 써야 하는가? (0) | 2025.10.26 |
| [Flutter] typedef란? (0) | 2025.10.18 |
| [Flutter] GlobalKey란? 유효성 검사 예시 (0) | 2024.10.20 |
| [Flutter] BottomSheet 사용법 (0) | 2024.09.21 |
