flutter | daum_postcode_search 직접 구현(inappWebView버전문제)

guddls ju·2024년 1월 25일
0

flutter 공부

목록 보기
5/5

상황

개발중인 flutter 앱에서 웹뷰를 사용하기 위해서 webview_flutter_wkwebview와 flutter_inappwebview 라이브러리를 둘 다 사용하고 있습니다.

그리고 시군구 코드를 얻기위해 다음 검색 api를 사용하는 daum_postcode_search 라이브러리를 도입하고 얼마후에 flutter_inappwebview 새 버전이 나와서 버전을 올리려고 하려는데

문제

daum_postcode_search가 inappWebView의 옛버전은 참조하고 있어서 버전을 올릴수 없었고, daum_postcode_search 의 업데이트는 18개월전이 마지막으로 수정될 여지는 없어보였습니다.

해결방법

다른 위치 검색 라이브러리도 마찬가지로 웹뷰 라이브러리의 옛버전을 참조하고있고, 언젠가는 웹뷰 버전을 올려야 하기에...

daum_postcode_search 라이브러리를 떼고 직접 구현하기로 했습니다.
html로 웹뷰 코드를 앱에 넣고 로컬서버로 구동시키는 방식을 택했습니다.


1. html 파일 생성 및 적용

검색을 위한 html코드를 작성하여 프로젝트 폴더안에 assets > html 안에 넣어준다.
pubspec.yaml파일에 경로 추가해야합니다

flutter:
  assets:
    - assets/html/daum_postcode.html

<html>
  <meta
    name="viewport"
    content="width=device-width, initial-scale=1, shrink-to-fit=no"
  />
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>주소 검색 페이지</title>
  </head>
  <style>
    html,
    body {
      margin: 0;
      padding: 0;
      height: 100%;
      width: 100%;
    }
    #full-size {
      height: 100%;
      width: 100%;
      display: none;
      overflow: hidden; /* or overflow:auto; if you want scrollbars */
    }
  </style>
  <body>
    <div id="full-size"></div>
  </body>
  <script src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
  <script type="text/javascript">
    const element_layer = document.getElementById("full-size");

    daum.postcode.load(function () {
      new daum.Postcode({
        oncomplete: function (data) {
          window.flutter_inappwebview.callHandler("onSelectAddress", data);
        },
        width: "100%",
        height: "100%",
        maxSuggestItems: 5,
        alwaysShowEngAddr: false,
        hideMapBtn: true,
        hideEngBtn: false,
      }).embed(element_layer);

      element_layer.style.display = "block";
    });
  </script>
</html>

2. 위치 검색 후 받아온 데이터를 저장하는 모델 생성

class DaumPostModel {
  final String address;
  final String roadAddress;
  final String jibunAddress;
  final String sido;
  final String sigungu;
  final String bname;
  final String roadname;
  final String buildingName;
  final String addressEnglish;
  final String roadAddressEnglish;
  final String jibunAddressEnglish;
  final String sidoEnglish;
  final String sigunguEnglish;
  final String bnameEnglish;
  final String roadnameEnglish;
  final String zonecode;
  final String sigunguCode;
  final String bcode;
  final String buildingCode;
  final String roadnameCode;
  final String addressType;
  final String apartment;
  final String userLanguageType;
  final String userSelectedType;

  DaumPostModel(
      this.address,
      this.roadAddress,
      this.jibunAddress,
      this.sido,
      this.sigungu,
      this.bname,
      this.roadname,
      this.buildingName,
      this.addressEnglish,
      this.roadAddressEnglish,
      this.jibunAddressEnglish,
      this.sidoEnglish,
      this.sigunguEnglish,
      this.bnameEnglish,
      this.roadnameEnglish,
      this.zonecode,
      this.sigunguCode,
      this.bcode,
      this.buildingCode,
      this.roadnameCode,
      this.addressType,
      this.apartment,
      this.userLanguageType,
      this.userSelectedType);
}

