본문으로 이동

플러터:마이크 입력: 두 판 사이의 차이

학교의 모든 지식. SMwiki
잔글편집 요약 없음
잔글편집 요약 없음
51번째 줄: 51번째 줄:


== 데시벨 측정 예시 코드 ==
== 데시벨 측정 예시 코드 ==
마찬가지로 패키지 버전에 따라 함수명이 달라지기도 함. 오래되면 문제가 발생할 수 있음.<syntaxhighlight lang="dart">import 'dart:async';
마찬가지로 패키지 버전에 따라 함수명이 달라지기도 함. 오래되면 문제가 발생할 수 있음.
= Flutter 데시벨 측정기 학습 위키 =
 
== 프로젝트 개요 ==
* '''목표''': 실시간 마이크 입력으로 데시벨 측정
* '''난이도''': 초급 (코드 67줄)
* '''학습 시간''': 1-2시간
* '''플랫폼''': ✅ Android / ✅ iOS / ❌ Windows / ❌ Web
 
== 필요한 패키지 ==
<syntaxhighlight lang="yaml">
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  noise_meter: ^5.0.1        # 🎤 마이크 데시벨 측정
  permission_handler: ^11.3.1 # 🔒 마이크 권한 관리
</syntaxhighlight>
 
== 권한 설정 ==
 
=== Android ===
파일: <code>android/app/src/main/AndroidManifest.xml</code>
<syntaxhighlight lang="xml">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</syntaxhighlight>
 
=== iOS ===
파일: <code>ios/Runner/Info.plist</code>
<syntaxhighlight lang="xml">
<key>NSMicrophoneUsageDescription</key>
<string>데시벨 측정을 위해 마이크 권한이 필요합니다.</string>
</syntaxhighlight>
 
== 완전한 소스코드 ==
<syntaxhighlight lang="dart">
// filepath: c:\Temp\for device\decibel_app\lib\main.dart
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:noise_meter/noise_meter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:noise_meter/noise_meter.dart';
import 'dart:async';
 
void main() => runApp(MyApp());


void main() {
class MyApp extends StatelessWidget {
  runApp(const MyApp());
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '데시벨 측정기',
      home: DecibelMeter(),
    );
  }
}
}


class MyApp extends StatefulWidget {
class DecibelMeter extends StatefulWidget {
  const MyApp({super.key});
 
   @override
   @override
   State<MyApp> createState() => _MyAppState();
   _DecibelMeterState createState() => _DecibelMeterState();
}
}


class _MyAppState extends State<MyApp> {
class _DecibelMeterState extends State<DecibelMeter> {
   NoiseMeter? _noiseMeter;
   double _currentDB = 0.0;
   StreamSubscription<NoiseReading>? _subscription;
  bool _isListening = false;
  double _decibel = 0;
   StreamSubscription<NoiseReading>? _noiseSubscription;


  @override
   Future<void> _startListening() async {
  void initState() {
    super.initState();
    _initMicrophone();
  }
 
   Future<void> _initMicrophone() async {
     // 마이크 권한 요청
     // 마이크 권한 요청
     if (await Permission.microphone.request().isGranted) {
     var status = await Permission.microphone.request();
       _noiseMeter = NoiseMeter(onError: (e) {
    if (status.isGranted) {
        print("Noise meter error: $e");
       _noiseSubscription = NoiseMeter().noise.listen(
        (NoiseReading noiseReading) {
          setState(() {
            _currentDB = noiseReading.meanDecibel;
          });
        },
        onError: (error) {
          print('오류: $error');
        },
      );
      setState(() {
        _isListening = true;
       });
       });
      _subscription = _noiseMeter!.noiseStream.listen((noise) {
        setState(() {
          _decibel = noise.meanDecibel;
        });
      });
      _noiseMeter!.start();
    } else {
      print("마이크 권한 거부됨");
     }
     }
   }
   }


