[Flutter] 만보기 1 라이브러리

류재성's avatar
May 18, 2024
[Flutter] 만보기 1 라이브러리
 
 

1. 라이브러리 설치

pubspec.yaml
notion image
 
 
라이브러리 설치 후 pub get 을 누른다.
 

2. 라이브러리를 쓰기 전에 permission_handler 라이브러리를 먼저 추가해야 함 (권한 추가)

 
notion image
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" /> <application android:label="project_app" android:name="${applicationName}" android:icon="@mipmap/ic_launcher"> <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <!-- Specifies an Android theme to apply to this Activity as soon as the Android process has started. This theme is visible to the user while the Flutter UI initializes. After that, this theme continues to determine the Window background behind the Flutter UI. --> <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- Don't delete the meta-data below. This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> <meta-data android:name="flutterEmbedding" android:value="2" /> </application> <!-- Required to query activities that can process text, see: https://developer.android.com/training/package-visibility?hl=en and https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT. In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. --> <queries> <intent> <action android:name="android.intent.action.PROCESS_TEXT"/> <data android:mimeType="text/plain"/> </intent> </queries> </manifest>
notion image
💡
아래의 파일에 넣지 않게 주의!
notion image
 

3. 권한을 넣고 싶은 곳에 적용하기 앱이 시작할 때 넣으면 좋음 나는 로그인 직후에 넣음

 
main_page.dart
import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; import '_common/bottom_navigation_bar.dart'; import 'main/activity/activity_page.dart'; import 'main/challenge/challenge_page.dart'; import 'main/my/my_page.dart'; import 'main/today/today_page.dart'; class MainPage extends StatefulWidget { final int index; MainPage(this.index); @override State<MainPage> createState() => _MainPageState(index); } class _MainPageState extends State<MainPage> { int _selectedIndex = 0; _MainPageState(this._selectedIndex); @override void initState() { super.initState(); _requestActivityRecognitionPermission(); } Future<void> _requestActivityRecognitionPermission() async { var status = await Permission.activityRecognition.status; if (!status.isGranted) { var result = await Permission.activityRecognition.request(); _handlePermissionStatus(result); } } void _handlePermissionStatus(PermissionStatus status) { if (status.isGranted) { // 권한이 허용된 경우 print("Permission granted"); } else { if (status.isPermanentlyDenied) { // '다시 묻지 않기' 선택 후 거부 _showSettingsDialog(); } else { // 일반 거부 _showPermissionDialog(); } } } void _showSettingsDialog() { showDialog( context: context, builder: (context) => AlertDialog( title: Text("권한 필요"), content: Text("이 앱은 활동 인식 권한이 필요합니다. 설정에서 권한을 허용해주세요."), actions: <Widget>[ TextButton( child: Text("설정으로 이동"), onPressed: () { Navigator.of(context).pop(); openAppSettings(); }, ), TextButton( child: Text("취소"), onPressed: () => Navigator.of(context).pop(), ), ], ), ); } void _showPermissionDialog() { showDialog( context: context, builder: (context) => AlertDialog( title: Text("권한 필요"), content: Text("이 앱은 활동 인식 권한이 필요합니다. 권한을 허용해주세요."), actions: <Widget>[ TextButton( child: Text("허용"), onPressed: () { Navigator.of(context).pop(); _requestActivityRecognitionPermission(); // 권한 요청 다시 시도 }, ), TextButton( child: Text("취소"), onPressed: () => Navigator.of(context).pop(), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( body: IndexedStack( index: _selectedIndex, children: [ TodayPage(), ActivityPage(), ChallengePage(), MyPage() ], ), bottomNavigationBar: CustomBottomNavigationBar( selectedIndex: _selectedIndex, onItemSelected: (index) { setState(() { _selectedIndex = index; }); }, ), ); } }
 
💡
적용 순서
상태관리 클래스에 걸음수 데이터를 유지하고 업데이트하는 로직 추가
스트림을 통해 걸음수 데이터를 받아 변수에 저장 후 업데이트
 
 

4. 라이브러리 적용

 
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:pedometer/pedometer.dart'; import '../../../../_core/constants/constants.dart'; import '../widgets/step_count_body.dart'; class StepCountDetailPage extends StatefulWidget { @override _StepCountDetailPageState createState() => _StepCountDetailPageState(); } class _StepCountDetailPageState extends State<StepCountDetailPage> { int _currentSteps = 0; late StreamSubscription<StepCount> _stepCountStream; @override void initState() { super.initState(); _initializePedometer(); } void _initializePedometer() { _stepCountStream = Pedometer.stepCountStream.listen( _onStepCount, onError: _onStepCountError, ); } void _onStepCount(StepCount event) { setState(() { _currentSteps = event.steps; }); } void _onStepCountError(dynamic error) { print("Step Count Error: $error"); } @override void dispose() { _stepCountStream.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( title: Text('걸음 수', style: TextStyle(color: Colors.white)), backgroundColor: kAccentColor2, iconTheme: IconThemeData(color: Colors.white), bottom: TabBar( tabs: [ Tab(text: 'Day'), Tab(text: 'Week'), Tab(text: 'Month'), ], indicatorColor: Colors.white, labelColor: Colors.white, ), ), body: StepCountBody(currentSteps: _currentSteps), ), ); } }
 
StepCountBody - currentSteps를 받아 전달
import 'package:flutter/material.dart'; import 'activity_stats_row.dart'; import 'day_tab.dart'; import 'month_tab.dart'; import 'week_tab.dart'; import 'step_count_progress.dart'; class StepCountBody extends StatelessWidget { final int currentSteps; StepCountBody({required this.currentSteps}); @override Widget build(BuildContext context) { return Column( children: <Widget>[ SizedBox(height: 20), StepCountProgress(currentSteps: currentSteps), ActivityStatsRow(), Expanded( flex: 3, child: TabBarView( children: [ DayTab(), WeekTab(), MonthTab(), ], ), ), ], ); } }
 
StepCountProgress - currentSteps를 받아 걸음 수 표시
import 'package:flutter/material.dart'; import '../widgets/buffer_progress_widget.dart'; class StepCountProgress extends StatelessWidget { final int currentSteps; StepCountProgress({required this.currentSteps}); @override Widget build(BuildContext context) { int goalSteps = 10000; // 예시 목표 걸음 수 double progressPercentage = currentSteps / goalSteps * 100; return Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text( '목표의 ${progressPercentage.toStringAsFixed(1)}%를 달성했어요!', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), ), BufferProgressWidget(goalSteps: goalSteps, currentSteps: currentSteps), ], ); } }
 
notion image
Share article

{CODE-RYU};