본문 바로가기

06. 앱

00016. [APP-00002] 사진 기반 식물/동물 인식 앱 개발기 #7

반응형

#7. 내 도감 저장 기능 만들기 (SharedPreferences 연동)

이번에는 사용자가 인식한 결과를 로컬에 저장해서 나만의 도감을 만들 수 있도록 해볼게. Flutter에서 간단한 데이터 저장엔 SharedPreferences가 유용해.


✅ 1단계: SharedPreferences 패키지 추가

pubspec.yaml에 의존성 추가:

dependencies:
  shared_preferences: ^2.2.2

명령어: flutter pub get


📦 2단계: 저장 기능 구현

lib/services/storage_service.dart 파일 생성:

import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';

class StorageService {
  static const String key = 'plant_records';

  static Future<void> saveRecord(Map<String, dynamic> data) async {
    final prefs = await SharedPreferences.getInstance();
    final List<String> current = prefs.getStringList(key) ?? [];
    current.add(jsonEncode(data));
    await prefs.setStringList(key, current);
  }

  static Future<List<Map<String, dynamic>>> loadRecords() async {
    final prefs = await SharedPreferences.getInstance();
    final List<String> raw = prefs.getStringList(key) ?? [];
    return raw.map((item) => jsonDecode(item)).cast<Map<String, dynamic>>().toList();
  }

  static Future<void> clearRecords() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(key);
  }
}

🌿 3단계: 저장 버튼 만들기

home_screen.dart에 아래 버튼을 추가:

ElevatedButton(
  onPressed: () {
    if (_result != null && _image != null) {
      StorageService.saveRecord({
        'name': _result,
        'timestamp': DateTime.now().toIso8601String(),
        'image': _image!.path, // 실제 앱에선 Base64나 S3 연동 고려
      });
    }
  },
  child: const Text('도감에 저장'),
),

로컬 이미지 경로는 기기 재부팅 시 접근 불가할 수 있으니 추후 개선 필요함.


📄 4단계: 저장한 도감 리스트 보여주기

lib/screens/my_collection_screen.dart 생성:

import 'dart:io';
import 'package:flutter/material.dart';
import '../services/storage_service.dart';

class MyCollectionScreen extends StatefulWidget {
  const MyCollectionScreen({super.key});

  @override
  State<MyCollectionScreen> createState() => _MyCollectionScreenState();
}

class _MyCollectionScreenState extends State<MyCollectionScreen> {
  List<Map<String, dynamic>> _records = [];

  @override
  void initState() {
    super.initState();
    _load();
  }

  void _load() async {
    final records = await StorageService.loadRecords();
    setState(() => _records = records);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('내 도감')),
      body: ListView.builder(
        itemCount: _records.length,
        itemBuilder: (context, index) {
          final record = _records[index];
          return ListTile(
            leading: Image.file(File(record['image']), width: 50, height: 50, fit: BoxFit.cover),
            title: Text(record['name']),
            subtitle: Text(record['timestamp']),
          );
        },
      ),
    );
  }
}

그리고 home_screen.dart에서 이동 버튼을 만들면 완성!

IconButton(
  icon: const Icon(Icons.book),
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => const MyCollectionScreen()),
    );
  },
)

다음 에피소드 예고

#8. 실시간 인식 모드 준비하기 (카메라 프리뷰 + 실시간 분석 기획)

  • camera 패키지로 실시간 영상 캡처
  • 일정 간격으로 이미지 추출 및 전송 설계
  • 인식 속도와 리소스 고려
반응형