首页 > 解决方案 > 颤振 | 如何从 Native 到 Flutter 端获取带有 Event Channel 的数据流?

问题描述

我想使用事件通道从 Spotify SDK 获取数据流。在原生端,我可以通过订阅我的 PlayerState 来自动显示当前歌曲的状态。我的目标是能够使用我的 Flutter 应用程序访问此数据流。但是,我没有得到任何真实数据,只是一个 MapStream:

Instance of '_MapStream<dynamic, double>'

根据我的 StreamBuilder,这个 MapStream 没有数据。错误在哪里?如何将数据从 Native 推送到 Flutter 端?

本机代码:

package test.test.spotifysdk04

import com.spotify.android.appremote.api.ConnectionParams
import com.spotify.android.appremote.api.Connector
import com.spotify.android.appremote.api.SpotifyAppRemote
import com.spotify.protocol.types.PlayerState
import com.spotify.sdk.android.authentication.AuthenticationClient
import com.spotify.sdk.android.authentication.AuthenticationRequest
import com.spotify.sdk.android.authentication.AuthenticationResponse
import io.flutter.Log
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import com.spotify.protocol.types.Track


class Spotifysdk04Plugin(private var registrar: Registrar): MethodCallHandler, EventChannel.StreamHandler {
    private val clientId = "65fd9b2b0ee74575a6d26223a1675917"
    private val redirectUri = "spotify-flutter://callback"
    private var spotifyAppRemote: SpotifyAppRemote? = null
    private val REQUEST_CODE = 1337
    private var mEventSink: EventChannel.EventSink? = null

  companion object {
    @JvmStatic
    fun registerWith(registrar: Registrar) {
      val channel = MethodChannel(registrar.messenger(), "spotifysdk")
      channel.setMethodCallHandler(Spotifysdk04Plugin(registrar))

        val eventChannel = EventChannel(registrar.messenger(), "timerStream")
        eventChannel.setStreamHandler(Spotifysdk04Plugin(registrar))
    }
  }

  override fun onMethodCall(call: MethodCall, result: Result) {
    if (call.method == "loginAppRemote") {
        val connectionParams = ConnectionParams.Builder(clientId)
                .setRedirectUri(redirectUri)
                .showAuthView(true)
                .build()

        SpotifyAppRemote.connect(registrar.context(), connectionParams, object : Connector.ConnectionListener {
            override fun onConnected(appRemote: SpotifyAppRemote) {
                spotifyAppRemote = appRemote
                Log.d("Spotify App Remote Login", "Connected!")
                result.success(true)
            }

            override fun onFailure(throwable: Throwable) {
                Log.e("Spotify App Remote Login", "Error!", throwable)
                result.success(false)
            }
        })
    } else if(call.method == "loginSpotifyAuthentication") {
        try {
            AuthenticationClient.openLoginActivity(
                    registrar.activity(), REQUEST_CODE,
                    AuthenticationRequest.Builder(clientId,AuthenticationResponse.Type.TOKEN,redirectUri)
                            .setScopes(arrayOf("user-modify-playback-state")).build())
        }catch (err:Throwable){
            Log.v("getAuthTokenError",err.message.toString())
        }
        registrar.addActivityResultListener { requestCode, resultCode, intent ->
            if (requestCode == REQUEST_CODE){
                val response = AuthenticationClient.getResponse(resultCode, intent).accessToken
                result.success(response)
            }
            true
        }
    } else {
      result.notImplemented()
    }
  }

    override fun onCancel(arguments: Any?) {
        mEventSink = null
    }

    override fun onListen(arguments: Any?, eventSink: EventChannel.EventSink?) {
        createListener(eventSink)
    }

    fun createListener(event: EventChannel.EventSink?) {
        if (spotifyAppRemote != null) {
            spotifyAppRemote!!.playerApi.subscribeToPlayerState()
                    .setEventCallback { playerState: PlayerState? ->
                        var position = playerState!!.playbackPosition.toDouble()
                        Log.d("playbackPosition", position.toString())
                        event?.success(position)
                    }
        }
    }
}

飞镖插件代码:

import 'dart:async';

import 'package:flutter/services.dart';

class Spotifysdk04 {
  static const MethodChannel _channel = const MethodChannel('spotifysdk');
  static const EventChannel _timerEventChannel = const EventChannel('timerStream');

  static Stream<double> _timerStream;

  static Future<bool> get loginAppRemote async {
    final bool connected = await _channel.invokeMethod('loginAppRemote');
    return connected;
  }

  static Future<String> get loginSpotifyAuthentication async {
    final String accessToken =
        await _channel.invokeMethod('loginSpotifyAuthentication');
    return accessToken;
  }

  static Stream<double> get timerStream {
    _timerStream =
        _timerEventChannel.receiveBroadcastStream().map<double>((value) => value);
    print(_timerStream);

    return _timerStream;
  }
}

颤振代码:

//...
StreamBuilder(
                    stream: Spotifysdk04.timerStream,
                    builder: (BuildContext context, AsyncSnapshot snapshot) {
                      print(snapshot);
                      if (snapshot.hasData) {
                        return Text(
                          'Pressure Stream ${snapshot.data}',
                          style: TextStyle(
                            color: Colors.white,
                          ),
                        );
                      }
                      return Text(
                        'No data',
                        style: TextStyle(
                          color: Colors.white,
                        ),
                      );
                    }),
//...

标签: androidkotlinflutterspotify

解决方案


您快到了。

_timerStream = _timerEventChannel.receiveBroadcastStream();

这已经足够了。通常你会使用它的StreamSubscription<T> listen(void onData(T))方法来监听一个流:

_timerStream = _timerEventChannel.receiveBroadcastStream();
var subscription = _timerStream.listen(onData);

void onData(dynamic d) {
  print("Data: $d");
}

然而,StreamBuilder 已经为您处理了所有的监听逻辑。


推荐阅读