Flutter學習中遇到的問題:已有項目加入Flutter模塊

本文主要嘗試解決如下幾個問題:

  1. 如何在在已經項目加入Flutter
  2. 混合跳轉
  3. 混合棧問題
  4. 混合棧數據問題

跳轉黑屏是因為debug的緣故,打release包則沒有。

af.gif

1. 如何在在已經項目加入Flutter

直接參考這篇文章Add Flutter to existing apps

2. 混合跳轉

首先:Android跳轉到Flutter

創(chuàng)建一個FlutterActivity

直接繼承FlutterActivity或者自定義一個CustomFlutterActivity,我用的自定義,是做了一些封裝和flutter插件注冊

public abstract class CustomFlutterActivity extends AppCompatActivity implements FlutterView.Provider, PluginRegistry, FlutterActivityDelegate.ViewFactory {

    private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
    private final FlutterActivityEvents eventDelegate;
    private final FlutterView.Provider viewProvider;
    private final PluginRegistry pluginRegistry;

    public CustomFlutterActivity() {
        this.eventDelegate = this.delegate;
        this.viewProvider = this.delegate;
        this.pluginRegistry = this.delegate;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if (injectRouter())
            ARouter.getInstance().inject(this);
        super.onCreate(savedInstanceState);
        this.eventDelegate.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        registerCustomPlugin(this);
    }

    protected boolean injectRouter() {
        return false;
    }

    //省略部分代碼
    .........
    .........
    private static void registerCustomPlugin(PluginRegistry registrar) {

        FlutterPluginJumpToAct.registerWith(registrar.registrarFor(FlutterPluginJumpToAct.CHANNEL));

    }
}
做一個Flutter用的容器,通過容器統一管理需要跳轉的Flutter界面
@Route(path = RouterPath.MainPath.MAIN_FLUTTER_CONTAINER, name = "FlutterContainerActivity")
public class FlutterContainerActivity extends CustomFlutterActivity {

    private static String CHANNEL = "com.jzhu.msg/plugin";

    private static int TIME_ONE_SECOND = 1000;

    @Autowired(name = "path")
    String path;

    @Override
    protected boolean injectRouter() {
        return true;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initBasicMessageChannel();
    }

    @Override
    public FlutterView createFlutterView(Context context) {
        WindowManager.LayoutParams matchParent = new WindowManager.LayoutParams(-1, -1);
        FlutterNativeView nativeView = this.createFlutterNativeView();
        FlutterView flutterView = new FlutterView(FlutterContainerActivity.this, (AttributeSet) null, nativeView);
        flutterView.setInitialRoute(path);
        flutterView.setLayoutParams(matchParent);
        this.setContentView(flutterView);
        return flutterView;
    }

    private void initBasicMessageChannel() {
        switch (path) {
            case RouterPath.ModuleFlutterPath.FLUTTER_HOME:
                initHomeBasicMessage();
                break;
            case RouterPath.ModuleFlutterPath.FLUTTER_TEST:
                initTestBasicMessage();
                break;
        }

    }

   private void initTestBasicMessage() {
        BasicMessageChannel channel = new BasicMessageChannel<String>(
                getFlutterView(), CHANNEL, StringCodec.INSTANCE);
        channel.setMessageHandler((o, reply) -> {
            ToastUtils.show((String)o,3000);
            reply.reply("FlutterContainerActivity:回條消息給你");
        });
    
        new Handler().postDelayed(() -> channel.send("FlutterContainerActivity:發(fā)條消息給你"), TIME_ONE_SECOND);
    }

    private void initHomeBasicMessage() {
        //todo
    }

}
Flutter提供的頁面
void main() {
  runApp(new MaterialApp(
    routes: <String, WidgetBuilder>{
    '/homepage': (BuildContext context) => new MyHomePage(),
    '/testpage': (BuildContext context) => new TestPage(),
  }, home: new MyHomePage()));
}
路由
public interface RouterPath {

