Flutter로 Android 앱의 딥 링크 처리

딥링크란 무엇입니까?



딥 링크는 사용자를 웹사이트가 아닌 앱으로 직접 연결하는 링크 유형입니다.

왜 이것이 필요한가요?



웹과 모바일 모두에서 원활한 사용자 경험을 제공합니다. 또 다른 이유는 비즈니스에 웹 사이트와 모바일 앱이 있는 경우 딥 링크를 사용하여 "모바일 퍼스트"웹 디자인 패턴에서 벗어날 수 있기 때문입니다.

구현


AndroidManifest.xml 아래에서 android/app/src/main 파일 찾기

내부에 이 링크를 추가하십시오<activity>.

 <intent-filter android:label="InviteLink">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
        android:scheme="https"
        android:host="only4.dev"
        android:pathPrefix="/invite"
    />
</intent-filter>


Android 운영 체제는 일부 앱에 특정 작업을 등록하는 API를 제공합니다. 예를 들어 mp3 파일을 클릭하면 Android에서 지원되는 앱 목록에서 앱을 선택하라는 메시지를 사용자에게 표시합니다.
only4.dev/invite에서 시작하는 링크를 클릭하면 항상 모바일 앱에서 이 링크를 열라는 메시지가 표시됩니다.

앱에서 링크를 처리할 수 있습니다.



