在已有Android项目当中开发Flutter 1. 引入Flutter模块 参考 引入flutter
步骤
1.假设有一个 some/path/MyApp的android程序 输入以下命令
1 2 $ cd some/path/ $ flutter create -t module flutter_module
这样就在当前path文件夹里与 MyApp有同层级的模块 flutter_module这个文件夹
2.引入flutter_module 在当前MyApp的 settings.gradle加入:
1 2 3 4 5 setBinding(new Binding([gradle: this])) evaluate(new File( settingsDir.parentFile, 'flutter_module/.android/include_flutter.groovy' ))
3. 用 implementation 将flutter模块引入到app当中 1 2 3 4 5 6 // MyApp/app/build.gradle : dependencies { implementation project(':flutter') : }
2. 创建Flutter容器 实现Flutter与Native交互 首先看下官方提供的两种方法
Use the Flutter module’s Java API to add Flutter views to your host app. This can be done by directly using Flutter.createView // 接下来使用Flutter模块提供的java api去给app增加flutterView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // MyApp/app/src/main/java/some/package/MainActivity.java fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { View flutterView = Flutter.createView( MainActivity.this, getLifecycle(), "route1" ); FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800); layout.leftMargin = 100; layout.topMargin = 200; addContentView(flutterView, layout); } });
也可以创建一个 FlutterFragment,它可以有独立的生命周期
1 2 3 4 5 6 7 8 9 // MyApp/app/src/main/java/some/package/SomeActivity.java fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { FragmentTransaction tx = getSupportFragmentManager().beginTransaction(); tx.replace(R.id.someContainer, Flutter.createFragment("route1")); tx.commit(); } });
再看下Flutter部分的代码,上面我们使用字符串“route1”告诉Dart代码要在Flutter视图中显示哪个组件。 Flutter模块项目模板的 lib/main.dart文件应该打开(或以其他方式解释)提供的路由字符串,该字符串可用作window.defaultRouteName,以确定创建和传递哪个小部件给runApp。示意图,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import 'dart:ui'; import 'package:flutter/material.dart'; void main() => runApp(_widgetForRoute(window.defaultRouteName)); Widget _widgetForRoute(String route) { switch (route) { case 'route1': return SomeWidget(...); case 'route2': return SomeOtherWidget(...); default: return Center( child: Text('Unknown route: $route', textDirection: TextDirection.ltr), ); } }
完全取决于您想要的路径字符串以及如何解释它们。就是传相应的route显示相应的值 通过以上的代码
通过以上代码 我们可以有多种flutter也Activity结合方案 图示
针对上个图片 我们可以看出一个login模块可以创建多个FlutterView实例,来进行客户的定制化,也可以同一个FlutterView来对应多个Widget。相比创建多个FlutterView实例,创建单个FlutterView更省资源。
目前适合我们目前现有的开发方式 还是一个Login模块对应一个FlutterView容器对应一种定制化FlutterWidget。在接下来的篇章会讲解整个实践的过程
3.Arndoird-Flutter 实践篇 1.在一个已有的Activity当中起使用FlutterView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 final FlutterView flutterView2 = Flutter.createView( GuideActivity.this , getLifecycle(), "/register" ); btnGoAllFlutter.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { L.e(TAG,"点击22222" ); final FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); addContentView(flutterView2, layout); flutterView2.addFirstFrameListener(new FlutterView.FirstFrameListener() { @Override public void onFirstFrame () { L.e(TAG, "2222第一帧" ); } }); } }); void main () => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build (BuildContext context) { return MaterialApp( title: 'Flutter Demo' , theme: ThemeData( primarySwatch: Colors.blue, ), routes: { LoginPage.sName: (context) { return LoginPage(); }, RegisterPage.sName: (context) { return RegisterPage(); }, cus1Login.sName: (context) { return cus1Login(); }, Test.sName: (context) { return Test(); } }, ); } }
实践结果 能跳转,但是在使用TextField这个widget时 出现无法自动弹出输入框,route可以根据自己传入的参数,跳转到指定页面
2.重新写一个Activity 继承FlutterActivity实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class Customer1Activity extends FlutterActivity implements LifecycleOwner,MethodChannel.MethodCallHandler{ private LifecycleRegistry mLifecycleRegistry; public static final String TAG = Customer1Activity.class.getSimpleName(); private static final String CHANNEL = "com.ty.test/android"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLifecycleRegistry = new LifecycleRegistry(this); mLifecycleRegistry.markState(Lifecycle.State.CREATED); // FlutterMain.startInitialization(this); L.e(TAG,"定制一Activity的第一个页面"); //注册通道 final FlutterView flutterView = Flutter.createView( Customer1Activity.this, mLifecycleRegistry, "/" ); GeneratedPluginRegistrant.registerWith(this); ToastPlugin.register(this,flutterView); //插件必须要和相应的flutterView绑定 ApiPlugin.register(this,flutterView); final FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); addContentView(flutterView, layout); //解决黑屏问题 尝试一下 flutterView.addFirstFrameListener(new FlutterView.FirstFrameListener() { @Override public void onFirstFrame() { L.e(TAG, "定制1第一帧"); } }); // FlutterActivity } @Override protected void onStart() { super.onStart(); mLifecycleRegistry.markState(Lifecycle.State.STARTED); } @NonNull @Override public Lifecycle getLifecycle() { return mLifecycleRegistry; } @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { } }
实践结果:输入框无法点击,看来这样写还是有问题,且继承FlutterActivity 就要放弃继承我们代码中封装过BaseActivity 所以将出现第三种写法
3.继承Activity并实现FlutterActivity相关接口 自己通过相关接口来写一个Flutter容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 public class TYFlutterActivity extends Activity implements PluginRegistry, FlutterActivityDelegate.ViewFactory, FlutterView.Provider, LifecycleOwner { private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this); private final FlutterActivityEvents eventDelegate; private final FlutterView.Provider viewProvider; private final PluginRegistry pluginRegistry; private FlutterView flutterView2; public TYFlutterActivity() { this.eventDelegate = this.delegate; this.viewProvider = this.delegate; this.pluginRegistry = this.delegate; } private LifecycleRegistry mLifecycleRegistry; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLifecycleRegistry = new LifecycleRegistry(this); mLifecycleRegistry.markState(Lifecycle.State.CREATED); // flutterView2 = Flutter.createView( // TYFlutterActivity.this, // getLifecycle(), // "/register" // ); this.eventDelegate.onCreate(savedInstanceState); ToastPlugin.register(this,this.getFlutterView()); //插件必须要和相应的flutterView绑定 ApiPlugin.register(this,this.getFlutterView()); GeneratedPluginRegistrant.registerWith(this); } protected void onStart() { super.onStart(); this.eventDelegate.onStart(); } protected void onResume() { super.onResume(); this.eventDelegate.onResume(); } protected void onDestroy() { this.eventDelegate.onDestroy(); super.onDestroy(); } public void onBackPressed() { if (!this.eventDelegate.onBackPressed()) { super.onBackPressed(); } } protected void onStop() { this.eventDelegate.onStop(); super.onStop(); } protected void onPause() { super.onPause(); this.eventDelegate.onPause(); } protected void onNewIntent(Intent intent) { this.eventDelegate.onNewIntent(intent); } public void onUserLeaveHint() { this.eventDelegate.onUserLeaveHint(); } public void onTrimMemory(int level) { this.eventDelegate.onTrimMemory(level); } public void onLowMemory() { this.eventDelegate.onLowMemory(); } @Override public FlutterView createFlutterView(Context context) { // if(flutterView2!=null){ // return flutterView2; // } WindowManager.LayoutParams matchParent = new WindowManager.LayoutParams(-1, -1); FlutterNativeView nativeView = this.createFlutterNativeView(); FlutterView flutterView = new FlutterView(TYFlutterActivity.this, (AttributeSet) null, nativeView); flutterView.setInitialRoute("/"); flutterView.setLayoutParams(matchParent); this.setContentView(flutterView); return flutterView; } @Override public FlutterNativeView createFlutterNativeView() { return null; } @Override public boolean retainFlutterNativeView() { return false; } @Override public Registrar registrarFor(String pluginKey) { return this.pluginRegistry.registrarFor(pluginKey); } @Override public boolean hasPlugin(String key) { return this.pluginRegistry.hasPlugin(key); } @Override public <T> T valuePublishedByPlugin(String pluginKey) { return this.pluginRegistry.valuePublishedByPlugin(pluginKey); } @Override public FlutterView getFlutterView() { return this.viewProvider.getFlutterView(); } @NonNull @Override public Lifecycle getLifecycle() { return mLifecycleRegistry; } }
实践结果:任何功能正常,且在release环境下,页面切换与native相似,这样写扩展性更高