  @override
   void _stopListening() {
   void dispose() {
     _noiseSubscription?.cancel();
     _subscription?.cancel();
     setState(() {
     _noiseMeter?.stop();
      _isListening = false;
     super.dispose();
     });
   }
   }


   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return MaterialApp(
     return Scaffold(
       home: Scaffold(
       appBar: AppBar(
         appBar: AppBar(title: const Text("데시벨 측정기")),
         title: Text('데시벨 측정기'),
        body: Center(
      ),
           child: Text(
      body: Center(
            "${_decibel.toStringAsFixed(1)} dB",
        child: Column(
            style: const TextStyle(fontSize: 50),
          mainAxisAlignment: MainAxisAlignment.center,
           ),
           children: [
            Text(
              '${_currentDB.toInt()} dB',
              style: TextStyle(fontSize: 48),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _isListening ? _stopListening : _startListening,
              child: Text(_isListening ? '정지' : '시작'),
            ),
           ],
         ),
         ),
       ),
       ),
     );
     );
   }
   }
}</syntaxhighlight>
}
</syntaxhighlight>
 
== 코드 구조 분석 ==
 
=== 핵심 클래스들 ===
<syntaxhighlight lang="dart">
// 📊 데시벨 읽기
NoiseMeter().noise.listen((NoiseReading reading) {
  double db = reading.meanDecibel; // 평균 데시벨
});
 
// 🔒 권한 요청
Permission.microphone.request();
 
// 🎛️ 스트림 제어
StreamSubscription<NoiseReading>? subscription;
</syntaxhighlight>
 
=== 상태 관리 ===
<syntaxhighlight lang="dart">
class _DecibelMeterState extends State<DecibelMeter> {
  double _currentDB = 0.0;                      // 현재 데시벨 값
  bool _isListening = false;                    // 측정 중인지 상태
  StreamSubscription<NoiseReading>? _noiseSubscription;  // 스트림 구독 객체
}
</syntaxhighlight>
 
== 동작 흐름 ==
# 앱 시작
# 마이크 권한 요청
# 권한 허용 시 NoiseMeter 시작
# 실시간 데시벨 스트림 수신
# UI 업데이트 (setState)
# 정지 버튼으로 스트림 취소
 
== 주요 학습 포인트 ==
 
=== 1. 스트림(Stream) 패턴 ===
<syntaxhighlight lang="dart">
// 스트림 구독
_subscription = NoiseMeter().noise.listen(
  (data) => setState(() => _currentDB = data.meanDecibel),
  onError: (error) => print('에러: $error'),
);
 
// 메모리 누수 방지
_subscription?.cancel();
</syntaxhighlight>
 
=== 2. 권한 관리 ===
<syntaxhighlight lang="dart">
Future<void> _requestPermission() async {
  var status = await Permission.microphone.request();
  if (status.isGranted) {
    // 권한 허용됨
  } else if (status.isDenied) {
    // 권한 거부됨
  } else if (status.isPermanentlyDenied) {
    // 영구 거부됨 - 설정으로 유도
    openAppSettings();
  }
}
</syntaxhighlight>
 
=== 3. 생명주기 관리 ===
<syntaxhighlight lang="dart">
@override
void dispose() {
  _noiseSubscription?.cancel(); // 위젯 해제 시 스트림 정리
  super.dispose();
}
</syntaxhighlight>
 
== 데시벨 참고값 ==
{| class="wikitable"
! 데시벨(dB) !! 소음 수준 !! 예시
|-
| 0-20 || 매우 조용 || 도서관, 속삭임
|-
| 20-40 || 조용 || 조용한 사무실
|-
| 40-60 || 보통 || 일반 대화
|-
| 60-80 || 시끄러움 || TV, 음악
|-
| 80-100 || 매우 시끄러움 || 지하철, 공사장
|-
| 100+ || 위험 || 콘서트, 제트기
|}
 
== UI 개선 아이디어 ==
 
=== 데시벨 수준별 색상 ===
<syntaxhighlight lang="dart">
Color _getDecibelColor(double db) {
  if (db < 40) return Colors.green;      // 조용
  if (db < 70) return Colors.yellow;    // 보통
  return Colors.red;                    // 시끄러움
}
</syntaxhighlight>
 
