How to listen the method channel result - flutter

On firebase service, there are two method onNewToken and onMessageReceived, the code I tried to implement to get the new token and remote message from them on android side, then feedback to flutter side. But I have no idea how to listen the method channel when it has a result.
// Service
public class FluttersService extends FirebaseMessagingService {
#Override
public void onNewToken(String token) {
...
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
#Override
public void onMessageReceived(RemoteMessage message) {
...
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
}
// Plugin
public class FlutterPushPlugin extends BroadcastReceiver
implements MethodCallHandler, NewIntentListener, FlutterPlugin, ActivityAware {
// BroadcastReceiver implement
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) {
return;
}
if (action.equals(ACTION_TOKEN)) {
String token = intent.getStringExtra(EXTRA_TOKEN);
methodChannel.invokeMethod("onToken", token);
} else if (action.equals(ACTION_REMOTE_MESSAGE)) {
RemoteMessage message =
intent.getParcelableExtra(EXTRA_REMOTE_MESSAGE);
if (message == null) return;
Map<String, Object> content = remoteMessageToMap(message);
methodChannel.invokeMethod("onMessage", content);
}
}
#Override
public void onMethodCall(final MethodCall call, #NonNull final Result result) {
switch (call.method) {
case "onToken":
// How to response the result to flutter side?
break;
case "onMessage":
// How to response the result to flutter side?
break;
default:
result.notImplemented();
return;
}
}
}

Use Event channel for sending an event to flutter from native.
A complete description is provided in the below article.
https://rvsevtag62.medium.com/event-channel-to-listen-to-broadcast-events-from-android-43a813672896

Related

How to listen a startActivityForResult call when using FlutterPlugin?

I'm developing a Flutter Plugin for Android using Java. When i call the MethodChannel, I need to call another Android Intent. If I was using an native activity, it would be simple since I can call startActivityForResult and implement the method onActivityResult on the same class.
But, when I develop a Flutter Plugin, I can implement FlutterPlugin, MethodCallHandler and ActivityAware interfaces but how can I start a new activity and listen for async result?
Java code:
public class GetnetPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
private MethodChannel channel;
private Context context;
private Activity activity;
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
context = flutterPluginBinding.getApplicationContext();
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.example/method");
channel.setMethodCallHandler(this);
}
#Override
public void onDetachedFromEngine(#NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
#Override
public void onMethodCall(#NonNull MethodCall call, #NonNull Result result) {
switch (call.method){
case "makePayment":
makePayment();
result.success(true);
return;
default:
result.notImplemented();
return;
}
}
#Override
public void onAttachedToActivity(#NonNull ActivityPluginBinding binding) {
this.activity = binding.getActivity();
}
#Override
public void onDetachedFromActivityForConfigChanges() {
this.activity = null;
}
#Override
public void onReattachedToActivityForConfigChanges(#NonNull ActivityPluginBinding binding) {
this.activity = binding.getActivity();
}
#Override
public void onDetachedFromActivity() {
this.activity = null;
}
//This is the method that is called with main action. It starts a new activity by a new intent, then it should listen for a async result
public void makePayment(){
Bundle bundle = new Bundle();
DecimalFormat df = new DecimalFormat("000000000000");
bundle.putString("amount", df.format(10));
bundle.putString("paymentType", "credit");
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("getnet://pagamento/v3/payment"));
intent.putExtras(bundle);
if(intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivityForResult(intent, REQUEST_CODE);
}
}
//This is the method that I need to be called after new activity returns a result
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
channel.invokeMethod("success", data);
}
});
}
}
Flutter side:
class Getnet {
final MethodChannel _channel =
const MethodChannel('com.example/method');
final StreamController _streamController = StreamController.broadcast();
Getnet() {
_channel.setMethodCallHandler((call) async {
final method = call.method;
switch (method) {
case 'success':
_streamController.sink.add({
"status": 1,
"msg": call.arguments,
});
break;
case 'error':
_streamController.sink.add({
'status': 0,
'msg': call.arguments['errorMessage'],
});
break;
}
});
}
void dispose() {
_streamController.close();
}
Stream get stream => _streamController.stream;
Future makePayment() async {
await _channel.invokeMethod('makePayment');
}
}
I just found the answer on Enrico Ori's post at Medium: https://medium.com/theotherdev-s/mastering-flutter-create-a-plugin-e81242b6065
The solution is to make Plugin implements ActivityAware and PluginRegistry.ActivityResultListener, which demands onActivityResult implementation.

