flutter学习笔记,持续更新
- 在真实的项目中,我们基本上不会去做一个纯flutter项目,都是在现有的Android工程中拿一个模块来用flutter实现
- flutter官方文档中提供的接入方式:Add Flutter to existing apps
- 用命令flutter create新建
$ flutter create -t module my_flutter
- 用Android Studio新建
Android Studio->File->New->New Flutter Project->Flutter Module
- 在host app's settings.gradle中添加
// 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
))
- 在build.gradle中引用
dependencies {
implementation project(':flutter')
}
$ cd .android/
$ ./gradlew flutter:assembleDebug
有些时候会遇到问题,这个时候可以尝试在flutter module的根目录下执行:
$ flutter clean
$ flutter run
host端app目录下新建目录libs。
app下build.gradle中添加:
repositories {
flatDir {
dirs 'libs'
}
}
添加dependencies:
implementation name: 'flutter-debug', ext: 'aar'
从flutter module中拷贝生成的aar到libs目录下。
rebuild,run
十有八九会crash并且报错:
A/flutter: [FATAL:flutter/fml/icu_util.cc(95)] Check failed: context->IsValid(). Must be able to initialize the ICU context. Tried: /data/user/0/com.example.androidwithflutter3/app_flutter/icudtl.dat
A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 21199 (oidwithflutter3)
原因就是文件icudtl.dat没有引进来(不得不说,flutter坑真多),解决方法:
- 新建assets目录并把icudtl.dat拷进来,注意路径是:assets/flutter_shared/icudtl.dat
- 可以在flutter.jar中找到这个文件
关于这个坑更多的描述,参见:#18025
完成之后就可以run起来了。
- 在Flutter module的.android目录中打包aar并上传仓库
- 引用这个远程aar
宿主端Android APP可以通过route的方式调用flutter module中的内容,例如,Android端可以新建一个Activity来承载flutter。
public class FlutterViewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View flutterView = Flutter.createView(
FlutterViewActivity.this,
getLifecycle(),
"route"
);
setContentView(flutterView);
}
}
也可以通过Fragment的形式来承载flutter
public class FlutterFragmentActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fm);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, Flutter.createFragment("route"))
.commit();
}
}
在flutter module中,需要实现路由
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'demo_app':
return MyApp();
case 'version':
return VersionPage();
default:
return ErrorPage('Unknown route: $route');
}
}
关于这个主题的官方文档地址:Developing packages & plugins
Android Studio IDE下的File/New/New Flutter Project选项下自带了四种模板:
- Flutter Application: Flutter应用
- Flutter Plugin:Flutter插件
- Flutter Package:纯Dart组件
- Flutter Module:Flutter模块
Flutter Plugin提供Android或者iOS的底层封装,在Flutter层提供组件功能,使Flutter层可以较方便的调用Android或者IOS层提供的接口。很多平台相关性或者对于Flutter实现起来比较复杂的部分,都可以封装成Plugin。
Flutter Plugin通信的原理图如下:
Dart层和平台层(Android&IOS)通过MethodChannel实现通信和调用。
在Android Studio IDE下的File/New/New Flutter Project选项下选择模板Flutter Plugin,就可以新建一个Flutter Plugin。
新建的Flutter Plugin有以下主要部分:
- android:android侧的插件代码
- ios:ios侧的插件代码
- lib:插件Dart代码
- pubspec.yaml:插件配置文件
- example:使用插件的flutter示例工程
Android侧java层的Plugin实现类,都会继承MethodCallHandler接口,在方法onMethodCall中实现对flutter层调用的分发,如:
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals(GET_PLATFORM_VERSION)) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else if (call.method.equals(GET_SDK_INT)) {
result.success("Android SDK: " + android.os.Build.VERSION.SDK_INT);
} else {
result.notImplemented();
}
}
而这个插件类被注册并暴露给flutter层,则是通过一个static方法:
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), PLUGIN_NAME);
channel.setMethodCallHandler(new FlutterPlugin());
}
这个插件是什么时候被注册的呢,在插件调用示例的example flutter工程的MainActivity中,我们可以看到在onCreate有这样一行代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
GeneratedPluginRegistrant类是自动生成的插件注册类:
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
FlutterPlugin.registerWith(registry.registrarFor("com.jeremyliao.flutterplugin.FlutterPlugin"));
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}
在此处完成了插件的注册。
lib下的代码就是flutter侧的实现,比如demo中的这个flutter_plugin.dart,本质上就是把平台侧的接口重新封装了一下,提供给其他dart使用:
class FlutterPlugin {
static const MethodChannel _channel = const MethodChannel('flutter_plugin');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
static Future<String> get sdkInt async {
final String version = await _channel.invokeMethod('getSdkInt');
return version;
}
}
插件开发完毕,可以发布插件让其他人使用,在发布之前,确保pubspec.yaml,、README.md以及CHANGELOG.md文件的内容都正确填写完毕。可以通过dry-run命令来看准备是否就绪。
flutter packages pub publish --dry-run
检查无误后,可以执行下面的命令,发布到Dart packages
flutter packages pub publish
在dependencies加上我们需要引入的库,例如引入url_launcher库:
dependencies:
url_launcher: ^0.4.2
dependencies:
flutter_plugin:
path: ../
dependencies:
flutter_plugin:
git:
url: git://github.com/flutter/packages.git
path: packages/package1
调用是异步的,关键代码如下:
Future<void> initPlatformState() async {
...
try {
platformVersion = await FlutterPlugin.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
...
}