=== 프로그레스 바 ===
<syntaxhighlight lang="dart">
LinearProgressIndicator(
  value: _currentDB / 120.0, // 120dB 기준
  backgroundColor: Colors.grey[300],
  valueColor: AlwaysStoppedAnimation(_getDecibelColor(_currentDB)),
)
</syntaxhighlight>
 
== 트러블슈팅 ==
 
=== 권한 문제 ===
<syntaxhighlight lang="dart">
// 권한 상태 확인
var status = await Permission.microphone.status;
if (status.isPermanentlyDenied) {
  // 사용자를 설정으로 유도
  showDialog(...);
}
</syntaxhighlight>
 
=== 플랫폼별 이슈 ===
* '''Android''': <code>RECORD_AUDIO</code> 권한 필수
* '''iOS''': <code>Info.plist</code>에 사용 목적 명시 필수
* '''Windows/Web''': <code>noise_meter</code> 패키지 미지원
 
=== 성능 최적화 ===
<syntaxhighlight lang="dart">
// 백그라운드에서 정지
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.paused) {
    _stopListening(); // 배터리 절약
  }
}
</syntaxhighlight>
 
== 플랫폼 지원 현황 ==
{| class="wikitable"
! 플랫폼 !! 지원 여부 !! 이유
|-
| '''Android''' || ✅ || 완전 지원
|-
| '''iOS''' || ✅ || 완전 지원
|-
| '''Windows''' || ❌ || noise_meter 미지원
|-
| '''Web''' || ❌ || 마이크 API 제한
|-
| '''macOS''' || ❓ || 테스트 필요
|-
| '''Linux''' || ❓ || 테스트 필요
|}
 
== 다음 단계 학습 ==
 
=== 중급 기능 ===
* 📈 실시간 그래프 (charts_flutter)
* 💾 측정값 저장 (shared_preferences)
* 🔔 임계값 알림 (flutter_local_notifications)
 
=== 고급 기능 ===
* 🎵 주파수 분석 (FFT)
* 📊 통계 분석 (평균, 최대, 최소)
* 🌐 데이터 공유 (Firebase)
 
== 완성된 기능 ==
* ✅ 실시간 데시벨 측정
* ✅ 마이크 권한 관리
* ✅ 간단한 시작/정지 제어
* ✅ 크로스 플랫폼 (Android/iOS)
* ✅ 메모리 안전 (스트림 정리)
 
이 프로젝트로 '''Flutter의 스트림, 권한, 네이티브 패키지 사용법'''을 모두 학습할 수 있습니다! 🎉

2025년 11월 19일 (수) 05:48 판

틀:플러터 Dart:개요 플러터에 대한 지식 분류

  1. 플러터:개요
    1. 플러터:VSCode
    2. 플러터:안드로이드 스튜디오
  2. 플러터:실행
  3. 플러터:개념 잡기
    1. 플러터:화면 하나 만들기
    2. 플러터:변하는 화면(StatefulWidget)
    3. 플러터:화면 전환(화면 쌓기, 하단 네비게이션 바)
    4. 플러터:화면 전환(Drawer)
    5. 플러터:입력 관련
      1. 플러터:버튼
      2. 플러터:키보드 입력
      3. 플러터:슬라이더
    6. 플러터:그래프 그리기(fl chart)
    7. 플러터:데이터 저장(간단한 데이터)
    8. 플러터:인증(Firebase 인증)(미완)
    9. 플러터:인증(OAuth2)(미완)
  4. 권한 사용
    1. 플러터:마이크 입력
  5. 위젯
    1. 플러터:아이콘
    2. 플러터:레이아웃 계열 위젯
    3. 플러터:네비게이션 계열 위젯
    4. 플러터:버튼
    5. 플러터:상태관리(미완)
  6. 플러터:DB연결
    1. 플러터:Firebase(미완)
    2. 플러터:MySQL(미완)
  7. 디자인
    1. 플러터:테마
    2. 플러터:앱바
  8. 플러터:배포
    1. 플러터:배포(안드로이드)(미완)
  9. 플러터:참고자료
  10. 플러터:위젯
    1. 플러터:공간배치용 위젯
  11. 플러터:구글 AdMob(미완)
  12. 플러터:라이브러리
    1. 플러터:logger

