Flutter学习中遇到的问题:已有项目加入Flutter模块

本文主要尝试解决如下几个问题:

  1. 如何在在已经项目加入Flutter
  2. 混合跳转
  3. 混合栈问题
  4. 混合栈数据问题

跳转黑屏是因为debug的缘故,打release包则没有。

af.gif

1. 如何在在已经项目加入Flutter

直接参考这篇文章Add Flutter to existing apps

2. 混合跳转

首先:Android跳转到Flutter

创建一个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:发条消息给你"), 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
    以上需要根据实际情况处理,存在特殊跳转,销毁等逻辑
开发中需要特别需要注意的一个问题:

跳转顺序: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学习中的问题记录: 如何监听实体/虚拟返回键和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中初始化,监听,发送

为什么要延迟发送?
因为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:发条消息给你"), 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中监听,发送
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:发条消息给你');
     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,后续遇到问题解决后会整理发布,或者你们觉得可能存在的问题,可以私信我,留言,空余时间会尽力尝试解决。

已有项目集成到Flutter代码已经上传到我的GITHUB

知乎日报Flutter版代码已经上传到我的GITHUB

基础学习过程中的代码都放在GITHUB

每天学一点,学到Flutter发布正式版!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,805评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,700评论 2 59
  • 获取View的宽/高 在Activity的onCreate、onStart、onResume中均无法正确得到某个V...
    TomyZhang阅读 177评论 0 0
  • 我本人工作十三年零三个月,但在工作和生活的某一个点,我还是一个小白。不知道你是不是有这样的感觉,嗯?这个点...
    木子默阅读 640评论 0 0
  • 自爱就是自己尊重自己、爱护自己,只有做到自爱,才能得到他人的尊重与爱护。一个不自爱的人,当然不会去爱别人、尊重别人...
    胜者为王王臣森阅读 93评论 0 1