3. html을 웹뷰로 연결하는 위젯 만들고 로컬 서버에서 구동시켜주기

import 'package:dplanit_front/model/talk/daumPost/daum_post_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

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

  
  State<WebviewWithDaumPostWebview> createState() =>
      _WebviewWithDaumPostWebviewState();
}

class _WebviewWithDaumPostWebviewState
    extends State<WebviewWithDaumPostWebview> {
  final InAppLocalhostServer _localhostServer = InAppLocalhostServer();
  late InAppWebViewController _controller;

  bool isLoading = true;
  bool isError = false;

  
  void initState() {
    super.initState();
    _localhostServer.start();
  }

  
  void dispose() {
    _localhostServer.close();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("다음 주소 검색")),
      body: Stack(
        children: [
          InAppWebView(
            initialUrlRequest: URLRequest(
                 url: WebUri(
                    "http://localhost:8080/assets/html/daum_postcode.html")),
            onWebViewCreated: (controller) {
              _controller = controller;
              _controller.addJavaScriptHandler(
                  handlerName: 'onSelectAddress',
                  callback: (args) {
                    Map<String, dynamic> fromMap = args.first;
                    DaumPostModel data = _dataSetting(fromMap);
                    Navigator.of(context).pop(data);
                  });
            },
            onLoadStop: (controller, url) {
              setState(() {
                if (_localhostServer.isRunning()) {
                  isLoading = false;
                } else {
                  _localhostServer.start().then((value) {
                    _controller.reload();
                  });
                }
              });
            },
          ),
          if (isLoading) ...[
            const SizedBox(
              child: Center(
                child: CircularProgressIndicator(),
              ),
            ),
          ],
          if (isError) ...[
            Container(
              color: const Color.fromRGBO(71, 71, 71, 1),
              child: const Center(
                child: Text(
                  "페이지를 찾을 수 없습니다",
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
                ),
              ),
            ),
          ],
        ],
      ),
    );
  }

  DaumPostModel _dataSetting(Map<String, dynamic> map) {
    return DaumPostModel(
      map["address"],
      map["roadAddress"],
      map["jibunAddress"],
      map["sido"],
      map["sigungu"],
      map["bname"],
      map["roadname"],
      map["buildingName"],
      map["addressEnglish"],
      map["roadAddressEnglish"],
      map["jibunAddressEnglish"],
      map["sidoEnglish"],
      map["sigunguEnglish"],
      map["bnameEnglish"],
      map["roadnameEnglish"],
      map["zonecode"],
      map["sigunguCode"],
      map["bcode"],
      map["buildingCode"],
      map["roadnameCode"],
      map["addressType"],
      map["apartment"],
      map["userLanguageType"],
      map["userSelectedType"],
    );
  }
}

(검색 데이터를 사용하고 싶으시면 kakao developer에서 private key 받아서 앱 main에 넣어주는 작업은 따로 하셔야합니다)


이렇게 해주시면은 간단하게 다음 검색api를 을 이용할 수 있습니다~! 데이터를 받을 모델을 만들면 기존에 사용하던것과 크게 다른건 없을겁니다.

이번 이슈를 해결하면서 몸소 느꼈습니다.
라이브러리 남용은 언제나 재앙이 된다.
쉽게 쉽게 기능을 구현할 수 있는 라이브러리들을 적재적소에 쓰는것은 개발속도에 도움이 되지만 이런식으로 언제든 문제가 생길 수 있기 때문에 라이브러를 선택할 때와 사용할 때 한번 더 생각해볼 필요가 있겠습니다.



참조
https://velog.io/@tygerhwang/Flutter-%EB%8B%A4%EC%9D%8C-%EC%9A%B0%ED%8E%B8%EB%B2%88%ED%98%B8-API%EC%A3%BC%EC%86%8C-%EA%B2%80%EC%83%89-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-2%ED%8E%B8

profile
효율에 미친자

0개의 댓글