space_story_sdk 0.0.26 copy "space_story_sdk: ^0.0.26" to clipboard
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');
        },
      ),
    );
  }
}