美文网首页Flutter圈子Android开发经验谈All in Flutter
Flutter学习中遇到的问题:已有项目加入Flutter模块

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

作者: jzhu085 | 来源:发表于2018-09-24 22:17 被阅读199次

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

    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.
    onWillPop参考这篇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发布正式版!

    相关文章

      网友评论

      • 7c78a9d8ace3:博主这个一看就知道是正真的实践过,比那些百度上你复制我,我复制你的博客主好多了.关注一波:smiley:
        jzhu085:@帅呆sky 。。。。谢谢,我每个文章都贴代码的,有地址
      • walter_feng:我看了你的实现方法。原生跳Flutter,先跳到指定Flutter页面,然后原生postDelayed里给这个页面发消息,把参数传过去。
        那么为题来了,跳转到Flutter后我需要在initState里拿原生传过来的参数请求网络,这个时候参数还没发送过来...。
        :fearful:
        很奇怪flutterView为什么不能直接setArguments传参数,只能setInitialRoute。而FlutterView本身是支持参数的。( flutterView.runFromBundle(arguments) )
        jzhu085:@walter_feng 这个我知道,栈管理比较麻烦
        walter_feng:可以在initialRoute添加url参数,例如:goodsDetail?goodsId=154231584
        然后Flutter端 runApp(_widgetForRoute(window.defaultRouteName)),window.defaultRouteName可以拿到这个字符串(goodsDetail?goodsId=154231584),再路由并解析参数就可以了
        jzhu085:@walter_feng 我之前用FlutterView试过了。。不行,所以才想到用延迟发送的方式。。。:scream: 先玩着呗,等下版本release了再看看,只能说勉强能用,还不是特别好

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

      本文链接:https://www.haomeiwen.com/subject/qhncoftx.html