Programming/Dart+Flutter

[기말고사 대비] 3. search page

주눅 2025. 6. 11. 13:08

3. search page

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

import 'home_page.dart';  // Place 클래스 참조용

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

  @override
  State<SearchPage> createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> {
  final TextEditingController _searchController = TextEditingController();

  // API로 받은 장소 목록
  List<Place> _searchResults = [];

  bool _isLoading = false;  // 로딩 상태 표시

  // 카카오 REST API 키를 여기에 넣으세요
  static const String kakaoApiKey = 'b4da1ac3a5fd6507a6777f8f22f41cd6';

  // 카카오 장소 검색 함수
  Future<void> _searchPlaces(String query) async {
    if (query.isEmpty) return;

    setState(() {
      _isLoading = true;
      _searchResults = [];
    });

    final url = Uri.parse('https://dapi.kakao.com/v2/local/search/keyword.json?query=$query&size=15');

    final response = await http.get(
      url,
      headers: {
        'Authorization': 'KakaoAK $kakaoApiKey',  // 헤더에 API 키 추가
      },
    );

    if (response.statusCode == 200) {
      final jsonData = json.decode(response.body);

      // 카카오 API 결과에서 문서(documents) 리스트 가져오기
      final List documents = jsonData['documents'];

      setState(() {
        _searchResults = documents.map((doc) {
          return Place(
            id: doc['id'],
            name: doc['place_name'],
            address: doc['road_address_name'] != '' ? doc['road_address_name'] : doc['address_name'],
          );
        }).toList();
      });
    } else {
      // 오류 처리 - 간단 알림
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('장소 검색 실패: ${response.statusCode}')),
      );
    }

    setState(() {
      _isLoading = false;
    });
  }

  @override
  void dispose() {
    _searchController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('장소 검색'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: [
            // 검색 입력창
            TextField(
              controller: _searchController,
              decoration: InputDecoration(
                hintText: '장소명 입력 후 검색 버튼 누르기',
                suffixIcon: IconButton(
                  icon: const Icon(Icons.search),
                  onPressed: () {
                    _searchPlaces(_searchController.text.trim());
                  },
                ),
                border: const OutlineInputBorder(),
              ),
              onSubmitted: (value) {
                _searchPlaces(value.trim());
              },
            ),
            const SizedBox(height: 10),

            // 로딩 표시
            if (_isLoading) const LinearProgressIndicator(),

            // 검색 결과 목록
            Expanded(
              child: ListView.builder(
                itemCount: _searchResults.length,
                itemBuilder: (context, index) {
                  final place = _searchResults[index];
                  return ListTile(
                    title: Text(place.name),
                    subtitle: Text(place.address),
                    onTap: () {
                      // 선택한 장소를 홈 화면으로 넘기고 종료
                      Navigator.pop(context, place);
                    },
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}