Flutter怎么写Android专属代码?

文章导读
Previous Quiz Next Flutter 提供了一个通用框架来访问平台特定功能。这使得开发者能够使用平台特定代码扩展 Flutter 框架的功能。像相机、电池电量、浏览器等平台特定功能,可以通过该框架轻松访问。
A A

Flutter - 编写 Android 特定代码



Previous
Quiz
Next

Flutter 提供了一个通用框架来访问平台特定功能。这使得开发者能够使用平台特定代码扩展 Flutter 框架的功能。像相机、电池电量、浏览器等平台特定功能,可以通过该框架轻松访问。

访问平台特定代码的通用思路是通过简单的消息协议。Flutter 代码、Client 和平台代码与 Host 绑定到一个共同的 Message Channel。Client 通过 Message Channel 向 Host 发送消息。Host 监听 Message Channel,接收消息并执行必要的功能,最后通过 Message Channel 将结果返回给 Client。

平台特定代码架构如下块图所示 −

Specific Code Architecture

消息协议使用标准消息编解码器(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 按钮,你会看到浏览器已启动。浏览器应用的主页截图如下所示 −

Flutter Demo Home Page

Productively Build Apps