개요

기기에서 마이크를 사용하는 법.

사전준비 권한 설정

권한 부여

항목 설명 비고
안드로이드 android/app/src/main/AndroidManifest.xml 에 넣는다.
  • 권한은 <application> 태그 밖, <manifest> 태그 안에 위치해야 함.
  • <uses-permission android:name="android.permission.RECORD_AUDIO"/>
아이폰 ios/Runner/Info.plist에 넣는다. <key>NSMicrophoneUsageDescription</key>

<string>앱에서 음성 입력을 사용합니다.</string>

Windows,

Web

별도 설정 불필요. 자동으로 팝업이 뜸.

필요한 패키지 점검

항목 설명 비고
패키지 설치 dependencies:

  flutter:

    sdk: flutter

permission_handler: ^11.0.0

noise_meter: ^5.0.2

noise_meter는 db를 읽는다.

아마 시간이 지나면 오래된 버전이라 문제가 발생할 수 있음. flutter pub get

데시벨 측정 예시 코드

마찬가지로 패키지 버전에 따라 함수명이 달라지기도 함. 오래되면 문제가 발생할 수 있음.

Flutter 데시벨 측정기 학습 위키

프로젝트 개요

  • 목표: 실시간 마이크 입력으로 데시벨 측정
  • 난이도: 초급 (코드 67줄)
  • 학습 시간: 1-2시간
  • 플랫폼: ✅ Android / ✅ iOS / ❌ Windows / ❌ Web

필요한 패키지

# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  noise_meter: ^5.0.1        # 🎤 마이크 데시벨 측정
  permission_handler: ^11.3.1 # 🔒 마이크 권한 관리

권한 설정

Android

파일: android/app/src/main/AndroidManifest.xml

<uses-permission android:name="android.permission.RECORD_AUDIO" />

iOS

파일: ios/Runner/Info.plist

<key>NSMicrophoneUsageDescription</key>
<string>데시벨 측정을 위해 마이크 권한이 필요합니다.</string>

완전한 소스코드

// filepath: c:\Temp\for device\decibel_app\lib\main.dart
import 'package:flutter/material.dart';
import 'package:noise_meter/noise_meter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:async';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '데시벨 측정기',
      home: DecibelMeter(),
    );
  }
}

class DecibelMeter extends StatefulWidget {
  @override
  _DecibelMeterState createState() => _DecibelMeterState();
}

class _DecibelMeterState extends State<DecibelMeter> {
  double _currentDB = 0.0;
  bool _isListening = false;
  StreamSubscription<NoiseReading>? _noiseSubscription;

  Future<void> _startListening() async {
    // 마이크 권한 요청
    var status = await Permission.microphone.request();
    if (status.isGranted) {
      _noiseSubscription = NoiseMeter().noise.listen(
        (NoiseReading noiseReading) {
          setState(() {
            _currentDB = noiseReading.meanDecibel;
          });
        },
        onError: (error) {
          print('오류: $error');
        },
      );
      setState(() {
        _isListening = true;
      });
    }
  }

  void _stopListening() {
    _noiseSubscription?.cancel();
    setState(() {
      _isListening = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('데시벨 측정기'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '${_currentDB.toInt()} dB',
              style: TextStyle(fontSize: 48),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _isListening ? _stopListening : _startListening,
              child: Text(_isListening ? '정지' : '시작'),
            ),
          ],
        ),
      ),
    );
  }
}

코드 구조 분석

핵심 클래스들

// 📊 데시벨 읽기
NoiseMeter().noise.listen((NoiseReading reading) {
  double db = reading.meanDecibel; // 평균 데시벨
});