How to implement websocket_channel with bloc in flutter

below is my websocket provider .it doesn't even hit to the server when I called from block builder
I can connect to the server without bloc, I tried a lot examples but failed
I need help guys , thanks
any example about flutter bloc with web_socket_channel
WebSocket provider
static final String wsRelationUrl = "ws://127.0.0.1:8080/chat/";
final IOWebSocketChannel _channel =
IOWebSocketChannel.connect(wsRelationUrl, headers: headers);
WebSocketProvider();
#override
Future<void> disconect() async {
if (_channel != null) {
_channel.sink.close();
}
}
#override
Future<Stream> messages() async {
if (_channel != null) {
return _channel.stream;
}
return null;
}
#override
Future<void> sendMessage(String message) async {
if (_channel != null) {
return _channel.sink.add(message);
}
}
}

How to stop the subscription of a stream of a singleton class?

How to stop the subscription of a stream of a singleton class, and when re-creating the subscription do not continue the data of that stream?
class MyClass {
static MyClass _instance;
factory MyClass() {
_instance ??= MyClass._internal();
return _instance;
}
MyClass._internal();
final BehaviorSubject<Status> _streamController = BehaviorSubject<Status>();
Stream<Status> get stream => _streamController.stream;
Function(Status) get sink => _streamController.sink.add;
StreamSubscription subscriptionImage;
void dispose() {
_streamController?.close();
}
void listen() {
subscriptionImage = stream.listen((Status statusAnalysis) async {
if (statusAnalysis.saving) {
try {
await patch(statusAnalysis);
} on MyException catch (e) {}
}
});
}
}
Just add a subscriptionImage?.cancel(); before
subscriptionImage = stream.listen((Status statusAnalysis){...});
and you'll make sure that you cancel your previous subscription (if any) before creating a new one.

startup.ActivityLogin has leaked window DecorView#3a9e526[ActivityLogin] that was originally added here

Here is my code, I am trying to allow the user to sign in via his Gmail id. But I am getting this error:
Error Image
Not sure what I am missing, I think I have called dismiss() at the right place. Is it the timing issue?
The app is launching fine in my phone though. I am not sure if it will crash in other devices so I want to get rid of this error.
//Google Sign In
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == GOOGLE_SIGN_IN_KEY) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
try {
GoogleSignInAccount account = task.getResult(ApiException.class);
firebaseAuthWithGoogle(account);
} catch (ApiException e) {
if (e.getStatusCode() == 12500) {
Snackbar.make(findViewById(android.R.id.content), "Sign In Error! Update Google Play Service.", Snackbar.LENGTH_LONG).show();
}
}
}
}
private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
FirebaseUser user = firebaseAuth.getCurrentUser();
showGoogleSignUpDialog();
storeUserInfo(user.getPhotoUrl().toString(), user.getUid(), user.getDisplayName(), user.getEmail());
} else {
Snackbar.make(findViewById(android.R.id.content), "Sign In Failed!", Snackbar.LENGTH_LONG).show();
}
}
});
}
private void storeUserInfo(final String stringUserImage, final String stringUserID, final String stringName, final String stringEmail) {
FirebaseUser user = firebaseAuth.getCurrentUser();
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(stringName)
.setPhotoUri(Uri.parse(stringUserImage))
.build();
user.updateProfile(profileUpdates)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
firebaseFirestore.collection("UserData").document(stringUserID).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.getData() != null && documentSnapshot.getData().size() > 0) {
Snackbar.make(findViewById(android.R.id.content), "Welcome back!", Snackbar.LENGTH_LONG).show();
startActivity(new Intent(ActivityLogin.this, ActivityHome.class));
finish();
} else {
createUserData(stringUserID, stringUserImage, stringName, stringEmail);
}
}
});
}
}
});
}
private void createUserData(String stringUserID, String stringUserImage, String stringName, String stringEmail) {
final Map<String, Object> userDataMap = new HashMap<>();
userDataMap.put("userName", stringName);
userDataMap.put("userImage", stringUserImage);
userDataMap.put("userID", stringUserID);
userDataMap.put("userPoints", 100);
userDataMap.put("userVerified", true);
userDataMap.put("userEmail", stringEmail);
userDataMap.put("timestamp", FieldValue.serverTimestamp());
firebaseFirestore.collection("UserData").document(stringUserID).set(userDataMap)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
alertGoogleSignIn.dismiss();
Snackbar.make(findViewById(android.R.id.content), "Welcome!", Snackbar.LENGTH_LONG).show();
startActivity(new Intent(ActivityLogin.this, ActivityHome.class).putExtra("canShowCoinCredit", true));
finish();
}
}, 3000);
}
});
}
private void showGoogleSignUpDialog() {
final AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Signing in");
alert.setMessage("Please hold on while we process...");
alert.setCancelable(false);
alertGoogleSignIn = alert.create();
alertGoogleSignIn.show();
}
Try the following:
Generate the onStop() override method and transfer your startactivity as shown below. The finish() method you have already specified will invoke the onStop() override method then your activity should be started.
#Override
protected void onStop() {
super.onStop();
startActivity(new Intent(ActivityLogin.this, ActivityHome.class).putExtra ("canShowCoinCredit", true));
}
This error occurred because alertGoogleSignIn dialog is not dismissed before intent to next screen
Need to dismiss alertGoogleSignIn before startIntent
Please add alertGoogleSignIn.dismiss() in following function
private void storeUserInfo(final String stringUserImage, final String stringUserID, final String stringName, final String stringEmail) {
FirebaseUser user = firebaseAuth.getCurrentUser();
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(stringName)
.setPhotoUri(Uri.parse(stringUserImage))
.build();
user.updateProfile(profileUpdates)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
firebaseFirestore.collection("UserData").document(stringUserID).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
alertGoogleSignIn.dismiss(); // dismiss dialog here
if (documentSnapshot.getData() != null && documentSnapshot.getData().size() > 0) {
Snackbar.make(findViewById(android.R.id.content), "Welcome back!", Snackbar.LENGTH_LONG).show();
startActivity(new Intent(ActivityLogin.this, ActivityHome.class));
finish();
} else {
createUserData(stringUserID, stringUserImage, stringName, stringEmail);
}
}
});
}
}
});
}
I hope this helps you

Platform specific code error: MissingPluginException

I want to send an Notification in Flutter, so I set up my platform specific code (only Android) but I get the following error back:
Unhandled Exception: MissingPluginException(No implementation found for method send_notification on channel reminderChannel)
I already cleaned the project but still not working.
Future to invoke method:
const platform = const MethodChannel("reminderChannel");
Future<void> invokeMethod() async {
try {
//FIXME Missing plugin
int testValue = await platform.invokeMethod("send_notification");
} on PlatformException catch (e) {}
}
invokeMethod();
mainActivity:
private static final String notificationChannel = "reminderChannel";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), notificationChannel).setMethodCallHandler(
new MethodCallHandler() {
#Override
public void onMethodCall(MethodCall methodCall, Result result) {
if (methodCall.method.equals("send_notification")) {
System.out.print("Android Method called");
result.success(5);
} else {
result.notImplemented();
}
}
}
);
}
I want that the testValue variable in invokeMethod equals 5.
Thanks for helping.
I suspect your channel is deallocated at the end of the method.
So keep a reference to the MethodChannel in your acivity:
private static final String notificationChannel = "reminderChannel";
private MethodChannel mainChannel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
reminderChannel = new MethodChannel(getFlutterView(), notificationChannel)
reminderChannel.setMethodCallHandler(
new MethodCallHandler() {
#Override
public void onMethodCall(MethodCall methodCall, Result result) {
if (methodCall.method.equals("send_notification")) {
System.out.print("Android Method called");
result.success(5);
} else {
result.notImplemented();
}
}
}
);
}