달력

12

« 2024/12 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
반응형

며칠 전, Flutter에서 webview_flutter(https://pub.dev/packages/webview_flutter)로 띄운 소스에서

alert가 보이지 않으니 해결해달라는 요청을 받아 처리하게 되었습니다.

찾아보니 webview_flutter는 alert&confirm을 지원하지 않는다고 합니다.

flutter와 html에서 약속을 해서 새로운 function을 만들고 호출하는 방법도 있지만,

(참조: https://cording-cossk3.tistory.com/103)

저는 html 개발하시는 분과 직접 소통하는 것도 아니고, 최대한 네이티브 소스는 건드리지 않기 위하여

그냥 기존에 있는 alert를 flutter의 toast로 작업하는 방식으로 처리하였습니다.

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:toast/toast.dart';
import 'package:webview_flutter/webview_flutter.dart';

class WebviewPage extends StatelessWidget {
  WebViewController _webViewController;

  @override
  void initState() {
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: '${yourUrl}',
      javascriptMode: JavascriptMode.unrestricted,
      javascriptChannels: <JavascriptChannel>{
        _alertJavascriptChannel(context),
      },
      onWebViewCreated: (WebViewController webViewController) {
        _webViewController = webViewController;
      },
      onPageFinished: (url) async {
        try {
          var javascript = '''
                             window.alert = function (e){
                               Alert.postMessage(e);
                             }
                           ''';
          await _webViewController?.evaluateJavascript(javascript);
        } catch (_) {}
      },
    );
  }

  JavascriptChannel _alertJavascriptChannel(BuildContext context) {
    return JavascriptChannel(
        name: 'Alert',
        onMessageReceived: (JavascriptMessage message) {
          Toast.show(message.message, context,
              duration: Toast.LENGTH_LONG,
              gravity: Toast.CENTER,
              backgroundColor: Colors.black38,
              backgroundRadius: 5);
        });
  }
}

이 방법으로 하면 confirm도 처리되긴 합니다.

그런데 confirm이 하나일땐 크게 문제 없겠지만 (위와 같이 confirm을 정의해서 flutter 내에서 dialog 호출해서 처리)

여러 개일때는 confirm마다 기능이 다르다면 결국 html 개발자와 같이 상의해서 개발해야 할 것입니다.

confirm도 처리해야 할 때는 android는 webview_flutter를 2.0.13 버전 이상으로 업 하면 해결됩니다.

ios는 다음 글을 참고하여 처리하여 주세요. (다음 글 : https://risha-lee.tistory.com/42)

 

 

참고 출처 : https://github.com/flutter/flutter/issues/30358

 

 

 

 

 

 

 

반응형
:
Posted by 리샤씨
2021. 8. 12. 20:53

Flutter에서 카카오톡 채널 추가하기 개발/Flutter2021. 8. 12. 20:53

반응형

Flutter를 한지 반년 이상 지났지만, 바쁘기도 했고 보통 pub.dev에서 갖다썼기 때문에

마땅히 포스팅할게 없어서 이제서야 관련 글을 작성합니다.

 

최근, 카카오톡 채널 추가하기 기능을 추가해달라는 요청을 받았는데 찾아보니 pub.dev에도 없고..

네이티브 소스를 좀 작성해야 가능한 것처럼 보여서 코틀린과 Swift로 작업을 진행했습니다.

카카오 개발 설정은 다른 블로그 글도 많기 때문에 생략했습니다.

설정하셔서 해시키도 등록해두시고 native app key를 얻으셔서 진행하시길 바랍니다.

 

그리고 제가 이해하기로는 카카오톡 친구를 강제적으로 추가해주는 API는 존재하지 않아보이고,

(가입시 카카오에서 제공하는 체크박스 제외)

카카오톡 친구를 보여주어 채널을 추가를 유도하는 정도의 기능만 구현할 수 있어보여서 그렇게 진행하였습니다.

 

(22. 9. 1) 1년이 지난 지금, 찾아보니 Kakao에서 Flutter를 지원해서 아래 링크를 따라하시면 간단히 할 수 있게 되었네요.

아마 kakao_flutter_sdk 버전 1.0.0이 공식 출시 되면서 22년 3월 경에 해당 링크와 기능을 지원하기 시작한거 같습니다.

낮은 확률로 다른 패키지와 충돌해서 kakao_flutter_sdk를 사용하실 수 없을 수도 있으니 

제가 작성한 소스도 남겨 놓도록 하겠습니다.

가능하시면 아래 링크를 통해 연결해보세요.

https://developers.kakao.com/docs/latest/ko/kakaotalk-channel/flutter

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

1. yaml을 작성한다.

flutter_kakao_login: 3.3.0
url_launcher: 6.0.3 # 카카오톡 설치 여부를 위해 추가

 

2. main.dart를 작성한다.

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_kakao_login/flutter_kakao_login.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '카카오톡 채널 추가 Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: '카카오톡 채널 추가 Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void goKakaoChannel() async {
    final installed = await canLaunch(Platform.isIOS ? "kakaokompassauth://authorize" : "kakaolink://");
    if (installed) {
      final FlutterKakaoLogin kakaoSignIn = new FlutterKakaoLogin();
      await kakaoSignIn.init("your_native_app_key");
      const MethodChannel _channel = const MethodChannel('myChannel');
      _channel.invokeMethod('addKakaoChannel', "your_channel_public_id");
    } else {
      //카카오톡이 설치되지 않았을때 처리
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            FloatingActionButton(
              onPressed: goKakaoChannel,
              tooltip: '카카오톡 채널 추가',
              child: Icon(Icons.add),
            )
          ],
        ),
      ),
    );
  }
}

 