2가지 유형의 이벤트가 있을 수 있습니다.
  • 앱이 실행되고 있지 않을 때
  • 앱이 이미 실행 중인 경우.

  • 이를 위해 2가지 유형의 메시지를 생성하고 브로드캐스트 수신기도 생성합니다.

    브로드캐스트 수신기(수신기)는 시스템 또는 애플리케이션 이벤트에 등록할 수 있는 Android 구성요소입니다. 이벤트에 등록된 모든 수신자는 이 이벤트가 발생하면 Android 런타임에서 알림을 받습니다.

    private static final String CHANNEL = "initial";
    private static final String EVENTS = "eventWhileAppIsRunning";
    private String startString;
    private BroadcastReceiver linksReceiver;
    


    사건을 처리합시다. 첫 번째 경우에 대해 새 항목MethodChanenel을 만들고 두 번째 경우에 대해 새 항목EventChannel을 만듭니다.

    Android 측MethodChannel에서 Android(API) 및 iOS 측FlutterMessageChannel(API)은 메서드 호출을 수신하고 결과를 다시 보내는 데 사용됩니다. 필요한 경우 Android/IOS 플랫폼이 클라이언트 역할을 하고 Dart에서 구현된 메서드를 사용하여 메서드 호출을 반대 방향으로 보낼 수도 있습니다.
    EventChannel는 데이터를 스트리밍할 때 사용됩니다. 그 결과 Dart 측에 스트림이 있고 기본 측에서 해당 스트림을 공급할 수 있습니다. (즉, 앱이 이미 실행 중일 때).

    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      GeneratedPluginRegistrant.registerWith(this);
    
      Intent intent = getIntent();
      Uri data = intent.getData();
    
      new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
        new MethodChannel.MethodCallHandler() {
          @Override
          public void onMethodCall(MethodCall call, MethodChannel.Result result) {
            if (call.method.equals("initialLink")) {
              if (startString != null) {
                result.success(startString);
              }
            }
          }
        }
      );
    
      new EventChannel(getFlutterView(), EVENTS).setStreamHandler(
        new EventChannel.StreamHandler() {
          @Override
          public void onListen(Object args, final EventChannel.EventSink events) {
            linksReceiver = createChangeReceiver(events);
          }
    
          @Override
          public void onCancel(Object args) {
            linksReceiver = null;
          }
        }
      );
    
      if (data != null) {
        startString = data.toString();
        if(linksReceiver != null) {
          linksReceiver.onReceive(this.getApplicationContext(), intent);
        }
      }
    }
    


    시간을 내어 이것을 읽고 이해하십시오.

    딥 링크를 구현해야 하는 경우 시간을 내어 이것을 이해하십시오.

    여기에 최종 MainActivity.java가 있습니다.

    package my.app.com;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Bundle;
    
    import io.flutter.app.FlutterActivity;
    import io.flutter.plugin.common.EventChannel;
    import io.flutter.plugin.common.MethodCall;
    import io.flutter.plugin.common.MethodChannel;
    import io.flutter.plugins.GeneratedPluginRegistrant;
    import io.flutter.plugins.GeneratedPluginRegistrant;
    
    public class MainActivity extends FlutterActivity {
      private static final String CHANNEL = "initial";
      private static final String EVENTS = "eventWhileAppIsRunning";
      private String startString;
      private BroadcastReceiver linksReceiver;
    
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
    
        Intent intent = getIntent();
        Uri data = intent.getData();
    
        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
          new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(MethodCall call, MethodChannel.Result result) {
              if (call.method.equals("initialLink")) {
                if (startString != null) {
                  result.success(startString);
                }
              }
            }
          }
        );
    
        new EventChannel(getFlutterView(), EVENTS).setStreamHandler(
          new EventChannel.StreamHandler() {
            @Override
            public void onListen(Object args, final EventChannel.EventSink events) {
              linksReceiver = createChangeReceiver(events);
            }
    
            @Override
            public void onCancel(Object args) {
              linksReceiver = null;
            }
          }
        );
    
        if (data != null) {
          startString = data.toString();
          if(linksReceiver != null) {
            linksReceiver.onReceive(this.getApplicationContext(), intent);
          }
        }
      }
    
      @Override
      public void onNewIntent(Intent intent){
        super.onNewIntent(intent);
        if(intent.getAction() == android.content.Intent.ACTION_VIEW && linksReceiver != null) {
          linksReceiver.onReceive(this.getApplicationContext(), intent);
        }
      }
    
      private BroadcastReceiver createChangeReceiver(final EventChannel.EventSink events) {
        return new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
            // NOTE: assuming intent.getAction() is Intent.ACTION_VIEW
    
            String dataString = intent.getDataString();
    
            if (dataString == null) {
              events.error("UNAVAILABLE", "Link unavailable", null);
            } else {
              events.success(dataString);
            }
          }
        };
      }
    }
    


    다음 단계는 이러한 이벤트를 플러터 코드에 전달하는 것입니다.


    StreamController() 클래스에 DeepLinkBloc를 생성합니다.

    이 클래스의 생성자 생성

    DeepLinkBloc() {
        startUri().then(_onRedirected);
        stream.receiveBroadcastStream().listen((d) => _onRedirected(d));
      }
    


    이벤트를 수신할 때마다 _onRedirected 메서드를 호출합니다. 이제 드디어 플러터 코드에 초기 링크가 생겼습니다.

    다음은 클래스의 전체 코드입니다.

    import 'dart:async';
    
    import 'package:flutter/services.dart';
    import 'package:flutter/widgets.dart';
    
    abstract class Bloc {
      void dispose();
    }
    
    class DeepLinkBloc extends Bloc {
      DeepLinkBloc() {
        startUri().then(_onRedirected);
        stream.receiveBroadcastStream().listen((d) => _onRedirected(d));
      }
    
      // Initial event channel
      static const platform = MethodChannel('initial');
    
      // Runtime Event channel
      static const stream = EventChannel('eventWhileAppIsRunning');
    
      final StreamController<String> _stateController = StreamController();
    
      Stream<String> get state => _stateController.stream;
    
      Sink<String> get stateSink => _stateController.sink;
    
      //Adding the listener into constructor
    
      void _onRedirected(String uri) {
        debugPrint(uri);
        stateSink.add(uri);
      }
    
      @override
      void dispose() {
        _stateController.close();
      }
    
      Future<String> startUri() async {
        try {
          return platform.invokeMethod('initialLink');
        } on PlatformException catch (e) {
          return "Failed to Invoke: '${e.message}'.";
        }
      }
    }
    


    이제 링크를 사용하려는 위치Provider<DeepLinkBloc>.value(value: _bloc)에 위젯을 래핑하기만 하면 됩니다.

    이 공급자를 사용하는 새 위젯을 만들 수 있습니다.

    class DeepLinkWrapper extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final _bloc = Provider.of<DeepLinkBloc>(context);
    
        return StreamBuilder<String>(
          stream: _bloc.state,
          builder: (context, snapshot) {
            // if app is started normally, no deep link is clicked show your old home page widget
            if (!snapshot.hasData) {
            return Container(
              child: const HomePage(),
            );
          } else {
            final splitInviteLink = snapshot.data.split('/');
            final inviteToken = splitInviteLink[splitInviteLink.length - 1];
    
            return RegisterStartPage(key: UniqueKey(),inviteToken: inviteToken,);
          }
        });
      }
    }
    


    이 마지막 변경만 하면 됩니다. main.dart 파일에서 이 수정 작업을 수행합니다.

     home: Scaffold(
       body: Provider<DeepLinkBloc>(
         builder: (context) => _bloc,
         dispose: (context, bloc) => bloc.dispose(),
         child: DeepLinkWrapper()
       )
     )
    


    다음은 main.dart의 최종 MyApp 클래스에 대한 코드입니다.




    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final _bloc = DeepLinkBloc();
    
        return MultiProvider(
          providers: [
            Provider<DeepLinkBloc>.value(value: _bloc),
            // ...other providers
          ],
          child: MaterialApp(
            debugShowCheckedModeBanner: false,
            localizationsDelegates: const [
              AppLocalizationsDelegate(),
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
            ],
            supportedLocales: const [
              Locale('de'),
              Locale('en'),
            ],
            routes: {
              HomePage.route: (context) => const HomePage(),
              LoginPage.route: (context) => LoginPage(),
            },
            home: Scaffold(
              body: Provider<DeepLinkBloc>(
                create: (context) => _bloc,
                dispose: (context, bloc) => bloc.dispose(),
                child: DeepLinkWrapper()
              )
            )
          ),
        );
      }
    }
    

    좋은 웹페이지 즐겨찾기