美文网首页
Android 原生项目集成Flutter

Android 原生项目集成Flutter

作者: leilifengxingmw | 来源:发表于2019-04-10 23:22 被阅读0次

    可以将两个Flutter module宿主App克隆到同一个目录下面运行看看效果。

    创建Flutter module

    假定存在的Android原生项目目录是some/path/MyApp,在MyApp所在的目录下创建一个flutter module

    $ cd some/path/
    $ flutter create -t module my_flutter
    

    上面的命令会创建一个名为my_flutter的flutter module,并且会在my_flutter目录下生成一个./android的隐藏子目录,这个目录的作用就是将创建好的flutter module包装成一个 android library

    创建好的flutter module会为我们自动生成一个 main.dart。我们可以直接使用Android studio打开创建好的flutter module,先运行一下看下效果。

    main.jpg

    我们修改一下代码去掉顶部的AppBar,修改后页面如下图所示。

    remove_appbar.jpg

    宿主app的要求

    在连接flutter module和宿主app之前,你需要确保在宿主app的build.gradle文件中声明如下

    android {
        //...
        compileOptions {
            sourceCompatibility 1.8
            targetCompatibility 1.8
        }
    }
    

    让宿主app依赖flutter module

    在宿主app的settings.gradle中把flutter module包含进来作为子项目

    // MyApp/settings.gradle
    include ':app'                                     // assumed existing content
    setBinding(new Binding([gradle: this]))                                 // new
    evaluate(new File(                                                      // new
      settingsDir.parentFile,                                               // new
      'my_flutter/.android/include_flutter.groovy'                          // new
    ))                                                                      // new
    

    然后在app/build.gradle文件中声明如下

    // MyApp/app/build.gradle
    
    dependencies {
      implementation project(':flutter')
      //...
    }
    

    然后同步一下项目宿主工程MyApp。

    在Java代码中使用Flutter module

    使用 flutter module

    使用flutter module生成的Java API 来添加 Flutter views 到宿主app中。flutter module生成的Java API 路径如下图所示。

    path.png
    1. 可以直接使用Flutter.createView实现添加 Flutter views 到宿主app中。

    宿主app的MainActivity的布局文件如下

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/btnFlutterCreateView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="使用Flutter.createView方法"
            android:textAllCaps="false"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <Button
            android:id="@+id/btnFlutterFragment"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="使用FlutterFragment"
            android:textAllCaps="false"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btnFlutterCreateView" />
    
        <FrameLayout
            android:id="@+id/flContainer"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/btnFlutterFragment" />
    
    </android.support.constraint.ConstraintLayout>
    

    当点击btnFlutterCreateView按钮的时候,我们创建一个flutterView,然后添加到布局文件中的flContainer中去。

    // MyApp/app/src/main/java/some/package/MainActivity.java
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            //创建flutterView
            View flutterView = Flutter.createView(MainActivity.this, getLifecycle(), null);
            FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT, 800);
            addContentView(flutterView, layout);
        }
    
    });
    
    

    看下运行效果


    createView.jpg
    1. 使用Flutter.createFragment方法创建一个Fragment,使用Flutter.createFragment方法我们不需要传递一个Lifecycle对象,FlutterFragment可以自己处理生命周期。
    // MyApp/app/src/main/java/some/package/SomeActivity.java
      private fun createFragment() {
          supportFragmentManager
                  .beginTransaction()
                  .replace(R.id.flContainer, Flutter.createFragment(null))
                  .commit()
    }
    

    运行效果是和上面的一样的,就不贴图了。

    我们看一下Flutter.createViewFlutter.createFragment的方法签名。

    public static FlutterView createView(final Activity activity, final Lifecycle lifecycle, 
    final String initialRoute) {
        //...
    }
    
    public static FlutterFragment createFragment(String initialRoute) {
        //...
    }
    
    

    这两个方法都有一个initialRoute参数,这个是标记加载的flutter的初始界面。我们可以传递不同的initialRoute来打开不同的界面。在flutter中,可以通过window.defaultRouteName获取这个参数值。下面展示一个打开不同界面的例子。

    1. 在flutter_module中新建一个route1.dart
    import 'package:flutter/material.dart';
    
    ///
    /// Created by dumingwei on 2019/4/9.
    /// Desc:  rout1
    ///
    
    class Route1App extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Route1(),
        );
      }
    }
    
    class Route1 extends StatefulWidget {
      String text;
    
      Route1({Key key, this.text}) : super(key: key);
    
      @override
      State createState() {
        return Route1State();
      }
    }
    
    class Route1State extends State<Route1> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Center(
              child: Text(
                'Hello world ,i am route1',
              ),
            ),
          ),
        );
      }
    }
    
    1. 修改一下flutter module的main.dart

    flutter module的入口是lib/main.dart。默认创建的widget是MyApp

    import 'package:flutter/material.dart';
    
    ///lib/main.dart
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget{
       ///...
    }
    
    

    现在我们要根据传入的initialRoute值来初始化widget。

    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    
    import 'route1.dart';
    
    //调用window.defaultRouteName获取传入的传入的`initialRoute`值
    void main() => runApp(_widgetForRoute(window.defaultRouteName));
    
    Widget _widgetForRoute(String route) {
      switch (route) {
        case 'route1'://创建Route1App
          return Route1App();
        default:
          return MyApp();
      }
    }
    //...
    

    在上面的代码中如果传入的initialRoute值是'route1',我们就初始化Route1App。

    下面修改宿主app中MainActivity的代码。

    // MyApp/app/src/main/java/some/package/SomeActivity.java
    
     override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            btnFlutterFragment.setOnClickListener {
                createFragment()
            }
        }
    
      private fun createFragment() {
          supportFragmentManager
                  .beginTransaction()
                  .replace(R.id.flContainer, Flutter.createFragment("route1"))
                  .commit()
      }
    
    

    在上面的代码中Flutter.createFragment()方法我们传入的initialRoute是"route1"。

    现在我们重新运行一下MyApp,效果如下。

    route1.jpg

    热加载/热重启和调试Dart代码

    完整的IDE集成以支持使用混合应用程序的Flutter / Dart代码的工作正在进行中。但是flutter命令行工具和Dart Observatory web user interface已经提供了一些基本功能。

    连接真机或者模拟器。然后使Flutter 命令行工具监听您的应用程序。

    切换到flutter module的目录下,在命令行输入下面的命令

    $ cd some/path/my_flutter
    $ flutter attach
    Waiting for a connection from Flutter on LLD AL20...
    
    

    然后使用debug模式启动宿主app,然后导航到使用flutter的地方。然后回到命令行(我这里使用的命令行就是用Android Studio 打开 my_flutter,Android Studio中自带的命令行),可以看到类似下面输出的信息。

    Done.
    Syncing files to device LLD AL20...                              1,752ms
    
    🔥  To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
    An Observatory debugger and profiler on LLD AL20 is available at: http://127.0.0.1:54043/
    For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
    
    

    现在你可以修改flutter中的代码,然后在命令行里输入r来进行热更新。输入'R'来进行热重启(重新构建flutter widget的状态)。你也可以黏贴上面输出信息中的URL到浏览器中来使用Dart Observatory来设置断点,分析内存保留以及其他调试任务。更多详细的帮助信息请输入h,断开连接请输入d,退出请输入q

    我们先来试一试热更新。按照上面的步骤,使用debug的模式启动MyApp以后,我们点击使用Flutter.createView方法按钮,输出如下。

    route1.jpg

    然后我们修改my_flutter中的route1.dart中显示的文字。

    class Route1App extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Route1(),
        );
      }
    }
    
    class Route1 extends StatefulWidget {
      String text;
    
      Route1({Key key, this.text}) : super(key: key);
    
      @override
      State createState() {
        return Route1State();
      }
    }
    
    class Route1State extends State<Route1> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Center(
              child: Text(
                //修改文字
                'Hello world ,i am route1 haha',
              ),
            ),
          ),
        );
      }
    }
    
    

    然后在命令行里输入r。发现热更新起作用了。

    perss_r.jpg

    关于Dart Observatory以后再研究。

    Flutter module源码
    宿主App源码

    参考链接

    1. Android 原生项目集成Flutter

    2. Add Flutter to existing apps

    3. 原生App项目集成flutter混合开发详细指南

    相关文章

      网友评论

          本文标题:Android 原生项目集成Flutter

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