3.  build.gradle(Project)에서 카카오 레파지토리 설정한다.

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }
    }
}

 

4. build.gradle(Module)에 디펜던시를 설정한다.

dependencies {
    implementation "com.kakao.sdk:v2-talk:2.4.2" // 친구, 메시지(카카오톡)
}

 

5. MainActivity.kt를 작성한다.

package com.risha.blog

import androidx.annotation.NonNull

import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

import com.kakao.sdk.talk.*
import com.kakao.sdk.common.util.*

class MainActivity: FlutterActivity() {

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine)
        val channel = MethodChannel(flutterEngine.getDartExecutor(), "myChannel")
        channel.setMethodCallHandler(handler)
    }


    private val handler: MethodChannel.MethodCallHandler = MethodChannel.MethodCallHandler({ methodCall, result ->
        if (methodCall.method.equals("addKakaoChannel")) {
            val channelPublicId = methodCall.arguments as String
            val url = TalkApiClient.instance.channelChatUrl(channelPublicId)

            // CustomTabs 로 열기
            KakaoCustomTabsClient.openWithDefault(context, url)
        } else {
            result.notImplemented()
        }
    })
}

 

6. Podfile을 작성한다. 

# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'

pod 'KakaoSDKTalk'  # 친구, 메시지(카카오톡)

작성 뒤, pod install합니다.

 

7. Info.plist를 작성한다.

    <key>LSApplicationQueriesSchemes</key>
    <array>
        <!-- common -->
        <string>kakao${your_native_app_key}</string>

        <!-- KakaoTalk login -->
        <string>kakaokompassauth</string>
        <string>storykompassauth</string>
    </array>

 

8. AppDelegate.swift를 작성한다.

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    var navigationController: UINavigationController!
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    linkNativeCode(controller: controller)
    GeneratedPluginRegistrant.register(with: self)
    
    self.navigationController = UINavigationController(rootViewController: controller)
    self.window.rootViewController = self.navigationController
    self.navigationController.setNavigationBarHidden(true, animated: false)
    self.window.makeKeyAndVisible()
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

    private func addKakaoChannel(call: FlutterMethodCall, result: @escaping FlutterResult) {
        let vc = UIStoryboard.init(name: "Main", bundle: .main)
                                .instantiateViewController(withIdentifier: "ViewController") as! ViewController
                        if let arguments = call.arguments as? String {
                            vc.arguments = arguments
                        }
        
                        vc.result = result
                        self.navigationController.pushViewController(vc, animated: true)
    }
}

extension AppDelegate {
    
    func linkNativeCode(controller: FlutterViewController) {
        setupMethodChannel(controller: controller)
    }
    
    private func setupMethodChannel(controller: FlutterViewController) {
        let commonChannel = FlutterMethodChannel(name: "myChannel",
                                                  binaryMessenger: controller.binaryMessenger)
        
        commonChannel.setMethodCallHandler({
          (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
          // Note: this method is invoked on the UI thread.
            
            //let viewController = ViewController()
            if call.method == "addKakaoChannel" {
                self.addKakaoChannel(call: call, result: result)
            }
        })
    }
}

 

9. ViewController.swift를 작성한다.

import UIKit
import SafariServices
import KakaoSDKTalk

class ViewController: UIViewController, SFSafariViewControllerDelegate {
    var result: FlutterResult!
    var arguments: String!
    var safariViewController : SFSafariViewController? // to keep instance
    
    override func viewDidLoad() {
        super.viewDidLoad()

        self.safariViewController = SFSafariViewController(url: TalkApi.shared.makeUrlForChannelChat(channelPublicId: arguments)!)

        guard (self.safariViewController != nil) else { return }

        self.safariViewController?.modalTransitionStyle = .crossDissolve
        self.safariViewController?.modalPresentationStyle = .overCurrentContext
        self.safariViewController?.delegate = self
        self.present(self.safariViewController!, animated: true) {
            print("Kakao Talk Channel chat 연결 페이지 실행 성공")
        }
    }
    
    func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
        //To access the  Specific tabBar ViewController
        self.navigationController?.popViewController(animated: true)
    }
}

 

10. Storyboard ID를 작성한다.

AppDelegate.swift의 instantiateViewController(withIdentifier: "ViewController")는

Main.storyboard에 ViewController를 추가하신 뒤, Storyboard ID를 withIdentifier 값으로 기입하시면 됩니다.

 

 

 

 

 

참고 출처 :

https://blog.usejournal.com/integrating-native-third-party-sdk-in-flutter-8aab03afa9da

https://developers.kakao.com/docs/latest/ko/kakaotalk-channel/ios

 

반응형
:
Posted by 리샤씨


반응형
반응형