    interface  MainPath{
        String MAIN_FLUTTER_CONTAINER = "/main/FlutterContainerActivity";
        String MAIN_TEST= "/main/TestActivity";
    }

    interface  ModuleFlutterPath{
        String FLUTTER_HOME= "/homepage";
        String FLUTTER_TEST= "/testpage";
    }

}

點擊跳轉
ARouter.getInstance()
               .build(RouterPath.MainPath.MAIN_FLUTTER_CONTAINER)
               .withString("path", RouterPath.ModuleFlutterPath.FLUTTER_HOME)
               .navigation();

最后:Flutter跳轉到Android

通過插件實現,用法參考我之前寫一篇Flutter知識點: Flutter與原生(Android)的交互

3. 混合棧問題

  1. 如果是Android的頁面跳轉到Android頁面,所以就是普通的Activity棧。
  2. 如果是Android的頁面跳轉到Flutter頁面,那么都使用了我們的容器FlutterContainerActivity,所以就是普通的Activity棧,這里面遇到個坑,下面會提出并嘗試解決。
  3. 如果是Flutter的頁面跳轉到Flutter頁面,那么由Flutter自己內部的棧管理。
  4. 如果是Flutter的頁面跳轉到Android頁面,那么自己管自己就好啦。

如果你把Flutter的頁面全裝到到容器FlutterContainerActivity展示,那就都是普通的Activity棧,省事!

混合跳轉注意:
  1. Android某些頁面的啟動模式Standard,SingleTop,SingleTask,SingleInstance
  2. Flutter頁面也存在一些特殊的跳轉,popAndPushNamed,pushNamedAndRemoveUntil,popUntil,pushReplacementNamed
    以上需要根據實際情況處理,存在特殊跳轉,銷毀等邏輯
開發(fā)中需要特別需要注意的一個問題:

跳轉順序:Android頁面 -> Flutter頁面(使用了FlutterContainerActivity)-> Flutter頁面(原始)
期望結果:Flutter頁面(原始)-> Flutter頁面(使用了FlutterContainerActivity)-> Android頁面
真實結果:Flutter頁面(原始)-> Flutter頁面(使用了FlutterContainerActivity)-> Flutter頁面(homepage) -> Android頁面
得到疑問:我們并沒有啟動homepage,為什么多了一個homepage?
代碼猜想:Flutter棧里初始化了一個homepage, 其他Flutter的頁面都在這個棧之上。

void main() {
  runApp(new MaterialApp(
    routes: <String, WidgetBuilder>{
    ............
  }, home: new MyHomePage()));
}

如何解決:使用SystemNavigator.pop(),
Tells the operating system to close the application, or the closest equivalent.
WillPopScope參考這篇Flutter學習中的問題記錄: 如何監(jiān)聽實體/虛擬返回鍵和AppBar返回鍵

class _MyHomePageState extends State<MyHomePage> {
  static const jumpPlugin = const MethodChannel('com.jzhu.jump/plugin');

  Future<Null> _jumpToNative() async {
    Map<String, String> map = {"path": "/main/TestActivity"};

    String result = await jumpPlugin.invokeMethod('jump2act', map);
    print(result);
  }

  Future<bool> _requestPop() {
    SystemNavigator.pop();
    return new Future.value(false);
  }


  @override
  Widget build(BuildContext context) {
    return new WillPopScope(
        child: new Scaffold(
          appBar: new AppBar(
            title: new Text("Home Page"),
          ),
          body: new Center(
            child: new RaisedButton(
                child: new Text("跳到TestActivity"), onPressed: _jumpToNative),
          ),
          // This trailing comma makes auto-formatting nicer for build methods.
        ),
        onWillPop: _requestPop);
  }
}

4. 混合棧數據問題,以插件的方式解決 。

MethodChannel,EventChanneld的數據傳輸,用法參考我之前寫一篇Flutter知識點: Flutter與原生(Android)的交互

主要舉例BasicMessageChannel和BinaryMessages

