[译]Flutter for Android Developers - Intents

在Flutter中Intent等价于什么

  • in Android

    • 在Android中Intent用来表示应用的一种意图,常见的使用场景比如切换不同的Activity,发送广播,调用手机内的其他模块功能等等。
  • in Flutter

    • 没有Intent的概念,但是如果需要的话Flutter也能通过调用Android对应的平台接口触发Intent。

在Flutter中要实现一个应用的多个界面并且在界面间切换跳转需要用到两个核心的概念:

1.Route: Route是应用中一个界面的抽象,类似Android中的Activity。

2.Navigator: Navigator是一个Widget,它用于管理应用中所有的Route。一个Navigator可以通过push或者pop操作来入栈和出栈一个Route,进而实现从一个界面进入另一个界面或者返回上一个界面的效果。

与在Android中的AndroidManifest.xml中声明Activity类似,在Flutter中通过构造MaterialApp实例时传入的routes参数来配置Route。如下:

void main() {
  runApp(new MaterialApp(
    home: new MyAppHome(), // becomes the route named '/'
    routes: <String, WidgetBuilder> {
      '/a': (BuildContext context) => new MyPage(title: 'page A'),
      '/b': (BuildContext context) => new MyPage(title: 'page B'),
      '/c': (BuildContext context) => new MyPage(title: 'page C'),
    },
  ));
}
复制代码

构造MaterialApp时与之前不同的是我们传入了一个Map作为其routes参数,该Map的key是String类型,value是一个用于构造对应Route的方法。 接下来就可以通过获取一个Navigator对象来改变当前屏幕内显示的Route:

Navigator.of(context).pushNamed('/b');
复制代码

pushNamed方法传入的参数与之前在MaterialApp构造时配置的routes是匹配的,这个例子中将key为"/b"的Route入栈,进而界面跳转到Page B。

另一个使用Intent主要的场景是去调用手机内的其他功能模块,比如启动相机或者文件管理器等。在这种情况下可以通过调用Android对应的平台接口来实现。后面的例子有简单用到Flutter中Android对应的平台接口。

小结: 在Flutter中处理界面间的跳转切换使用Navigator和Route配合完成。也可以通过Flutter提供的Android对应的平台接口去调用外部的其他模块。

怎样在Flutter中处理其他应用发送过来的Intent

在Flutter中处理收到的Intent分为两个步骤: 1.接收Intent 这一步还是利用Android的原理,在AndroidManifest.xml中配置我们的Activity所能够接收的Intent。 2.处理数据 这一步通过Flutter提供的Android平台接口在Flutter层获取Android层接收到的Intent中所携带的数据内容。 通过一个例子来解释一下。 首先在AndroidManifest.xml中配置我们的Activity可以接收的Intent种类:

<activity
       android:name=".MainActivity"
       android:launchMode="singleTop"
       android:theme="@style/LaunchTheme"
       android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
       android:hardwareAccelerated="true"
       android:windowSoftInputMode="adjustResize">
       <!-- This keeps the window background of the activity showing
            until Flutter renders its first frame. It can be removed if
            there is no splash screen (such as the default splash screen
            defined in @style/LaunchTheme). -->
       <meta-data
           android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
           android:value="true" />
       <intent-filter>
           <action android:name="android.intent.action.MAIN"/>
           <category android:name="android.intent.category.LAUNCHER"/>
       </intent-filter>
       <intent-filter>
           <action android:name="android.intent.action.SEND" />
           <category android:name="android.intent.category.DEFAULT" />
           <data android:mimeType="text/plain" />
       </intent-filter>
   </activity>
复制代码

这是一个AndroidManifest.xml配置文件片段,上面的配置说明我们的应用可以接收Action为android.intent.action.SEND的Intent。

接着,在MainActivity中我们实现接收Intent的逻辑,重点需要关注的是这里从Intent中获取的数据是如何传递给Flutter的:

package com.yourcompany.shared;

import android.content.Intent;
import android.os.Bundle;

