[토이프로젝트]대출이자계산기 만들어보기 - 4편 : Flutter로 모바일 서비스 개발하기
- -
지난 글에 이어 오늘은 자바스크립트 채널을 이용해 웹 함수를 네이티브 앱으로 가져와 사용해보겠습니다.
토스트 팝업 적용
입력값 체크를 위해 토스트 팝업을 사용해보겠습니다.
flutter pub add fluttertoast
웹 뷰 위젯에 토스트 팝업을 추가합니다.
_openToast() 함수는 메세지(String message)와 팝업 출력 위치(ToastGravity gravity)를 인자값으로 받아 팝업을 출력합니다.
lib/widget/webview.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:webview_flutter/webview_flutter.dart';
class MainWebView extends StatefulWidget {
const MainWebView({
Key? key,
}) : super(key: key);
@override
State<MainWebView> createState() => _MainWebViewState();
}
class _MainWebViewState extends State<MainWebView> {
late WebViewController _controller = WebViewController();
late FToast fToast;
void _openToast(
String message,
ToastGravity gravity,
) {
Widget toast = Container(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
color: const Color(0XFF0479f6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.notifications_active, color: Colors.white),
const SizedBox(width: 12.0),
Text(
message,
style: const TextStyle(color: Colors.white),
),
],
),
);
fToast.showToast(
child: toast,
gravity: gravity,
toastDuration: const Duration(seconds: 2),
);
}
@override
void initState() {
fToast = FToast();
fToast.init(context);
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('shareChannel',
onMessageReceived: (JavaScriptMessage javaScriptMessage) async {
var data = jsonDecode(javaScriptMessage.message);
print(data);
})
..addJavaScriptChannel('alertChannel',
onMessageReceived: (JavaScriptMessage javaScriptMessage) {
var data = jsonDecode(javaScriptMessage.message);
_openToast(data["message"], ToastGravity.TOP);
})
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {},
onPageStarted: (String url) {},
onPageFinished: (String url) async {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse('${dotenv.env['BASE_URL']}'));
super.initState();
}
@override
Widget build(BuildContext context) {
return WebViewWidget(controller: _controller);
}
}
이제 다시 앱을 실행해 테스트해봅니다.
웹에서 사용된 alert 창이 아닌 토스트 팝업이 확인됩니다.
네이티브 방식의 카카오 공유하기 추가
네이티브 방식으로 카카오 공유하기 구현시에는 몇가지 설정이 필요합니다.
- Android, iOS 플랫폼 추가
- Flutter kakao sdk 추가
- Android, iOS 커스텀 URL 스킴 추가
플랫폼을 등록할 때 앱 패키지명을 등록해주게 되는데 현재 패키지명은 com.example... 로 생성되어 있을 겁니다. 이후에 앱을 배포할 때 패키지명에 example이라는 값이 포함되면 안되기 때문에 미리 바꿔주도록 하겠습니다.
Android 먼저 수정해보겠습니다. 안드로이드 스튜디오를 실행하고 example 패키지 우클릭 후 Refactor - Rename을 통해 이름을 com.example.app_my_calculator에서 com.app.app_my_calculator로 바꾸겠습니다.
좌측 하단에 Do Refactor를 클릭합니다.
Command + Shift + R을 입력해 com.example이 포함된 파일을 모두 검색합니다. 파일을 클릭하고 변경된 패키지명을 입력합니다.
Xcode 실행 후 Runner - General 탭에서 Bundle Identifier를 클릭하고 패키지명을 바꿔줍니다.
1. Android, iOS 플랫폼 추가
카카오 개발자센터에 접속합니다.
플랫폼 - Android/iOS 플랫폼 등록을 클릭합니다. 각 플랫폼별 변경한 패키지명을 등록합니다.
안드로이드 플랫폼은 키 해시를 따로 등록해주어야 합니다. 키 해시는 디버그 키 해시와 릴리즈 키 해시 두가지가 있으며, 배포시에는 릴리즈 키 해시까지 등록해주어야 합니다. 릴리즈 키 해시는 이후 앱 배포를 진행할 때 등록해주겠습니다. 디버그 키 해시 생성 및 등록 방법은 아래 더보기를 참고해주세요.
1. 터미널 실행
2. 키 해시 생성 명령어 입력
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android | openssl sha1 -binary | openssl base64
3. 생성된 키 해시 등록
2. Flutter kakao sdk 추가
먼저 네이티브 앱 키를 복사해 env 파일에 추가합니다.
.env
BASE_URL=https://calculator.codedream.co.kr
KAKAO_API_KEY=b52af223c1ef1ee4c839add4abb6320e
flutter에서 사용할 카카오 sdk를 추가합니다.
pubspec.yaml
kakao_flutter_sdk_share: ^1.9.1+2
카카오 sdk는 안드로이드의 최소 sdk 21버전을 만족해야 합니다.
안드로이드 스튜디오를 실행해 19버전으로 맞췄던 minSdkVersion을 21로 수정합니다. 수정 후에는 Sync Now로 리빌딩해줍니다.
build.gradle(Module: app)
3. Android, iOS 커스텀 URL 스킴 추가
커스텀 URL 스킴(Custom URL Scheme)은 사용자가 Android와 iOS 환경에서 카카오톡 메세지 버튼이나 링크를 통해 서비스 앱을 실행하는데 사용됩니다. 공유하기 버튼 클릭시 카카오톡 앱이 실행될 수 있도록 추가합니다.
Andorid
app - manifests - AndroidManifest.xml에 아래 코드를 추가합니다. android:scheme에는 kakao + 네이티브앱키 형태로 값을 입력합니다.
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- 카카오톡 공유, 카카오톡 메시지 -->
<data android:host="kakaolink"
android:scheme="kakaob52af223c1ef1ee4c839add4abb6320e" />
</intent-filter>
iOS
Runner - Info(Source Code)에 아래 내용을 추가합니다.
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- 카카오 네이티브 앱키 -->
<string>kakaob52af223c1ef1ee4c839add4abb6320e</string>
<!-- 카카오톡으로 로그인 -->
<string>kakaokompassauth</string>
<!-- 카카오톡 공유 -->
<string>kakaolink</string>
</array>
Runner - TARGETS(Runner) - Info - URL Types를 클릭합니다.
추가 버튼을 클릭하고 URL Schemes에 kakako + 네이티브앱키 형태로 값을 입력합니다.
이제 설정은 마무리되었습니다.
main.dart에 카카오 sdk 초기화 함수를 추가합니다.
lib/main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 구성 파일 로드
await dotenv.load(fileName: ".env");
// 카카오 sdk 초기화
KakaoSdk.init(nativeAppKey: '${dotenv.env['KAKAO_API_KEY']}');
runApp(const MyApp());
}
웹에서 사용된 카카오 공유하기 템플릿을 동일하게 만들어줍니다. 이 템플릿에는 shareChannel로 전달된 파라미터들이 사용됩니다.
lib/widget/webview.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:kakao_flutter_sdk_share/kakao_flutter_sdk_share.dart';
import 'package:webview_flutter/webview_flutter.dart';
class MainWebView extends StatefulWidget {
const MainWebView({
Key? key,
}) : super(key: key);
@override
State<MainWebView> createState() => _MainWebViewState();
}
class _MainWebViewState extends State<MainWebView> {
late WebViewController _controller = WebViewController();
late FToast fToast;
void _openToast(
String message,
ToastGravity gravity,
) {
Widget toast = Container(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
color: const Color(0XFF0479f6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.notifications_active, color: Colors.white),
const SizedBox(width: 12.0),
Text(
message,
style: const TextStyle(color: Colors.white),
),
],
),
);
fToast.showToast(
child: toast,
gravity: gravity,
toastDuration: const Duration(seconds: 2),
);
}
@override
void initState() {
fToast = FToast();
fToast.init(context);
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('shareChannel',
onMessageReceived: (JavaScriptMessage javaScriptMessage) async {
var data = jsonDecode(javaScriptMessage.message);
String paymentMethod = data['paymentMethod'];
String amount = data['amount'];
String period = data['period'];
String interest = data['interest'];
String redirectUrl =
'${dotenv.env['BASE_URL']}/share?paymentMethod=$paymentMethod&amount=$amount&period=$period&interest=$interest';
try {
bool isKakaoTalkSharingAvailable =
await ShareClient.instance.isKakaoTalkSharingAvailable();
if (isKakaoTalkSharingAvailable) {
FeedTemplate defaultFeed = FeedTemplate(
content: Content(
title: "my-calculator",
description: "계산 결과를 확인해 보세요!",
imageUrl: Uri.parse('${dotenv.env['BASE_URL']}/favicon.png'),
link: Link(
webUrl: Uri.parse(redirectUrl),
mobileWebUrl: Uri.parse(redirectUrl)),
),
buttons: [
Button(
title: '계산 결과 확인하기',
link: Link(
webUrl: Uri.parse(redirectUrl),
mobileWebUrl: Uri.parse(redirectUrl),
),
),
],
);
Uri uri =
await ShareClient.instance.shareDefault(template: defaultFeed);
await ShareClient.instance.launchKakaoTalk(uri);
} else {
_openToast("카카오톡을 설치해 주세요.", ToastGravity.BOTTOM);
}
} catch (error) {
_openToast("공유하기에 실패했습니다.", ToastGravity.TOP);
}
})
..addJavaScriptChannel('alertChannel',
onMessageReceived: (JavaScriptMessage javaScriptMessage) {
var data = jsonDecode(javaScriptMessage.message);
_openToast(data["message"], ToastGravity.TOP);
})
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {},
onPageStarted: (String url) {},
onPageFinished: (String url) async {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse('${dotenv.env['BASE_URL']}'));
super.initState();
}
@override
Widget build(BuildContext context) {
return WebViewWidget(controller: _controller);
}
}
카카오 공유하기 테스트를 위해서는 애뮬레이터와 시뮬레이터가 아닌 실제 기기가 필요합니다.
케이블 연결 후에 안드로이드, 아이폰을 연결합니다. 안드로이드 폰이 없는 관계로 아이폰으로 테스트를 진행해보겠습니다.
맥북과 아이폰 연결 후 Xcode를 실행합니다. Runner에 본인 아이폰을 선택 후에 Product - Run을 실행합니다.
실행 후에는 아래와 같이 앱이 생성됩니다.
앱 접속 후 공유하기를 누르면 카카오톡으로 연결되며 공유되는 것을 확인할 수 있습니다.
여기까지 웹 뷰를 이용해서 간단한 모바일앱을 만들어봤습니다. 아직은 수정할 곳이 많아 보입니다.
다음 글에서 앱 이름 설정, 아이콘 생성 등과 같은 작업들을 통해 앱 스타일을 다듬어보고 마켓에 앱을 등록하는 과정을 알아보겠습니다.
전체 코드는 GitHub에서 확인하실 수 있습니다.
참고문서
'토이프로젝트' 카테고리의 다른 글
[토이프로젝트]대출이자계산기 만들어보기 - 5편 : 플레이스토어와 앱스토어에 어플리케이션 배포하기 (0) | 2024.05.21 |
---|---|
[토이프로젝트]대출이자계산기 만들어보기 - 5편 : 플레이스토어와 앱스토어에 어플리케이션 배포하기 (0) | 2024.05.08 |
[토이프로젝트]대출이자계산기 만들어보기 - 4편 : Flutter로 모바일 서비스 개발하기 (0) | 2024.05.01 |
[토이프로젝트]대출이자계산기 만들어보기 - 3편 : 웹 배포하기 (1) | 2024.04.27 |
[토이프로젝트]대출이자계산기 만들어보기 - 2편 : Next.js로 웹 서비스 개발하기 (1) | 2024.04.27 |
소중한 공감 감사합니다.