Flutter 之Router 頁面跳轉


Flutter 之Router 頁面跳轉

頁面跳轉在移動開發中是很常見的事情,在Android中打開另外一個頁面主要是用startActivity這個方法,在Flutter中也是提供這種能力,主要的使用方式就是通過Navigator 去打開一個頁面

1.跳轉到另外一個頁面

構建FirstScreen和SecondScreen 頁面

import 'package:flutter/material.dart';

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) {
              return SecondScreen();
            }));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
            onPressed: () {
               Navigator.pop(context);
            },
          child: Text("back"),
        ),
      ),
    );
  }
}

 

這里就是跳轉的主要代碼

Navigator.push(context, MaterialPageRoute(builder: (context) {
  return SecondScreen();
}));

push方式詳解

static Future<T> push<T extends Object>(BuildContext context, Route<T> route)

 

第一個參數就是上下問信息,類似Android中的Context,第二個參數就是路由信息,也就是要打開的主要頁面是哪個,MaterialPageRoute 就是Route其中的一個子類,用於在Material Desgin 模式下打開頁面的

Navigator.pop(context);

是用來返回上一個頁面的

 
 

 

2.通過routes路徑方式跳轉到下一個頁面

先定義Routes路由表,實際上就是一個Map結構,key是路徑,value就是對應的頁面

import 'package:flutter/material.dart';

import 'navigation/navigation_demo.dart';

void main() {
  runApp(MaterialApp(
    initialRoute: "/",
    routes: {
      "/": (context) => FirstScreen(),
      '/second': (context) => SecondScreen(),
    },
  ));
}

  

routes 就是一個map結構,根目錄/對應的頁面就是FirstScreen,/second路徑對應的頁面就是ScendScreen,在FirstScreen中打開SecondScreen的方式我們換一下,要通過Navigator.pushNamed方式打開一個在路由表中已經存在的頁面

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pushNamed(context, "/second");
//            Navigator.push(context, MaterialPageRoute(builder: (context) {
//              return SecondScreen();
//            }));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

  

3.傳遞數據到下一個頁面

傳遞數據到下一個頁面也是比較常見的情況,例如說在一個相冊應用中,有一個列表頁面,單擊列表中某一個item,應該跳轉到照片的詳情頁面,其實這種情況就應該把照片的信息傳遞給另外一個頁面

傳遞的方式有兩種:

  • 在構造方法中傳遞數據
  • 在Route中傳遞數據給下一個頁面

在第一個頁面構造要傳遞的數據

class Photo {
  String title;
  String message;

  Photo({this.title, this.message});
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title",message: "pass message"));
//            Navigator.push(context, MaterialPageRoute(builder: (context) {
//              return SecondScreen();
//            }));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

  

在第二個頁面獲取數據

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final Photo photo=ModalRoute.of(context).settings.arguments;
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen ${photo.title}"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text("back ${photo.message}"),
        ),
      ),
    );
  }
}

  

這種方式有種不太好的地方就是需要在下一個頁面通過ModalRoute.of(context).settings.arguments; 方式獲取傳遞的數據,其實Flutter中已經提供了這種方式簡便處理方式

import 'package:flutter/material.dart';

import 'navigation/navigation_demo.dart';

void main() {
  runApp(MaterialApp(
    home: FirstScreen(),
    onGenerateRoute: (settings) {
      if (settings.name == ThreeScreen.routeName) {
        final Photo args = settings.arguments;
        return MaterialPageRoute(builder: (context) {
          return ThreeScreen(
            title: args.title,
            message: args.message,
          );
        });
      }
    },
  ));
}

 

onGenerateRoute 是用來統一攔截傳遞參數的方法,我們可以在這個地方獲取傳遞的數據,然后在構造頁面的時候把參數傳遞給目標頁面,這樣在目標頁面也就是不用考慮如何解析傳遞過來的數據了

class ThreeScreen extends StatelessWidget {
  static const routeName = '/extractArguments';

  final String title;
  final String message;

  ThreeScreen({this.title, this.message});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("second"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(title),
            Text(message),
          ],
        ),
      ),
    );
  }
}

 

在這個頁面,數據都是通過構造方法中傳遞了,減少了在頁面獲取傳遞數據的代碼

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pushNamed(context, ThreeScreen.routeName,
                arguments: Photo(title: "args title", message: "args message"));
          },
          child: Text("next screen"),
        ),
      ),
    );
  }
}

 

發送方式的代碼沒有改變

 

4.接收頁面返回值

有的時候我們希望在前一個頁面接收另外一個頁面的數據,這個怎么處理呢

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("First Screen"),
      ),
      body: FirstButton(),
    );
  }
}

class FirstButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        onPressed: () {
          Navigator.pushNamed(context, "/second",
                  arguments:
                      Photo(title: "pass title", message: "pass message"))
              .then((vale) {
            final snackBar = SnackBar(
              content: Text('Yay! A SnackBar!'),
              action: SnackBarAction(
                label: 'Undo',
                onPressed: () {
                },
              ),
            );
            Scaffold.of(context).showSnackBar(snackBar);

          });
        },
        child: Text("next screen"),
      ),
    );
  }
}

 

關鍵的代碼是這段,then 方法用來處理接收數據后的處理邏輯,這個例子中主要通過SnackBar 展示一下接收的信息

Navigator.pushNamed(context, "/second",
        arguments:
            Photo(title: "pass title", message: "pass message"))
    .then((vale) {
  final snackBar = SnackBar(
    content: Text('Yay! A SnackBar!'),
    action: SnackBarAction(
      label: 'Undo',
      onPressed: () {
      },
    ),
  );
  Scaffold.of(context).showSnackBar(snackBar);
});

 

為什么要單獨抽取出FirstButton組件?

是因為SnackBar只能在Scaffold 組件代碼中使用會報錯

下面代碼是用於在推出當前頁面的時候,處理了ok 給前一個頁面

Navigator.pop(context, "ok");

 

 
 

總結

使用上跟Android 的使用方式類似,有點經驗的人掌握這個不是很難

https://docs.flutter.io/flutter/widgets/Navigator-class.html

https://www.raywenderlich.com/110-flutter-navigation-tutorial



作者:飢餓的大灰狼  來源:簡書


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM