title: how to call android native code(both android/IOS) properly from flutter?
body: <p>I'm a beginner for flutter. And honestly don't know Android Studio.
  my goal is to achieve local(system) authentication. So got <code>local_auth</code> plugin which only gives you a fingerprint option. what if the user doesn't have fingerprint hardware? so I planned to achieve this thing by calling native code (Android) from flutter.
  But the problem is I can't authenticate users properly.
  For example, while authentication going the response is coming from native code.
  I can't suspend the execution of the function until authentication happens. Tried synchronized methods
  but still no luck.</p>
  
  <p>my dart code:</p>
  
  <pre><code>import 'package:flutter/material.dart';
  import 'package:flutter/services.dart';
  
  void main() =&gt; runApp(Container(
    child:   Row(
      children: &lt;Widget&gt;[
            Column(
          children: &lt;Widget&gt;[
                Padding(
              padding: const EdgeInsets.all(8.0),
              child:   Center(child: MyApp()),
            ),
          ],
        ),
      ],
    ),
  ));
  
  class MyApp extends StatelessWidget {
    // This widget is the root of your application.
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
  
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(title: 'Flutter Demo Home Page'),
      );
    }
  }
  
  class MyHomePage extends StatefulWidget {
    MyHomePage({Key key, this.title}) : super(key: key);
    final String title;
  
    @override
    _MyHomePageState createState() =&gt; _MyHomePageState();
  }
  
  class _MyHomePageState extends State&lt;MyHomePage&gt; {
    int _counter = 0;
  
    void _incrementCounter() {
  //    setState(() {
  //      // This call to setState tells the Flutter framework that something has
  //      // changed in this State, which causes it to rerun the build method below
  //      // so that the display can reflect the updated values. If we changed
  //      // _counter without calling setState(), then the build method would not be
  //      // called again, and so nothing would appear to happen.
  //      _counter++;
  //    });
  //    my_count();
  //  var cc = code().toString();
  //  print(cc);
  //    if(cc == "200") {
  //      Navigator.push(
  //        context,
  //        MaterialPageRoute(builder: (context) =&gt; SecondRoute()),
  //      );
  //    }else if( cc == "333") {
  //      Navigator.push(context,
  //      MaterialPageRoute(
  //        builder: (context) =&gt; ThirdRoute()
  //      ));
  //    }
    code();
  
    }
  
    @override
    void initState() {
      // TODO: implement initState
      super.initState();
      my_count();
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          // Here we take the value from the MyHomePage object that was created by
          // the App.build method, and use it to set our appbar title.
          title: Text(widget.title),
        ),
        body: Center(
          // Center is a layout widget. It takes a single child and positions it
          // in the middle of the parent.
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: &lt;Widget&gt;[
              Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.display1,
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ), // This trailing comma makes auto-formatting nicer for build methods.
      );
    }
    static const paltform = const MethodChannel('lock');
  
    // ignore: missing_return
    void code() async {
      int ii;
      try {
        ii = await paltform.invokeMethod('value');
      } catch (e){
        print(e);
      }
  //    print(ii);
  //    return ii;
      if(ii == 200) {
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) =&gt; SecondRoute()),
        );
      }else if( ii == 333) {
        Navigator.push(context,
            MaterialPageRoute(
                builder: (context) =&gt; ThirdRoute()
            ));
      }
    }
  
    // ignore: non_constant_identifier_names
    void my_count() async {
      try{
        _counter = await paltform.invokeMethod('getCounter');
      }catch(e){
        print(e);
      }
    }
     //if i call code() here it not executing
    code();
  }
  
  
  
  class SecondRoute extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text("Second Route"),
        ),
        body: Center(
          child: RaisedButton(
            onPressed: () {
              // Navigate back to first route when tapped.
              Navigator.pop(context);
            },
            child: Text('Go back!'),
          ),
        ),
      );
    }
  }
  
  class ThirdRoute extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text("Third Route"),
        ),
        body: Center(
          child: RaisedButton(
            onPressed: () {
              // Navigate back to first route when tapped.
              Navigator.pop(context);
            },
            child: Text('Go back!'),
          ),
        ),
      );
    }
  }
  </code></pre>
  
  <p>what I'm doing is invoking native code from flutter and getting response code from native side
  based on that routing pages
  here is my Android code:</p>
  
  <pre><code>package com.example.native_3;
  
  import androidx.annotation.NonNull;
  import androidx.annotation.RequiresApi;
  
  import io.flutter.embedding.android.FlutterActivity;
  import io.flutter.embedding.engine.FlutterEngine;
  import io.flutter.plugin.common.MethodChannel;
  import io.flutter.plugins.GeneratedPluginRegistrant;
  
  import android.app.KeyguardManager;
  import android.content.Intent;
  import android.os.Build;
  
  public class MainActivity extends FlutterActivity {
  
    private static final int LOCK_REQUEST_CODE = 221;
    private static final int SECURITY_SETTING_REQUEST_CODE = 233;
    int code = 0;
    private static final String CHANNEL = "lock";
  
    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
      GeneratedPluginRegistrant.registerWith(flutterEngine);
      new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
              .setMethodCallHandler(
                      (call, result) -&gt; {
                        // Note: this method is invoked on the main thread.
                        if (call.method.equals("getCounter")) {
                          synchronized (this) {
                            authenticateApp();
                          }
                          int batteryLevel = 0;
  
  
                          if (batteryLevel &gt;= 0) {
                            result.success(batteryLevel);
                          } else {
                            result.error("UNAVAILABLE", "Battery level not available.", null);
                          }
                        } else if (call.method.equals("value")) {
                          result.success(get());
                        }
                        else {
                          result.notImplemented();
                        }
                      }
              );
    }
  
    private int get() {
      return code;
    }
  
    private synchronized void authenticateApp() {
      //Get the instance of KeyGuardManager
      KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
  
      //Check if the device version is greater than or equal to Lollipop(21)
      if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.LOLLIPOP) {
        //Create an intent to open device screen lock screen to authenticate
        //Pass the Screen Lock screen Title and Description
        Intent i = keyguardManager.createConfirmDeviceCredentialIntent("unlock", "to unlock");
        try {
          //Start activity for result
          startActivityForResult(i, LOCK_REQUEST_CODE);
  
        } catch (Exception e) {
  
  //        //If some exception occurs means Screen lock is not set up please set screen lock
  //        //Open Security screen directly to enable patter lock
  //        Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
  //        try {
  //
  //          //Start activity for result
  //          startActivityForResult(intent, SECURITY_SETTING_REQUEST_CODE);
  //        } catch (Exception ex) {
  //
  //          //If app is unable to find any Security settings then user has to set screen lock manually
  //          textView.setText(getResources().getString(R.string.setting_label));
  //        }
        }
      }
    }
  
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  //    super.onActivityResult(requestCode, resultCode, data);
      synchronized (this) {
        switch (requestCode) {
          case LOCK_REQUEST_CODE:
            if (resultCode == RESULT_OK) {
              //If screen lock authentication is success update text
              code = 200;
            } else {
              //If screen lock authentication is failed update text
              code = 333;
            }
            break;
          case SECURITY_SETTING_REQUEST_CODE:
            //When user is enabled Security settings then we don't get any kind of RESULT_OK
            //So we need to check whether device has enabled screen lock or not
            if (isDeviceSecure()) {
              //If screen lock enabled show toast and start intent to authenticate user
              code = 444;
            } else {
              //If screen lock is not enabled just update text
  
              code = 555;
            }
  
            break;
        }
      }
    }
    private boolean isDeviceSecure() {
      KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
  
      //this method only work whose api level is greater than or equal to Jelly_Bean (16)
      return Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.JELLY_BEAN &amp;&amp; keyguardManager.isKeyguardSecure();
  
      //You can also use keyguardManager.isDeviceSecure(); but it requires API Level 23
  
    }
  
    }
  </code></pre>
  
  <p>after running them I'll get the response of success only after pressing the button.
  but I wanted it should route itself accordingly based on the response from native code</p>