space_story_sdk 0.0.26
space_story_sdk: ^0.0.26 copied to clipboard
A Flutter plugin for AR functionality using ARKit on iOS.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:space_story_sdk/space_story_sdk.dart';
import 'package:space_story_sdk/src/ar/data/ar_channel.dart';
import 'package:space_story_sdk/src/ar/data/ar_provider.dart';
import 'package:space_story_sdk_example/screens/ble_example_screen.dart';
import 'package:space_story_sdk_example/screens/voice_example_screen.dart';
import 'package:space_story_sdk_example/screens/pedometer_example_screen.dart';
import 'package:space_story_sdk_example/screens/camera_mission_example_screen.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
// ARProvider 명시적 초기화
ARProvider(); // 팩토리 생성자를 통해 초기화
// AR 뷰 팩토리 초기화
// SpaceStorySdk.registerARViewFactory();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Space Story SDK Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Space Story SDK 예제'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SpaceStorySdk.arView(
onCapture: (capturedCount) {
Navigator.pop(context); // AR 화면에서 돌아가기
// SpaceStorySdk.disposeARResources(); // AR 리소스 정리
},
),
),
);
},
child: const Text('AR 예제'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const BleExampleScreen(),
),
);
},
child: const Text('BLE 장치 검색 예제'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const VoiceExampleScreen(),
),
);
},
child: const Text('음성 인식 예제'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PedometerExampleScreen(),
),
);
},
child: const Text('걸음 수 확인 예제'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CameraMissionExampleScreen(),
),
);
},
child: const Text('카메라 미션 예제'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ARTiger5ExampleWrapper(),
),
);
},
child: const Text('AR Tiger5 예제'),
),
],
),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ARChannel _arChannel = ARChannel();
bool _isPeriodicEventsRunning = false;
@override
void initState() {
super.initState();
// 주기적 이벤트 스트림 리스너 설정
_arChannel.getPeriodicEventStream().listen((event) {
print('📬 받은 이벤트: $event');
}, onError: (error) {
print('❌ 이벤트 스트림 오류: $error');
});
}
void _togglePeriodicEvents() async {
if (_isPeriodicEventsRunning) {
await _arChannel.stopPeriodicEvents();
} else {
await _arChannel.startPeriodicEvents();
}
setState(() {
_isPeriodicEventsRunning = !_isPeriodicEventsRunning;
});
}
/// AR 리소스 완전 정리
void _disposeARResources() async {
print('🧹 AR 리소스 정리 시작...');
try {
final success = await SpaceStorySdk.disposeARResources();
if (success) {
print('✅ AR 리소스 정리 성공');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('✅ AR 리소스가 성공적으로 정리되었습니다'),
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
),
);
} else {
print('❌ AR 리소스 정리 실패');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('❌ AR 리소스 정리에 실패했습니다'),
backgroundColor: Colors.red,
duration: Duration(seconds: 2),
),
);
}
} catch (e) {
print('❌ AR 리소스 정리 중 오류: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('❌ AR 리소스 정리 중 오류: $e'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Space Story SDK Example'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Space Story SDK를 사용한 AR 데모',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
const SizedBox(height: 40),
// 주기적 이벤트 토글 버튼
ElevatedButton(
onPressed: _togglePeriodicEvents,
style: ElevatedButton.styleFrom(
backgroundColor: _isPeriodicEventsRunning ? Colors.red : Colors.blue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
),
child: Text(_isPeriodicEventsRunning ? '주기적 이벤트 중지' : '주기적 이벤트 시작', style: const TextStyle(fontSize: 22)),
),
// AR 게임 시작 버튼
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.of(context)
.push(
MaterialPageRoute(
builder: (context) => SpaceStorySdk.arView(
onCapture: (capturedCount) {
// 포획 성공 시 실행되는 콜백
print('🎉 포획 성공! 총 $capturedCount마리 잡았습니다!');
// 스낵바로 알림
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('🎉 포획 성공! 총 $capturedCount마리'),
backgroundColor: Colors.green,
duration: const Duration(seconds: 2),
),
);
},
),
),
)
.then((_) {
// AR 화면에서 돌아왔을 때 리소스 정리
// _disposeARResources();
});
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
),
child: const Text('AR 게임 시작', style: TextStyle(fontSize: 22)),
),
// AR 리소스 정리 버튼
const SizedBox(height: 20),
ElevatedButton(
onPressed: _disposeARResources,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
),
child: const Text('AR 리소스 정리', style: TextStyle(fontSize: 18)),
),
],
),
),
);
}
}
/// AR Tiger5 예제를 위한 래퍼 클래스
class ARTiger5ExampleWrapper extends StatefulWidget {
const ARTiger5ExampleWrapper({super.key});
@override
State<ARTiger5ExampleWrapper> createState() => _ARTiger5ExampleWrapperState();
}
class _ARTiger5ExampleWrapperState extends State<ARTiger5ExampleWrapper> {
String _statusMessage = '사진을 촬영하고 갤러리에 저장해보세요!';
Color _statusColor = Colors.blue;
int _savedPhotoCount = 0;
void _onPhotoSaved(String imagePath, bool success) {
setState(() {
if (success) {
_savedPhotoCount++;
_statusMessage = '✅ 사진 저장 성공! (총 $_savedPhotoCount장)\n경로: ${imagePath.split('/').last}';
_statusColor = Colors.green;
} else {
_statusMessage = '❌ 사진 저장 실패\n경로: ${imagePath.split('/').last}';
_statusColor = Colors.red;
}
});
// 콘솔에도 출력
if (success) {
print('✅ 사진이 갤러리에 저장됨: $imagePath');
} else {
print('❌ 사진 저장 실패: $imagePath');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AR Tiger5 예제'),
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(60),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
color: _statusColor.withOpacity(0.1),
child: Text(
_statusMessage,
style: TextStyle(
color: _statusColor,
fontSize: 14,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
),
),
body: SpaceStorySdk.arTiger5View(
onPhotoSaved: (imagePath, success) {
print('🎉 사진 저장 성공: $imagePath');
},
),
);
}
}