import java.nio.ByteBuffer;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    String sharedText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        Intent intent = getIntent();
        String action = intent.getAction();
        String type = intent.getType();

        if (Intent.ACTION_SEND.equals(action) && type != null) {
            if ("text/plain".equals(type)) {
                handleSendText(intent); // Handle text being sent
            }
        }

        new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                if (methodCall.method.contentEquals("getSharedText")) {
                    result.success(sharedText);
                    sharedText = null;
                }
            }
        });
    }


    void handleSendText(Intent intent) {
        sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
    }
}
复制代码

MainActivity从FlutterActivity继承而来。这里主要是接收Action为android.intent.action.SEND的Intent,将该Intent中包含的数据提取出来保存在成员变量sharedText中。关键在于onCreate方法中构造了一个MethodChannel实例,构造时需要传递一个String参数作为标识,在后面Flutter层需要构造同样标识的MethodChannel。MethodChannel的作用主要就是为了实现Flutter层与Android层的通信。关于MethodChannel的更多帮助信息可以参阅官方文档

最后,在Flutter层我们可以调用系统提供的接口请求Android层中保存的数据sharedText:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(new SampleApp());
}

class SampleApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Sample Shared App Handler',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => new _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  static const platform = const MethodChannel('app.channel.shared.data');
  String dataShared = "No data";

  @override
  void initState() {
    super.initState();
    getSharedText();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(body: new Center(child: new Text(dataShared)));
  }

  getSharedText() async {
    var sharedData = await platform.invokeMethod("getSharedText");
    if (sharedData != null) {
      setState(() {
        dataShared = sharedData;
      });
    }
  }
}
复制代码

这段代码比较简单,展示一个我们自定义的StatefulWidget在屏幕上,我们主要关注与SampleAppPage对应的_SampleAppPageState类。首先这里使用与前面相同的标识构造了一个MethodChannel类,相同的标识才能保证这里的MethodChannel能与之前Android层中定义的MethodChannel通信。另外定义了一个getSharedText异步方法,该方法中调用MethodChannel实例的invokeMethod方法,最终会回调到之前MainActivity中定义MethodChannel时传入的MethodCallHandler类的onMethodCall方法,该方法的methodCall参数封装Flutter层传递到Android层的信息,result参数用于向Flutter层返回结果信息。

小结: 接收Intent的配置和处理还是在Android实现,不同的是要在Flutter中处理接收到的Intent所带的数据时,需要使用MethodChannel类来完成Flutter层与Android层之间的通信。

在Flutter中startActivityForResult等价于什么

在Flutter中可以使用Navigator类获取从当前Route返回到上一个Route时附带的数据信息。只需要对push返回的Future对象做一个await操作。关于Future,await,async不太清楚可以参阅官方文档,他们用来在Dart中实现异步同步功能。

比如我们需要启动一个位置信息界面让用户选择他们所处的位置,我们可以写下面的代码:

Map coordinates = await Navigator.of(context).pushNamed('/location');
复制代码

此处通过pushNamed方法将屏幕上的当前界面跳转到了一个位置信息界面(假设我们已经配置好了/location对应的Route)。同时pushNamed会返回一个Future对象,我们需要将该Future对象作为await的表达式。

然后在我们的显示位置信息的界面中当用户选择好位置后我们就通过pop当前的Route出栈来实现返回上一个界面的效果,并同时带上需要返回给上一个界面的数据信息:

Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
复制代码

pop的参数就是返回给上一个界面的数据信息,这里是一个Map类型的数据。表示位置信息的Route出栈后,上面的await表达式将被唤醒,并且接收到传递过来的Map数据。

小结: 在Flutter中使用Navigator向Route Stack中push一个Route时返回的是一个Future对象,通过await表达式可以实现等待界面返回的效果,并且Navigator从Route Stack中pop一个Route时可以带上参数,此时带的参数就会返回给唤醒的await表达式。进而实现类似startActivityForResult中的当前界面返回并传递参数给上一个界面的效果。

英文原版传送

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值