兩分鐘帶你快速掌握Flutter的路由與導航

CrazyCodeBoy發表於2019-04-01

為大家傾力打造的課程《Flutter從入門到進階-實戰攜程網App》上線了,點我Get!!!

在這篇文章中,將帶著大家一起認識什麼是Flutter的路由與導航如何完成不同頁面跳轉?如何獲取路由跳轉的返回記過?,以及如何跳轉到其他APP?

首頁我們來學習在Flutter中如何實現不同頁面跳轉(導航)?

在Flutter中如何實現不同頁面跳轉(導航)?

Android:

要在Flutter中切換螢幕,我們可以訪問路由以繪製新的Widget。 管理多個螢幕有兩個核心概念和類:Route 和 Navigator。Route是應用程式的“螢幕”或“頁面”的抽象(可以認為是Activity), Navigator是管理Route的Widget。Navigator可以通過push和poproute以實現頁面切換。

和Android相似,我們可以在AndroidManifest.xml中宣告Activities,在Flutter中,我們可以將具有指定Route的Map傳遞到頂層MaterialApp例項,但這不是必須的。

iOS:

在 iOS 中,可以使用管理了 view controller 棧的 UINavigationController 來在不同的 view controller 之間跳轉。

React Native:

在React Native中,可以使用react-navigation來實現頁面之間的導航。

Flutter 也有類似的實現,使用了 Navigator和 Routes。一個路由是 App 中“螢幕”或“頁面”的抽象,而一個 Navigator是管理多個路由的 widget 。你可以粗略地把一個路由對應到一個 UIViewController。Navigator的工作原理和 iOS 中 UINavigationController 非常相似,當你想跳轉到新頁面或者從新頁面返回時,它可以 push() 和 pop() 路由。

在Flutter中,有兩個主要的widget用於在頁面之間導航:

  • Route是一個應用程式抽象的螢幕或頁面;
  • Navigator 是一個管理路由的widget;

以上兩種widget對應Flutter中實現頁面導航的有兩種選擇:

  • 具體指定一個由路由名構成的 Map。(MaterialApp)
  • 直接跳轉到一個路由。(WidgetApp)

下面是構建一個 Map 的例子:

void main() {
  runApp(MaterialApp(
    home: MyAppHome(), // becomes the route named '/'
    routes: <String, WidgetBuilder> {
      '/a': (BuildContext context) => MyPage(title: 'page A'),
      '/b': (BuildContext context) => MyPage(title: 'page B'),
      '/c': (BuildContext context) => MyPage(title: 'page C'),
    },
  ));
}
複製程式碼

以上程式碼片段的完整部分可以在課程原始碼中查詢。

通過把路由的名字 push 給一個 Navigator 來跳轉:

Navigator.of(context).pushNamed('/b');
複製程式碼

您還可以使用Navigatorpush方法,該方法將給定route新增到導航器的歷史記錄中。 在以下示例中,MaterialPageRoute widget是一種模版路由,它根據平臺自適應替換整個頁面。 在以下示例中,widget是一種模版路由,它使用平臺自適應替換整個頁面。它需要一個WidgetBuilder作為必需引數。

Navigator.push(context, MaterialPageRoute(builder: (BuildContext context)
 => UsualNavscreen()));
複製程式碼

以上程式碼片段的完整部分可以在課程原始碼中查詢。

如何獲取路由跳轉返回的結果?

在Android中有startActivityForResult來獲取跳轉頁面後返回的結果,那麼在FlutterNavigator類不僅用來處理 Flutter中的路由,還被用來獲取你剛 push 到棧中的路由返回的結果。通過 await等待路由返回的結果來達到這點。

舉個例子,要跳轉到“位置”路由來讓使用者選擇一個地點,你可能要這麼做:

Map coordinates = await Navigator.of(context).pushNamed('/location');
複製程式碼

之後,在 location 路由中,一旦使用者選擇了地點,攜帶結果一起 pop() 出棧:

Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
複製程式碼

以上程式碼片段的完整部分可以在課程原始碼中查詢。

如何在Flutter中處理來自外部應用程式傳入的Intents?(Android)

Flutter可以通過直接與Android層通訊並請求共享的資料來處理來自Android的Intents

在這個例子中,我們註冊文字共享Intent,所以其他應用程式可以共享文字到我們的Flutter應用程式

這個應用程式的基本流程是我們首先處理Android端的共享文字資料,然後等待Flutter請求資料,然後通過MethodChannel傳送。

如果你對MethodChannel還不熟悉的話可以通過第8章 Flutter進階提升:Flutter混合開發教程進行詳細的學習

首先在在AndroidManifest.xml中註冊我們想要處理的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">
  <!-- ... -->
  <intent-filter>
    <action android:name="android.intent.action.SEND" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
  </intent-filter>
</activity>
複製程式碼

以上程式碼片段的完整部分可以在課程原始碼中查詢。

然後,在MainActivity中,您可以處理intent,一旦我們從intent中獲得共享文字資料,我們就會持有它,直到Flutter在完成準備就緒時請求它。

...

public class MainActivity extends FlutterActivity {

  private 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 MethodCallHandler() {
        @Override
        public void onMethodCall(MethodCall call, MethodChannel.Result result) {
          if (call.method.contentEquals("getSharedText")) {
            result.success(sharedText);
            sharedText = null;
          }
        }
      });
  }

  void handleSendText(Intent intent) {
    sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
  }
}
複製程式碼

以上程式碼片段的完整部分可以在課程原始碼中查詢。

最後,在Flutter中,您可以在渲染Flutter檢視時請求資料。

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

  @override
  _SampleAppPageState createState() => _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 Scaffold(body: Center(child: Text(dataShared)));
  }

  getSharedText() async {
    var sharedData = await platform.invokeMethod("getSharedText");
    if (sharedData != null) {
      setState(() {
        dataShared = sharedData;
      });
    }
  }
}
複製程式碼

以上程式碼片段的完整部分可以在課程原始碼中查詢。

怎麼跳轉到其他 App?

在 iOS 中,要跳轉到其他 App,你需要一個特定的 URL Scheme。對系統級別的 App 來說,這個 scheme 取決於 App。為了在 Flutter 中實現這個功能,你可以建立一個原生平臺的整合層,或者使用現有的 plugin,例如 url_launcher

大家可以通過《路由、Navigator與頁面導航開發指南》來學習Flutter頁面導航與路由的更多技巧和實戰經驗。

未完待續

  • Flutter入門基礎知識
  • Flutter主題和文書處理
  • Flutter什麼是宣告式UI
  • Flutter佈局與列表
  • Flutter手勢檢測及觸控事件處理
  • Flutter狀態管理d
  • Flutter執行緒和非同步UI
  • Flutter表單輸入與富文字
  • Flutter認識檢視(Views)md
  • Flutter呼叫硬體、第三方服務以及平臺互動、通知
  • Flutter路由與導航
  • Flutter專案結構、資源、依賴和本地化

參考

相關文章