떨림 파악: 플러그인 만들기

고객이나 디자이너가 당신에게 기능을 실현하라고 요구할 때 이 이야기는 시작됩니다. 이 기능은 이 컴퓨터의 API로 실현할 수 있지만, Flatter에 포함되지 않습니다.
우선, 당신은 pub.dev에서 검색을 시도해 보았습니다. 누군가가 제 상황을 만났고 이런 상황을 해결하는 소프트웨어 패키지를 개발했고 지역 사회와 매우 우호적으로 공유했습니다.불행하게도, 당신은 문제를 해결할 방법을 찾지 못했고, 해결 방안 하나만 남았습니다. 플러그인을 만드는 것입니다.
하지만 잠깐만, 우선 플러그인이 무엇인지 알려줘야 해.진동 생태계에는 두 가지 종류의 가방이 있다.
  • 성도 패키지: 성도 코드만 있는 일반 패키지(예를 들어 path 패키지)
  • 플러그인 패키지: 본 기기의 코드와dart인터페이스를 결합한 특수 패키지로 플랫폼에 특정한 코드(예를 들어 url_launcher 패키지)를 사용할 수 있습니다
  • 그래서...플러그인 하나 해달래요.
    만약 안드로이드 스튜디오를 사용한다면, 파일 -> 새로 만들기 -> 새 진동 프로젝트에 들어갈 수 있습니다.그리고 진동 플러그를 선택하세요.


    명령줄을 사용하여 플러그인을 만들 수도 있습니다.
    flutter create --template=plugin
    
    플러그인을 개발할 플랫폼과 사용할 언어도 지정할 수 있습니다
    flutter create --org com.example --template=plugin --platforms=android,ios -I swift -a kotlin flutter_is_awesome
    
    이것은 flutter_is_awesome 라는 플러그인을 만들 것입니다. iOS 부분은 Swift에 있고, 안드로이드 부분은 Kotlin에 있습니다.
    시스템 연락처 선택기를 표시하는 플러그인을 만들 것입니다.

    플러그인 해부


    이것이 바로 만들 내용입니다.

    다음과 같은 기능이 있습니다.
  • 폴더: 안드로이드에 지정된 모든 코드를 여기에 놓을 것입니다.
  • 폴더android: 위와 같지만 iOS에 적용됩니다.
  • 폴더iOS: 이 폴더는 플러그인의 모든Dart 섹션을 포함하고 이 섹션은 응용 프로그램에서 호출됩니다.
  • 폴더lib: 여기에서 플러그인 개발 테스트를 할 것입니다.
  • 폴더: 의존항으로 패키지를 가져온 프로그램은 플러그인을 시도할 수 있으며 다른 프로그램에서 가져올 필요가 없습니다.
  • 이 플러그인은 test라는 a채널을 사용하여Dart부분과 본기부분 간에 통신을 하는데 이것은 마치 터널과 같다.Dart부분은 메시지를 보내고 본기부분은 이 메시지를 감청하여 반응한다.이것은 또한 본 컴퓨터 부분에서dart 부분으로 메시지를 보내는 데도 사용할 수 있지만, 본고는 이 점을 소개하지 않을 것이다.
    Google 플러그인은 오직 하나의 기능만 있습니다. 이 기기의 연락처 선택기를 보여줍니다. 사용자가 연락처를 선택하고 선택한 연락처의 이름을 사용자에게 되돌려줍니다.
    이 예에서, 우리는Dart 부분에 간단한 문자열을 되돌려 주지만, 지도나 목록을 사용하여 더욱 복잡한 데이터 구조를 되돌려 줍니다. 플러그인이 지원하는 데이터 형식 목록 here 을 찾을 수 있습니다.
    이제 플러그인을 만들고 그 기능을 정의했습니다. 이런 것들을 해야 합니다.
  • 플러그 인의 안드로이드 섹션 작성
  • 플러그인 iOS 섹션 작성
  • 플러그인의dart부분 작성
  • 안드로이드 코드


    플러그인을 만들면 기본 클래스가 생성됩니다. exampleMethodChannel 두 가지 방법을 포함합니다.플러그인이 초기화되었을 때, 첫 번째로 호출되었습니다.dart 부분과 통신하는 채널을 만들고, 채널의 정보를 감청하기 시작합니다.채널에 새로운 소식이 있을 때 두 번째 메시지를 호출합니다.이것은 두 가지 인자가 있습니다. onAttachedToEngine 호출된 세부 사항 (방법과 최종 인자) 과 onMethodCall 은 결과를 dart 위젯으로 보내는 데 사용됩니다.
    class FlutterIsAwesomePlugin: FlutterPlugin, MethodCallHandler {
    
      override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_is_awesome")
        channel.setMethodCallHandler(this)
      }
    
      override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
        if (call.method == "getAContact") {
    
        } else {
          result.notImplemented()
        }
      }
    
    플러그인에 변수를 정의합니다. 이 변수를 잠시 후에 사용하겠습니다
      val PICK_CONTACT_RESULT_CODE = 36
      var act: android.app.Activity? = null
      private lateinit var channel : MethodChannel
      private lateinit var result: Result
    
    현재 우리는 callresult 프로토콜을 실현해야 한다. 우리는 연락처 선택기를 표시하는 데 사용할 활동을 검색하기 위해 이 프로토콜을 필요로 한다.
      override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        act = binding.activity
        binding.addActivityResultListener(this)
      }
    
      override fun onDetachedFromActivityForConfigChanges() {
        act = null;
      }
    
      override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
        act = binding.activity
        binding.addActivityResultListener(this)
      }
    
      override fun onDetachedFromActivity() {
        act = null;
      }
    
      override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
        return false
      }
    
    유일하게 부족한 부분은 연락처 선택기를 표시하고 결과를 검색하는 코드입니다.저희ActivityAware에서 연락처 선택 활동을 시작하여 PluginRegistry.ActivityResultListener에서 결과를 검색하고 저희가 저장한 결과 대상으로dart부품에 보냅니다.
      override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
        this.result = result //saves the result to call it when the user selects a contact
        if (call.method == "getAContact") {
          val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
          act?.startActivityForResult(intent, PICK_CONTACT_RESULT_CODE)
        } else {
          result.notImplemented()
        }
      }
    
    ...
    
      override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
        if (requestCode == PICK_CONTACT_RESULT_CODE) {
          if (resultCode == Activity.RESULT_OK) {
            if (data != null) {
              val contactData = data.data
              val c = act!!.contentResolver.query(contactData!!, null, null, null, null)
              if (c!!.moveToFirst()) {
                val name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                result.success(name)
                return true
              }
            }
          }
        }
        return false
      }
    
    안드로이드 부분 완성!✅

    iOS 코드


    iOS 부분은 안드로이드 부분과 매우 유사할 것입니다. 여기에는 onMethodCall 함수가 있습니다. 이것은 안드로이드에서 본 onActivityResult 함수와 register 함수에 해당하며, 새로운 메시지가 도착할 때 호출됩니다.
    public class SwiftFlutterIsAwesomePlugin: NSObject, FlutterPlugin {
      public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "flutter_is_awesome", binaryMessenger: registrar.messenger())
        let instance = SwiftFlutterIsAwesomePlugin()
        registrar.addMethodCallDelegate(instance, channel: channel)
      }
    
      public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        if call.method == "getAContact" {
    
        } else {
            result(FlutterMethodNotImplemented)
        }
      }
    }
    
    현재 우리는 새로운 클래스 onAttachedToEngine 를 만들어야 한다. 이 클래스는 연락처 선택기 보기 컨트롤러의 리셋을 받고 플러그인 클래스를 알린다.이것은 두 개의 블록 변수 handleContactPickerDelegate 가 있는데, 선택기가 onSelectContact 프로토콜로 이 종류를 알릴 때 호출됩니다.
    import Foundation
    import ContactsUI
    
    class ContactPickerDelegate: NSObject, CNContactPickerDelegate {
        public var onSelectContact: (CNContact) -> Void
        public var onCancel: () -> Void
    
        init(onSelectContact: @escaping (CNContact) -> Void,
             onCancel: @escaping () -> Void) {
            self.onSelectContact = onSelectContact
            self.onCancel = onCancel
            super.init()
        }
    
        func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
            picker.presentingViewController?.dismiss(animated: true, completion: nil)
    
            onSelectContact(contact)
        }
    
        func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
            picker.presentingViewController?.dismiss(animated: true, completion: nil)
            onCancel()
        }
    }
    
    지금 우리가 해야 할 일은 onCancel 메시지를 받을 때 연락처 선택기를 표시하는 것입니다. 따라서 플러그인 클래스에 추가합니다.
      public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        if call.method == "getAContact" {
            getAContact(withResult: result)
        } else {
            result(FlutterMethodNotImplemented)
        }
      }
    
       //save the contact picker, so it's not deallocated    
        var contactPickerDelegate: ContactPickerDelegate?
        private func getAContact(withResult result: @escaping FlutterResult) {
            let contactPicker = CNContactPickerViewController()
            contactPickerDelegate = ContactPickerDelegate(onSelectContact: { contact in
                //sends the result back to dart
                result(contact.givenName + contact.familyName)
                self.contactPickerDelegate = nil //set to nil, so it's removed from memory
            },
            onCancel: {
                result(nil)
                self.contactPickerDelegate = nil //set to nil, so it's removed from memory
            })
            contactPicker.delegate = contactPickerDelegate
            let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow })
            let rootViewController = keyWindow?.rootViewController
            DispatchQueue.main.async {
                rootViewController?.present(contactPicker, animated: true)
            }
        }
    
    iOS 부분 완성!✅

    성도 코드


    퍼즐의 마지막 조각🧩 플러그인을 만드는 성도 부분으로 모든 다른 부분을 붙입니다.
    class FlutterIsAwesome {
      static const MethodChannel _channel =
          const MethodChannel('flutter_is_awesome');
    
      static Future<String> getAContact() async {
        final String contact = await _channel.invokeMethod('getAContact');
        return contact;
      }
    }
    
    보시다시피 채널에 있는 방법 CNContactPickerDelegate 을 사용해서 결과를 기다리고 되돌려줍니다.본 기기의 위젯과 상호작용하는 모든 함수는 비동기적이며 getAContact로 되돌아옵니다.
    이제 플러그인만 테스트하면 됩니다. 예시 폴더에서 간단한 프로그램을 완성했습니다. 단추와 탭만 있으면 모든 것이 정상인지 확인할 수 있습니다.
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'package:flutter_is_awesome/flutter_is_awesome.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      String _contact = 'Unknown';
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Flutter is Awesome'),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  MaterialButton(
                    color: Colors.red,
                    textColor: Colors.white,
                    child: Text('Picker'),
                    onPressed: () => _getAContact(),
                  ),
                  Text(_contact ?? '')
                ],
              ),
            ),
          ),
        );
      }
    
      _getAContact() async {
        String contact;
        try {
          contact = await FlutterIsAwesome.getAContact();
        } on PlatformException {
          contact = 'Failed to get contact.';
        }
        if (!mounted) return;
        setState(() {
          _contact = contact;
        });
      }
    }
    
    다음은 최종 결과입니다.


    결론


    플러그인의 최종 코드 GitHub link 를 찾을 수 있습니다.
    우리가 여기서 개발한 것은 기본 예시입니다.dart 부분에만 문자열을 되돌려줍니다. 그러나 앞에서 말한 바와 같이, 당신은 더욱 복잡한 데이터 구조를 되돌려받을 수 있고, getAContact 함수를 호출할 때도 본 컴퓨터 부분에 파라미터를 전달할 수 있습니다.
    네가 플러그인으로 할 수 있는 일은 거의 무한하다. 우리는 표면에 막 닿았고, 극한은 너의 상상력이다.

    좋은 웹페이지 즐겨찾기