Flutter學(xué)習(xí)中遇到的問(wèn)題:已有項(xiàng)目加入Flutter模塊

本文主要嘗試解決如下幾個(gè)問(wèn)題:

  1. 如何在在已經(jīng)項(xiàng)目加入Flutter
  2. 混合跳轉(zhuǎn)
  3. 混合棧問(wèn)題
  4. 混合棧數(shù)據(jù)問(wèn)題

跳轉(zhuǎn)黑屏是因?yàn)閐ebug的緣故借卧,打release包則沒(méi)有畦木。

af.gif

1. 如何在在已經(jīng)項(xiàng)目加入Flutter

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

2. 混合跳轉(zhuǎn)

首先:Android跳轉(zhuǎn)到Flutter

創(chuàng)建一個(gè)FlutterActivity

直接繼承FlutterActivity或者自定義一個(gè)CustomFlutterActivity蛙粘,我用的自定義亲茅,是做了一些封裝和flutter插件注冊(cè)

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));

    }
}
做一個(gè)Flutter用的容器闪檬,通過(guò)容器統(tǒng)一管理需要跳轉(zhuǎn)的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提供的頁(yè)面
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";
    }

}

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

最后:Flutter跳轉(zhuǎn)到Android

通過(guò)插件實(shí)現(xiàn)派昧,用法參考我之前寫一篇Flutter知識(shí)點(diǎn): Flutter與原生(Android)的交互

3. 混合棧問(wèn)題

  1. 如果是Android的頁(yè)面跳轉(zhuǎn)到Android頁(yè)面,所以就是普通的Activity棧剃浇。
  2. 如果是Android的頁(yè)面跳轉(zhuǎn)到Flutter頁(yè)面巾兆,那么都使用了我們的容器FlutterContainerActivity猎物,所以就是普通的Activity棧,這里面遇到個(gè)坑角塑,下面會(huì)提出并嘗試解決蔫磨。
  3. 如果是Flutter的頁(yè)面跳轉(zhuǎn)到Flutter頁(yè)面,那么由Flutter自己內(nèi)部的棧管理圃伶。
  4. 如果是Flutter的頁(yè)面跳轉(zhuǎn)到Android頁(yè)面堤如,那么自己管自己就好啦。

如果你把Flutter的頁(yè)面全裝到到容器FlutterContainerActivity展示窒朋,那就都是普通的Activity棧搀罢,省事!

混合跳轉(zhuǎn)注意:
  1. Android某些頁(yè)面的啟動(dòng)模式Standard侥猩,SingleTop榔至,SingleTask,SingleInstance
  2. Flutter頁(yè)面也存在一些特殊的跳轉(zhuǎn)欺劳,popAndPushNamed唧取,pushNamedAndRemoveUntil,popUntil划提,pushReplacementNamed
    以上需要根據(jù)實(shí)際情況處理兵怯,存在特殊跳轉(zhuǎn),銷毀等邏輯
開發(fā)中需要特別需要注意的一個(gè)問(wèn)題:

跳轉(zhuǎn)順序:Android頁(yè)面 -> Flutter頁(yè)面(使用了FlutterContainerActivity)-> Flutter頁(yè)面(原始)
期望結(jié)果:Flutter頁(yè)面(原始)-> Flutter頁(yè)面(使用了FlutterContainerActivity)-> Android頁(yè)面
真實(shí)結(jié)果:Flutter頁(yè)面(原始)-> Flutter頁(yè)面(使用了FlutterContainerActivity)-> Flutter頁(yè)面(homepage) -> Android頁(yè)面
得到疑問(wèn):我們并沒(méi)有啟動(dòng)homepage腔剂,為什么多了一個(gè)homepage媒区?
代碼猜想:Flutter棧里初始化了一個(gè)homepage, 其他Flutter的頁(yè)面都在這個(gè)棧之上掸犬。

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學(xué)習(xí)中的問(wèn)題記錄: 如何監(jiān)聽(tīng)實(shí)體/虛擬返回鍵和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. 混合棧數(shù)據(jù)問(wèn)題,以插件的方式解決 湾碎。

