一、 Flutter 混合开发 Flutter 环境配置
1. Flutter 整体架构
2. Flutter 开发环境配置 Flutter环境
1 2 3 $ cd ~/development$ unzip ~/Downloads/flutter_macos_v1.9.1+hotfix.4-stable.zip $ export PATH="$PATH :`pwd`/flutter/bin"
Android SDK
1 $ export ANDROID_HOME=/Users/*/Library/Android/sdk
Xcode 安装
1 $ sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
3. Flutter 常用命令 1 2 3 4 5 6 # $ flutter doctor # $ flutter version v1.5.4-hotfix.2 # $ flutter create --org com.exemple --template=[plugin,app,package,module] projectname
4. Flutter接入及原理 Flutter开发方式,目前有4种,分别是:纯的Dart库开发、Flutter应用开发、Flutter插件开发以及Flutter Module接入。下面,分别对这4种开发形式做一个介绍。
4.1 纯的Dart库开发 纯的Dart库开发,无客户端相关Native代码,通过命令行生成工程模板:
1 $ flutter create --org com.exemple --template=package somepackage
4.2 Flutter应用开发 Flutter应用开发,可直接运行,适合开发新的APP。
1 $ flutter create --org com.exemple --template=app someapp
从目录结构可以看出,此工程中有3个目录:android、ios、lib,android目录下是与Android平台相关的代码及配置,ios目录下是与iOS平台相关的代码及配置,lib目录下为flutter平台相关的代码。
pubspec.yaml为flutter工程相关的配置,包含依赖的插件、资源等。.flutter-plugins 文件为flutter插件路径映射文件。
我们以Android开发者的角度,简单看一下,Flutter是如何被引入到Android项目中的。
在Android工程中 settings.gralde
文件中,我们可以看到,主要做了以下几件事情:
1 2 3 4 5 6 7 8 9 10 11 12 include ':app' def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()def plugins = new Properties()def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins' )if (pluginsFile.exists()) { pluginsFile.withReader('UTF-8' ) { reader -> plugins.load(reader) } } plugins.each { name, path -> def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android' ).toFile() include ":$name" project(":$name" ).projectDir = pluginDirectory }
寻找.flutter-plugins
文件并读取配置
将.flutter-plugins
配置中的库引入工程中
.flutter-plugins
中的内容:
1 2 flutter_boost=/Users/zauther/.pub-cache/hosted/pub.flutter-io.cn/flutter_boost-0.1.54/ xservice_kit=/Users/zauther/.pub-cache/hosted/pub.flutter-io.cn/xservice_kit-0.0.29/
在app对应的 build.gradle
文件中,前面的配置代码:
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 def localProperties = new Properties()def localPropertiesFile = rootProject.file('local.properties' )if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8' ) { reader -> localProperties.load(reader) } } def flutterRoot = localProperties.getProperty('flutter.sdk' )if (flutterRoot == null ) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file." ) } def flutterVersionCode = localProperties.getProperty('flutter.versionCode' )if (flutterVersionCode == null ) { flutterVersionCode = '1' } def flutterVersionName = localProperties.getProperty('flutter.versionName' )if (flutterVersionName == null ) { flutterVersionName = '1.0' } apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" ...... flutter { source '../..' } ......
可以看出主要做了一下几件事:
读取 local.properties
配置
从配置中读取flutter.sdk、flutter.versionCode、flutter.versionName
通过flutter.sdk路径找到 flutter.gradle
,此文件即为flutter编译流程文件,后面单独介绍
设置flutter对应的lib所在目录,lib中包含flutter代码入口 main.dart
4.3 Flutter 插件开发 Flutter插件类似于Java开发过程中的库,可在目前项目上直接引入已有功能。
1 $ flutter create --org com.exemple --template=plugin someplugin
目录结构同Flutter应用,其中example模块为一个独立的flutter 工程,可用于测试插件。通过这个例子,以Android开发者视角,来看一下插件是插件如何编写以及被引入的。
Flutter与Native的通讯分为2种:一是Flutter主动调用Native,并获取Native的返回结果;二是Native主动调用Flutter,并获取Flutter返回结果。
Flutter调用Native 在flutter插件的android目录下,新建类Testflutterplugin2Plugin,实现MethodCallHandler接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class Testflutterplugin2Plugin implements MethodCallHandler { public static void registerWith (Registrar registrar) { final MethodChannel channel = new MethodChannel (registrar.messenger(), "testflutterplugin2" ); channel.setMethodCallHandler(new Testflutterplugin2Plugin ()); } @Override public void onMethodCall (MethodCall call, Result result) { if (call.method.equals("getPlatformVersion" )) { result.success("Android " + android.os.Build.VERSION.RELEASE); } else if (call.method.equals("FUNC" )){ try { result.success("do something" ); }catch (Exception e){ result.error("ERROR" ,"do something error" ,null ); } }else { result.notImplemented(); } } }
方法registerWith中,通过Registrar创建MethodChannel,并将Testflutterplugin2Plugin加入到MethodChannel中。
在onMethodCall方法中,参数MethodCall包含有Flutter调用的方法名以及传入参数,Result对象用于返回Native的处理结果给Flutter。
在Flutter代码中:
1 2 3 4 5 6 7 8 9 class Testflutterplugin2 { static const MethodChannel _channel = const MethodChannel('testflutterplugin2' ); static Future<String > get platformVersion async { final String version = await _channel.invokeMethod('getPlatformVersion' ); return version; } }
Native调用Flutter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Testflutterplugin2EventChannel implements EventChannel .StreamHandler { public static void registerWith (PluginRegistry.Registrar registrar) { final EventChannel channel = new EventChannel (registrar.messenger(), "testflutterplugin2.eventchannel" ); channel.setStreamHandler(new Testflutterplugin2EventChannel ()); } @Override public void onListen (Object arguments, EventChannel.EventSink eventSink) { try { eventSink.success("success" ); } catch (Exception e) { eventSink.error("error" , "Testflutterplugin2EventChannel error" , null ); } } @Override public void onCancel (Object arguments) { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Testflutterplugin2EventChannel { static const EventChannel _eventChannel = const EventChannel('testflutterplugin2.eventchannel' ); static void test() { _eventChannel.receiveBroadcastStream('arguments' ).listen((result) {}, onError: (e) {}, onDone: () {}, cancelOnError: true ); } static void test1() { _eventChannel.receiveBroadcastStream().listen((result) {}, onError: (e) {}, onDone: () {}, cancelOnError: true ); } }
Android平台下的插件引入 Android目录中的代码为Android与Flutter桥接的部分:
在settings.gradle
中 rootProject.name = 'testflutterplugin2'
只是声明了Android模块,build.gralde
中为Android相关的配置项。
插件代码 插件实现了MethodCallHandler
接口,实现了onMethodCall
方法。
registerWith
方法中:通过传入的Registrar对象,新建MethodChannel,并指定其名testflutterplugin2
。
设置MethodChannel的处理方法Testflutterplugin2Plugin。这段代码主要作用用于注册处理方法Testflutterplugin2Plugin到Registrar。而MethodChannel只是对这个注册过程的一个封装。
查看MethodChannel核心代码:
1 2 3 public void setMethodCallHandler (@Nullable MethodChannel.MethodCallHandler handler) { this .messenger.setMessageHandler(this .name, handler == null ? null : new MethodChannel .IncomingMethodCallHandler(handler)); }
在实现MethodCallHandler接口的类中,对于onMethodCall方法:
MethodCall对象中包含了来自于Flutter的调用方法名和参数信息。其中,arguments为Map对象或JSONObject
1 2 public final String method;public final Object arguments;
Result接口中包含Native端的返回给Flutter测的结果信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void onMessage (ByteBuffer message, final BinaryReply reply) { MethodCall call = MethodChannel.this .codec.decodeMethodCall(message); try { this .handler.onMethodCall(call, new MethodChannel .Result() { public void success (Object result) { reply.reply(MethodChannel.this .codec.encodeSuccessEnvelope(result)); } public void error (String errorCode, String errorMessage, Object errorDetails) { reply.reply(MethodChannel.this .codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails)); } public void notImplemented () { reply.reply((ByteBuffer)null ); } }); } catch (RuntimeException var5) { Log.e("MethodChannel#" + MethodChannel.this .name, "Failed to handle method call" , var5); reply.reply(MethodChannel.this .codec.encodeErrorEnvelope("error" , var5.getMessage(), (Object)null )); } }
在IncomingResultHandler中,Result的匿名内部类中,通过BinaryReply将Native端的处理结果经StandardMethodCodec类,编码后传递给Flutter侧。
插件的引入 在example模块中,在MainActivity.java
的onCreate
中:
1 2 3 4 5 6 7 public class MainActivity extends FlutterActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public final class GeneratedPluginRegistrant { public static void registerWith (PluginRegistry registry) { if (alreadyRegisteredWith(registry)) { return ; } Testflutterplugin2Plugin.registerWith(registry.registrarFor("cn.cwiki.flutter.test.testflutterplugin2.Testflutterplugin2Plugin" )); } private static boolean alreadyRegisteredWith (PluginRegistry registry) { final String key = GeneratedPluginRegistrant.class.getCanonicalName(); if (registry.hasPlugin(key)) { return true ; } registry.registrarFor(key); return false ; } }
1 2 3 4 <application android:name ="io.flutter.app.FlutterApplication" android:label ="testflutterplugin2_example" android:icon ="@mipmap/ic_launcher" >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class FlutterApplication extends Application { private Activity mCurrentActivity = null ; public FlutterApplication () { } @CallSuper public void onCreate () { super .onCreate(); FlutterMain.startInitialization(this ); } public Activity getCurrentActivity () { return this .mCurrentActivity; } public void setCurrentActivity (Activity mCurrentActivity) { this .mCurrentActivity = mCurrentActivity; } }
FlutterApplication中核心作用是 FlutterMain.startInitialization(this);
初始化Flutter框架
新建Flutter Module 1 $ flutter create --org com.exemple --template=plugin somemodule
settings.gradle
1 2 3 4 5 include ':app' rootProject.name = 'android_generated' setBinding(new Binding([gradle: this ])) evaluate(new File('include_flutter.groovy' ))
include_flutter.groovy
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 def scriptFile = getClass().protectionDomain.codeSource.location.toURI()def flutterProjectRoot = new File(scriptFile).parentFile.parentFilegradle.include ':flutter' gradle.project(':flutter' ).projectDir = new File(flutterProjectRoot, '.android/Flutter' ) def plugins = new Properties()def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins' )if (pluginsFile.exists()) { pluginsFile.withReader('UTF-8' ) { reader -> plugins.load(reader) } } plugins.each { name, path -> def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android' ).toFile() gradle.include ":$name" gradle.project(":$name" ).projectDir = pluginDirectory } gradle.getGradle().projectsLoaded { g -> g.rootProject.beforeEvaluate { p -> _mainModuleName = binding.variables['mainModuleName' ] if (_mainModuleName != null && !_mainModuleName.empty) { p.ext.mainModuleName = _mainModuleName } } g.rootProject.afterEvaluate { p -> p.subprojects { sp -> if (sp.name != 'flutter' ) { sp.evaluationDependsOn(':flutter' ) } } } }
Android Flutter 编译任务 Android 端Flutter编译过程
设置变异类型
1 2 3 4 5 6 7 8 9 10 11 project.android.buildTypes { profile { } dynamicProfile { } dynamicRelease { } }
设置Flutter Engine,如果本地有配置,则会使用本地的
1 2 3 4 5 if (project.hasProperty('localEngineOut' )) { } else { }
添加FlutterTask,并设置Flutter插件的依赖
1 2 3 4 5 6 project.extensions.create("flutter" , FlutterExtension) project.afterEvaluate this .&addFlutterTask plugins.each { name, _ -> }
FlutterTask
设置编译参数
flutter 代码路径:
1 2 3 4 5 6 7 String target = project.flutter.target if (target == null ) { target = 'lib/main.dart' } if (project.hasProperty('target' )) { target = project.property('target' ) }
编译目标平台:
1 2 3 4 String targetPlatformValue = null if (project.hasProperty('target-platform' )) { targetPlatformValue = project.property('target-platform' ) }
等等。
添加Flutter依赖以及FlutterTask
1 2 3 4 5 if (project.android.hasProperty("applicationVariants" )) { project.android.applicationVariants.all addFlutterDeps } else { project.android.libraryVariants.all addFlutterDeps }
包含:vm_snapshot_data、vm_snapshot_instr、isolate_snapshot_data、isolate_snapshot_instr等,以及Flutter源代码等。Flutter 的编译过程是由源代码编译的,插件等提供的也是源代码依赖。
FlutterTask
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 void buildBundle() { if (!sourceDir.isDirectory()) { throw new GradleException("Invalid Flutter source directory: ${sourceDir}" ) } intermediateDir.mkdirs() if (buildMode == "profile" || buildMode == "release" ) { project.exec { executable flutterExecutable.absolutePath workingDir sourceDir if (localEngine != null ) { args "--local-engine" , localEngine args "--local-engine-src-path" , localEngineSrcPath } args "build" , "aot" args "--suppress-analytics" args "--quiet" args "--target" , targetPath args "--target-platform" , "android-arm" args "--output-dir" , "${intermediateDir}" if (trackWidgetCreation) { args "--track-widget-creation" } if (extraFrontEndOptions != null ) { args "--extra-front-end-options" , "${extraFrontEndOptions}" } if (extraGenSnapshotOptions != null ) { args "--extra-gen-snapshot-options" , "${extraGenSnapshotOptions}" } if (buildSharedLibrary) { args "--build-shared-library" } if (targetPlatform != null ) { args "--target-platform" , "${targetPlatform}" } args "--${buildMode}" } } project.exec { executable flutterExecutable.absolutePath workingDir sourceDir if (localEngine != null ) { args "--local-engine" , localEngine args "--local-engine-src-path" , localEngineSrcPath } args "build" , "bundle" args "--suppress-analytics" args "--target" , targetPath if (verbose) { args "--verbose" } if (fileSystemRoots != null ) { for (root in fileSystemRoots) { args "--filesystem-root" , root } } if (fileSystemScheme != null ) { args "--filesystem-scheme" , fileSystemScheme } if (trackWidgetCreation) { args "--track-widget-creation" } if (compilationTraceFilePath != null ) { args "--compilation-trace-file" , compilationTraceFilePath } if (createPatch) { args "--patch" args "--build-number" , project.android.defaultConfig.versionCode if (buildNumber != null ) { assert buildNumber == project.android.defaultConfig.versionCode } } if (baselineDir != null ) { args "--baseline-dir" , baselineDir } if (extraFrontEndOptions != null ) { args "--extra-front-end-options" , "${extraFrontEndOptions}" } if (extraGenSnapshotOptions != null ) { args "--extra-gen-snapshot-options" , "${extraGenSnapshotOptions}" } if (targetPlatform != null ) { args "--target-platform" , "${targetPlatform}" } if (buildMode == "release" || buildMode == "profile" ) { args "--precompiled" } else { args "--depfile" , "${intermediateDir}/snapshot_blob.bin.d" } args "--asset-dir" , "${intermediateDir}/flutter_assets" if (buildMode == "debug" ) { args "--debug" } if (buildMode == "profile" || buildMode == "dynamicProfile" ) { args "--profile" } if (buildMode == "release" || buildMode == "dynamicRelease" ) { args "--release" } if (buildMode == "dynamicProfile" || buildMode == "dynamicRelease" ) { args "--dynamic" } } }
flutterExecutable为flutter命令
当 buildType 为 debug 时:
1 $ flutter build bundle --depfile ${intermediateDir} /snapshot_blob.bin.d --debug
当 buildType 为 release 时:
1 2 $ flutter build aot $ flutter build bundle --precompiled --release
Flutter 编译模型区别 https://juejin.im/post/5d68fb1af265da03d063b69e
启动devtools
1 2 $ pub global run devtools Serving DevTools at http://127.0.0.1:9100
在flutter lib同目录执行attach命令,等待App连接
1 2 $ flutter attach Waiting for a connection from Flutter on MI 8...
重新启动app,等待
1 2 3 4 5 6 7 8 9 $ flutter attach Waiting for a connection from Flutter on MI 8... Done. Syncing files to device MI 8... 3,832ms (!) 🔥 To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R". An Observatory debugger and profiler on MI 8 is available at: http://127.0.0.1:53469/Fv75Oo4wMEE=/ For a more detailed help message, press "h". To detach, press "d"; to quit, press "q"
打开chrome,http://127.0.0.1:9100 ,输入flutter attach
命令中的url http://127.0.0.1:53469/Fv75Oo4wMEE=/
通过Dart DevTools调试
工具项:
Flutter Inspector: 布局调试工具
Timeline :
Memory : 内存使用情况
Performance :
Debugger: 断点调试工具
Logging : Log输出
Hot Reload
Hot Restart
Flutter Inspector:Flutter 布局调试工具 Performance Overlay :
Paint Baselines:
Debug Paint:
当项目为Flutter工程时,可以直接通过Android Studio进行调试,当以Module的形式引入时,目前无法直接通过Android Studio进行调试,可采用Dart DevTools在Chrome中调试。
![image-20191024201010180](/Users/zauther/Library/Application Support/typora-user-images/image-20191024201010180.png)
三、Flutter 接入实践 接入现有ReactNative工程 1. 在Android测
引入Flutter Module
1 2 setBinding(new Binding([gradle: this ])) evaluate(new File(rootProject.projectDir, './fluttermodule/android/include_flutter.groovy' ))
在include_flutter.groovy指定自己的Flutter模块,因为.android目录是自己生成的,内容会被覆盖,可自定义模块
1 2 gradle.include ':flutter' gradle.project(':flutter' ).projectDir = new File(flutterProjectRoot, 'android/Flutter' )
定义FlutterActivity,并在AndroidManifest.xml中注册,此处集成FlutterBoost的BoostFlutterActivity,也可以使用Flutter原生的FlutterActivity。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class FlutterPageActivity extends BoostFlutterActivity { private String url; private HashMap params; @Override public void onCreate (Bundle saveInstanceBundle) { } @Override public void onRegisterPlugins (PluginRegistry registry) { } @Override public String getContainerName () { return url = = null ? "" : url; } @Override public Map getContainerParams () { return params = = null ? new HashMap () : params; } }
1 2 3 4 5 6 7 public class MainActivity extends FlutterActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this ); } }
FlutterActivity,用于承接Flutter界面
在Application中初始化Flutter框架
1 FlutterMain.startInitialization(context);
如果使用FlutterBoost,在初始化Flutter框架后,初始化FlutterBoost插件:
1 FlutterBoostPlugin.init(platform);
2. 在ReactNative测
自定义方法
1 2 3 FlutterSupport .openFlutterPage = function (url, params ){ NativeModules .RNFlutterSupport .openFlutterPage (url, params) }
新建ReactNativeModules,并注册到ReactNativeHost中
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 public class RNFlutterSupportModule extends ReactContextBaseJavaModule { @ReactMethod public void openFlutterPage (String url, ReadableMap params) { RNToFlutterManager.openFlutterPage(reactContext, url, params == null ? new HashMap () : params.toHashMap(), 0 ); } @ReactMethod public void invokeCallback (String callbackId, ReadableMap data) { ReactFlutterEventEmitterCallbackEvent callbackEvent = new ReactFlutterEventEmitterCallbackEvent (); callbackEvent.callBackId = callbackId; callbackEvent.data = data; mEmitter.onReactCallbackEvent(callbackEvent); } public void methodCallReactNative (String method, Object arguments, final Callback callback) { mEmitter.sendEvent(reactContext, method, arguments, callback == null ? null : new ReactFlutterEventEmitterCallbackHandler () { @Override public void handler (ReadableMap data) { callback.invoke(data.toHashMap()); } }); } @Override public String getName () { return "RNFlutterSupport" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class RNFlutterSupportPackage implements ReactPackage { public RNFlutterSupportModule mRNFlutterSupportModule; @Override public List<NativeModule> createNativeModules (ReactApplicationContext reactContext) { mRNFlutterSupportModule = new RNFlutterSupportModule (reactContext); return Arrays.<NativeModule>asList(mRNFlutterSupportModule); } public List<Class<? extends JavaScriptModule >> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers (ReactApplicationContext reactContext) { return Collections.emptyList(); } }
其中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void openFlutterPage (Context context, String url, final HashMap params, int requestCode) { Intent intent = new Intent (context, FlutterPageActivity.class); intent.putExtra("url" , url); intent.putExtra("params" , params); if (params != null && params.containsKey("flags" )) { String flags = (String) params.get("flags" ); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && "CLEAR_TASK" .equals(flags)) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); } else { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } } else { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } context.startActivity(intent); }
FlutterBoost 使用 在pubspec.yaml添加插件:
在Application中:
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 FlutterBoostPlugin.init(new IFlutterPlatform () { @Override public Application getApplication () { return null ; } @Override public Activity getMainActivity () { return null ; } @Override public boolean isDebug () { return false ; } @Override public boolean startActivity (Context context, String url, int requestCode) { return false ; } @Override public Map getSettings () { return null ; } });
在Flutter main.dart中注册页面并初始化FlutterBoost:
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 class _MyAPPState extends State <MyApp > { @override void initState() { FlutterBoost.singleton.registerPageBuilders({ 'teemo.home' : (pageName, params, _) => HomePageWidget(params), 'teemo.settings' : (pageName, params, _) => SettingPageWidget(params), 'teemo.changeenv' : (pageName, params, _) => ChangeEnvPageWidget(params), 'teemo.cabinet.errorreport' : (pageName, params, _) => ErrorReportPage(params), 'teemo.order.orderdetail' : (pageName, params, _) => OrderDetailPage(params), }); FlutterBoost.handleOnStartPage(); super .initState(); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Teemo' , builder: FlutterBoost.init(), home: new Container(), localizationsDelegates: [ RefreshLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, const FallbackCupertinoLocalisationsDelegate(), ], supportedLocales: [ const Locale('zh' , 'CH' ), const Locale('en' , 'US' ), ], ); } }
Flutter 中跳转页面:
1 2 3 FlutterBoost.singleton.openPage('teemo.settings' , {'canChangeRole' : canChangeRole ? "1" : "0" }, animated: true );
FlutterBoost flutter测常用方法:
1 2 3 4 5 6 FlutterBoost.singleton.openPage(String url, Map params, {bool animated, PageResultHandler resultHandler}); FlutterBoost.singleton.closePage(String url, String pageId, Map params, {bool animated}); FlutterBoost.singleton.closeCurPage(Map params); FlutterBoost.handleOnStartPage();