in Androidhtml
in Flutterjava
在Flutter中要實現一個應用的多個界面而且在界面間切換跳轉須要用到兩個核心的概念:android
1.Route: Route是應用中一個界面的抽象,相似Android中的Activity。app
2.Navigator: Navigator是一個Widget,它用於管理應用中全部的Route。一個Navigator能夠經過push或者pop操做來入棧和出棧一個Route,進而實現從一個界面進入另外一個界面或者返回上一個界面的效果。less
與在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:async
Navigator.of(context).pushNamed('/b');
複製代碼
pushNamed方法傳入的參數與以前在MaterialApp構造時配置的routes是匹配的,這個例子中將key爲"/b"的Route入棧,進而界面跳轉到Page B。ide
另外一個使用Intent主要的場景是去調用手機內的其餘功能模塊,好比啓動相機或者文件管理器等。在這種狀況下能夠經過調用Android對應的平臺接口來實現。後面的例子有簡單用到Flutter中Android對應的平臺接口。ui
小結: 在Flutter中處理界面間的跳轉切換使用Navigator和Route配合完成。也能夠經過Flutter提供的Android對應的平臺接口去調用外部的其餘模塊。this
在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中可使用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中的當前界面返回並傳遞參數給上一個界面的效果。