// 🔒 권한 요청
Permission.microphone.request();

// 🎛️ 스트림 제어
StreamSubscription<NoiseReading>? subscription;

상태 관리

class _DecibelMeterState extends State<DecibelMeter> {
  double _currentDB = 0.0;                      // 현재 데시벨 값
  bool _isListening = false;                    // 측정 중인지 상태
  StreamSubscription<NoiseReading>? _noiseSubscription;  // 스트림 구독 객체
}

동작 흐름

  1. 앱 시작
  2. 마이크 권한 요청
  3. 권한 허용 시 NoiseMeter 시작
  4. 실시간 데시벨 스트림 수신
  5. UI 업데이트 (setState)
  6. 정지 버튼으로 스트림 취소

주요 학습 포인트

1. 스트림(Stream) 패턴

// 스트림 구독
_subscription = NoiseMeter().noise.listen(
  (data) => setState(() => _currentDB = data.meanDecibel),
  onError: (error) => print('에러: $error'),
);

// 메모리 누수 방지
_subscription?.cancel();

2. 권한 관리

Future<void> _requestPermission() async {
  var status = await Permission.microphone.request();
  if (status.isGranted) {
    // 권한 허용됨
  } else if (status.isDenied) {
    // 권한 거부됨
  } else if (status.isPermanentlyDenied) {
    // 영구 거부됨 - 설정으로 유도
    openAppSettings();
  }
}

3. 생명주기 관리

@override
void dispose() {
  _noiseSubscription?.cancel(); // 위젯 해제 시 스트림 정리
  super.dispose();
}

데시벨 참고값

데시벨(dB) 소음 수준 예시
0-20 매우 조용 도서관, 속삭임
20-40 조용 조용한 사무실
40-60 보통 일반 대화
60-80 시끄러움 TV, 음악
80-100 매우 시끄러움 지하철, 공사장
100+ 위험 콘서트, 제트기

UI 개선 아이디어

데시벨 수준별 색상

Color _getDecibelColor(double db) {
  if (db < 40) return Colors.green;      // 조용
  if (db < 70) return Colors.yellow;     // 보통
  return Colors.red;                     // 시끄러움
}

프로그레스 바

LinearProgressIndicator(
  value: _currentDB / 120.0, // 120dB 기준
  backgroundColor: Colors.grey[300],
  valueColor: AlwaysStoppedAnimation(_getDecibelColor(_currentDB)),
)

트러블슈팅

권한 문제

// 권한 상태 확인
var status = await Permission.microphone.status;
if (status.isPermanentlyDenied) {
  // 사용자를 설정으로 유도
  showDialog(...);
}

플랫폼별 이슈

  • Android: RECORD_AUDIO 권한 필수
  • iOS: Info.plist에 사용 목적 명시 필수
  • Windows/Web: noise_meter 패키지 미지원

성능 최적화

// 백그라운드에서 정지
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.paused) {
    _stopListening(); // 배터리 절약
  }
}

플랫폼 지원 현황

플랫폼 지원 여부 이유
Android 완전 지원
iOS 완전 지원
Windows noise_meter 미지원
Web 마이크 API 제한
macOS 테스트 필요
Linux 테스트 필요

다음 단계 학습

중급 기능

  • 📈 실시간 그래프 (charts_flutter)
  • 💾 측정값 저장 (shared_preferences)
  • 🔔 임계값 알림 (flutter_local_notifications)

고급 기능

  • 🎵 주파수 분석 (FFT)
  • 📊 통계 분석 (평균, 최대, 최소)
  • 🌐 데이터 공유 (Firebase)

완성된 기능

  • ✅ 실시간 데시벨 측정
  • ✅ 마이크 권한 관리
  • ✅ 간단한 시작/정지 제어
  • ✅ 크로스 플랫폼 (Android/iOS)
  • ✅ 메모리 안전 (스트림 정리)

이 프로젝트로 Flutter의 스트림, 권한, 네이티브 패키지 사용법을 모두 학습할 수 있습니다! 🎉