Flutter - 编写 Android 特定代码
Flutter 提供了一个通用框架来访问平台特定功能。这使得开发者能够使用平台特定代码扩展 Flutter 框架的功能。像相机、电池电量、浏览器等平台特定功能,可以通过该框架轻松访问。
访问平台特定代码的通用思路是通过简单的消息协议。Flutter 代码、Client 和平台代码与 Host 绑定到一个共同的 Message Channel。Client 通过 Message Channel 向 Host 发送消息。Host 监听 Message Channel,接收消息并执行必要的功能,最后通过 Message Channel 将结果返回给 Client。
平台特定代码架构如下块图所示 −
消息协议使用标准消息编解码器(StandardMessageCodec class),它支持 JSON 类似值的二进制序列化,例如数字、字符串、布尔值等。在 client 和 host 之间,序列化和反序列化工作是透明的。
让我们编写一个简单的应用,使用 Android SDK 打开浏览器,并了解如何操作
在 Android Studio 中创建一个新的 Flutter 应用,flutter_browser_app
将 main.dart 代码替换为以下代码 −
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Center(
child: RaisedButton(
child: Text('Open Browser'),
onPressed: null,
),
),
);
}
}
这里,我们创建了一个新按钮用于打开浏览器,并将其 onPressed 方法设置为 null。
现在,导入以下包 −
import 'dart:async'; import 'package:flutter/services.dart';
这里,services.dart 包含调用平台特定代码的功能。
在 MyHomePage widget 中创建一个新的 message channel。
static const platform = const
MethodChannel('flutterapp.example.com/browser');
编写一个方法 _openBrowser,通过 message channel 调用平台特定方法 openBrowser。
Future<void> _openBrowser() async {
try {
final int result = await platform.invokeMethod(
'openBrowser', <String, String>{
'url': "https://flutter.dev"
}
);
}
on PlatformException catch (e) {
// 无法打开浏览器
print(e);
}
}
这里,我们使用了 platform.invokeMethod 来调用 openBrowser(在后续步骤中解释)。openBrowser 有一个参数 url,用于打开特定 URL。
将 RaisedButton 的 onPressed 属性值从 null 更改为 _openBrowser。
onPressed: _openBrowser,
打开 MainActivity.java(位于 android 文件夹内)并导入所需的库 −
import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugins.GeneratedPluginRegistrant;
编写一个方法 openBrowser 来打开浏览器
private void openBrowser(MethodCall call, Result result, String url) {
Activity activity = this;
if (activity == null) {
result.error("ACTIVITY_NOT_AVAILABLE",
"Browser cannot be opened without foreground
activity", null);
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
result.success((Object) true);
}
现在,在 MainActivity 类中设置 channel 名称 −
private static final String CHANNEL = "flutterapp.example.com/browser";
在 onCreate 方法中编写 Android 特定代码来设置消息处理 −
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
String url = call.argument("url");
if (call.method.equals("openBrowser")) {
openBrowser(call, result, url);
} else {
result.notImplemented();
}
}
});
这里,我们使用 MethodChannel 类创建了一个 message channel,并使用 MethodCallHandler 类来处理消息。onMethodCall 是实际负责通过检查消息调用正确平台特定代码的方法。onMethodCall 方法从消息中提取 url,只有当方法调用为 openBrowser 时才调用 openBrowser。否则,返回 notImplemented 方法。
应用的完整源代码如下 −
main.dart
MainActivity.java
package com..flutterapp.flutter_browser_app;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "flutterapp.example.com/browser";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
String url = call.argument("url");
if (call.method.equals("openBrowser")) {
openBrowser(call, result, url);
} else {
result.notImplemented();
}
}
}
);
}
private void openBrowser(MethodCall call, Result result, String url) {
Activity activity = this; if (activity == null) {
result.error(
"ACTIVITY_NOT_AVAILABLE", "Browser cannot be opened without foreground activity", null
);
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
result.success((Object) true);
}
}
main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(
title: 'Flutter Demo Home Page'
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
static const platform = const MethodChannel('flutterapp.example.com/browser');
Future<void> _openBrowser() async {
try {
final int result = await platform.invokeMethod('openBrowser', <String, String>{
'url': "https://flutter.dev"
});
}
on PlatformException catch (e) {
// 无法打开浏览器 print(e);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Center(
child: RaisedButton(
child: Text('Open Browser'),
onPressed: _openBrowser,
),
),
);
}
}
运行应用并点击 Open Browser 按钮,你会看到浏览器已启动。浏览器应用的主页截图如下所示 −