1_Sd6s3EDGkU8TBS9xLc4Zvw.png
FlutterContainerActivity中初始化,監(jiān)聽,發(fā)送

為什么要延遲發(fā)送?
因為FlutterView可能還沒初始化,這時候無法接收消息

 private static String CHANNEL = "com.jzhu.msg/plugin";
 private static String CHANNEL_BINARY = "com.jzhu.msg.binary/plugin";
 private static int TIME_ONE_SECOND = 1000;

  private void initTestBasicMessage() {
        BasicMessageChannel channel = new BasicMessageChannel<String>(
                getFlutterView(), CHANNEL, StringCodec.INSTANCE);
        channel.setMessageHandler((o, reply) -> {
            ToastUtils.show((String)o,3000);
            reply.reply("FlutterContainerActivity:回條消息給你");
        });
        new Handler().postDelayed(() -> channel.send("FlutterContainerActivity:發(fā)條消息給你"), TIME_ONE_SECOND);

        ByteBuffer message = ByteBuffer.allocateDirect(256);
        message.putDouble(3.14);
        message.putInt(123456789);
        new Handler().postDelayed(() -> getFlutterView().send(CHANNEL_BINARY,message), TIME_ONE_SECOND);
        getFlutterView().setMessageHandler(CHANNEL_BINARY, (byteBuffer, binaryReply) -> {
            byteBuffer.order(ByteOrder.nativeOrder());
            double x = byteBuffer.getDouble();
            int n = byteBuffer.getInt();
            Log.i("zj", "Received: "+x+ " and "+ n);
            binaryReply.reply(message);
        });
    }
Flutter中監(jiān)聽,發(fā)送
static const channel = const BasicMessageChannel<String>('com.jzhu.msg/plugin', StringCodec());

static const String channelBinary = 'com.jzhu.msg.binary/plugin';

void _sendMsg2Android() async {

     _replyMsg = await channel.send('TestPage:發(fā)條消息給你');
     setState(() {});

     final WriteBuffer buffer = WriteBuffer()
       ..putFloat64(3.14)
       ..putInt32(123456789);
     final ByteData message = buffer.done();
     _replyBinaryMsg = await BinaryMessages.send(channelBinary, message);
     _decodeData(message);

  }

  void _initMessageHandler() {

    channel.setMessageHandler((String message) async {
      _receivedMsg  = message;
      setState(() {});
      });

    BinaryMessages.setMessageHandler(channelBinary, (ByteData message) async {
      _decodeData(message);
    });

  }

  void _decodeData(ByteData message){
    final ReadBuffer readBuffer = ReadBuffer(message);
    final double x = readBuffer.getFloat64();
    final int n = readBuffer.getInt32();
    print('Received $x and $n');
  }

疑問:已有項目集成到Flutter,自定義插件應該放在哪注冊,什么時機注冊?有想法的私信留言謝謝!

已有項目集成到Flutter,后續(xù)遇到問題解決后會整理發(fā)布,或者你們覺得可能存在的問題,可以私信我,留言,空余時間會盡力嘗試解決。

已有項目集成到Flutter代碼已經上傳到我的GITHUB

知乎日報Flutter版代碼已經上傳到我的GITHUB

基礎學習過程中的代碼都放在GITHUB

每天學一點,學到Flutter發(fā)布正式版!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,716評論 25 709
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,898評論 2 59
  • 獲取View的寬/高 在Activity的onCreate、onStart、onResume中均無法正確得到某個V...
    TomyZhang閱讀 224評論 0 0
  • 我本人工作十三年零三個月,但在工作和生活的某一個點,我還是一個小白。不知道你是不是有這樣的感覺,嗯?這個點...
    木子默閱讀 754評論 0 0
  • 自愛就是自己尊重自己、愛護自己,只有做到自愛,才能得到他人的尊重與愛護。一個不自愛的人,當然不會去愛別人、尊重別人...
    勝者為王王臣森閱讀 125評論 0 1

友情鏈接更多精彩內容