MethodChannel宙攻,EventChanneld的數(shù)據(jù)傳輸,用法參考我之前寫一篇Flutter知識(shí)點(diǎn): Flutter與原生(Android)的交互

主要舉例BasicMessageChannel和BinaryMessages

1_Sd6s3EDGkU8TBS9xLc4Zvw.png
FlutterContainerActivity中初始化介褥,監(jiān)聽(tīng)座掘,發(fā)送

為什么要延遲發(fā)送?
因?yàn)镕lutterView可能還沒(méi)初始化柔滔,這時(shí)候無(wú)法接收消息

 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)聽(tīng)溢陪,發(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');
  }

疑問(wèn):已有項(xiàng)目集成到Flutter,自定義插件應(yīng)該放在哪注冊(cè)睛廊,什么時(shí)機(jī)注冊(cè)形真?有想法的私信留言謝謝!

已有項(xiàng)目集成到Flutter超全,后續(xù)遇到問(wèn)題解決后會(huì)整理發(fā)布咆霜,或者你們覺(jué)得可能存在的問(wèn)題邓馒,可以私信我,留言蛾坯,空余時(shí)間會(huì)盡力嘗試解決光酣。

已有項(xiàng)目集成到Flutter代碼已經(jīng)上傳到我的GITHUB

知乎日?qǐng)?bào)Flutter版代碼已經(jīng)上傳到我的GITHUB

基礎(chǔ)學(xué)習(xí)過(guò)程中的代碼都放在GITHUB

每天學(xué)一點(diǎn),學(xué)到Flutter發(fā)布正式版脉课!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挂疆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子下翎,更是在濱河造成了極大的恐慌,老刑警劉巖宝当,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件视事,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡庆揩,警方通過(guò)查閱死者的電腦和手機(jī)俐东,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)订晌,“玉大人虏辫,你說(shuō)我怎么就攤上這事⌒獠Γ” “怎么了砌庄?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)奕枢。 經(jīng)常有香客問(wèn)我娄昆,道長(zhǎng),這世上最難降的妖魔是什么缝彬? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任萌焰,我火速辦了婚禮,結(jié)果婚禮上谷浅,老公的妹妹穿的比我還像新娘扒俯。我一直安慰自己,他們只是感情好一疯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布撼玄。 她就那樣靜靜地躺著,像睡著了一般墩邀。 火紅的嫁衣襯著肌膚如雪互纯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天磕蒲,我揣著相機(jī)與錄音留潦,去河邊找鬼只盹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛兔院,可吹牛的內(nèi)容都是我干的殖卑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼坊萝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼孵稽!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起十偶,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤菩鲜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后惦积,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體接校,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年狮崩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蛛勉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睦柴,死狀恐怖诽凌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情坦敌,我是刑警寧澤侣诵,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站狱窘,受9級(jí)特大地震影響窝趣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜训柴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一哑舒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幻馁,春花似錦洗鸵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至稀拐,卻和暖如春火邓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工铲咨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躲胳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓纤勒,卻偏偏與公主長(zhǎng)得像坯苹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摇天,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,520評(píng)論 25 707
  • 用兩張圖告訴你粹湃,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,693評(píng)論 2 59
  • 獲取View的寬/高 在Activity的onCreate泉坐、onStart为鳄、onResume中均無(wú)法正確得到某個(gè)V...
    TomyZhang閱讀 176評(píng)論 0 0
  • 我本人工作十三年零三個(gè)月,但在工作和生活的某一個(gè)點(diǎn)腕让,我還是一個(gè)小白孤钦。不知道你是不是有這樣的感覺(jué),嗯?這個(gè)點(diǎn)...
    木子默閱讀 634評(píng)論 0 0
  • 自愛(ài)就是自己尊重自己记某、愛(ài)護(hù)自己,只有做到自愛(ài)构捡,才能得到他人的尊重與愛(ài)護(hù)液南。一個(gè)不自愛(ài)的人,當(dāng)然不會(huì)去愛(ài)別人勾徽、尊重別人...
    勝者為王王臣森閱讀 91評(píng)論 0 1