개발중인 flutter 앱에서 웹뷰를 사용하기 위해서 webview_flutter_wkwebview와 flutter_inappwebview 라이브러리를 둘 다 사용하고 있습니다.
그리고 시군구 코드를 얻기위해 다음 검색 api를 사용하는 daum_postcode_search 라이브러리를 도입하고 얼마후에 flutter_inappwebview 새 버전이 나와서 버전을 올리려고 하려는데
daum_postcode_search가 inappWebView의 옛버전은 참조하고 있어서 버전을 올릴수 없었고, daum_postcode_search 의 업데이트는 18개월전이 마지막으로 수정될 여지는 없어보였습니다.
다른 위치 검색 라이브러리도 마찬가지로 웹뷰 라이브러리의 옛버전을 참조하고있고, 언젠가는 웹뷰 버전을 올려야 하기에...
daum_postcode_search 라이브러리를 떼고 직접 구현하기로 했습니다.
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>
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);
}
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를 을 이용할 수 있습니다~! 데이터를 받을 모델을 만들면 기존에 사용하던것과 크게 다른건 없을겁니다.
이번 이슈를 해결하면서 몸소 느꼈습니다.
라이브러리 남용은 언제나 재앙이 된다.
쉽게 쉽게 기능을 구현할 수 있는 라이브러리들을 적재적소에 쓰는것은 개발속도에 도움이 되지만 이런식으로 언제든 문제가 생길 수 있기 때문에 라이브러를 선택할 때와 사용할 때 한번 더 생각해볼 필요가 있겠습니다.