Flutter11 路由、頁面傳值、Navigator

RainDou發表於2019-09-15

本文對應github地址Flutter11,如果由於github調整導致資源找不到,請訪問github

路由

  1. 簡介

    • iOS中頁面跳轉通過NavigationController,然後pushViewController,Android中可以初始化Intent然後startActivity,Flutter中則路由(Route)方式
    • 程式啟動後路由棧第一個(最底部)例項 MaterialApp(home: Screen1())
    • 命名路由,對頁面起別名(唯一字串),但一般用有意義字串,如 '/'表示根頁面,'/login'表示登入頁等
  2. 靜態路由push

    • 靜態路由在MeterialApp初始化時配置routes引數,通過起的別名(命名路由)進行跳轉
    • 新頁面為配置的固定頁面,且跳轉時引數也是固定的
    • 呼叫方法為pushNamed()
    // MaterialApp初始化時配置Key-Value
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primaryColor: Colors.red,
            primarySwatch: Colors.blue,
          ),
          home: DDYBottomBar(),
          showPerformanceOverlay: false,
          routes: {
            '/home/qrcode': (BuildContext context) => QRCodeScanner(title: '0',),
            '/home_qrcode': (BuildContext context) => QRCodeScanner(title: '1',),
            '+home_qrcode': (BuildContext context) => QRCodeScanner(title: '2',),
          },
        );
      }
    }
    
    // 在需要的地方呼叫['/home/qrcode'只是字串名字,並不是真正路徑,這樣仿路徑是為了可讀性]
    // Navigator.pushNamed(context, '/home/qrcode');
    // Navigator.pushNamed(context, '/home_qrcode');
    // Navigator.pushNamed(context, '+home_qrcode');
    Navigator.of(context).pushNamed('+home_qrcode');
    複製程式碼
  3. 動態路由push

    • 動態路由在需要跳轉時生成要跳轉的頁面物件,傳遞想要的引數,然後呼叫push()函式
    // Navigator.push(context, MaterialPageRoute(builder: (context) => QRCodeScanner()));
    // Navigator.of(context).push(MaterialPageRoute(builder: (_) => QRCodeScanner()));
    Navigator.of(context).push(MaterialPageRoute(builder: (context) => QRCodeScanner()));
    複製程式碼
  4. 路由pop

    • pop()方法中可新增回撥資料
    // Navigator.of(context).pop();
    // Navigator.pop(context);
    Navigator.of(context).pop('回撥資料');
    複製程式碼
  5. 頁面傳值

  • 正向傳值直接通過簡單建構函式引數方式即可

  • 反向傳值,不同風格其實都一樣,建議第一種

    // 風格1
    pushQRCodeScannerAndCallbackData1() {
      Navigator.of(context).push(MaterialPageRoute(builder: (context) => QRCodeScanner())).then((value){
        print('1 $value');
      });
    }
    // 風格2
    pushQRCodeScannerAndCallbackData2() {
      Future callbackFuture = Navigator.of(context).push(MaterialPageRoute(builder: (_) => QRCodeScanner()));
      callbackFuture.then((value){
        print('2 $value');
      });
    }
    // 風格3
    pushQRCodeScannerAndCallbackData3() async {
      // 如果知道型別
      String callbackString = await Navigator.of(context).push<String>(
          MaterialPageRoute(builder: (context){
            // 正向傳值直接通過簡單建構函式引數方式即可
            return QRCodeScanner(title:'I am 3');
          })
      );
      if (callbackString != null) {
        print('3 ${callbackString}');
      }
    }
    
    
    // pop()函式引數值即為反向傳回的資料
    Navigator.of(context).pop('回撥資料');
    複製程式碼
  • 還可以通過閉包作為引數進行傳值

    pushQRCodeGenerator() {
      Navigator.of(context).push(
        MaterialPageRoute(
          builder: (context) => QRCodeGenerator((String value) {
            print('QRCodeGenerator callback $value');
          }),
        ),
      );
    }
    
    
    // 呼叫
    IconButton(
      icon: Icon(Icons.print), 
        onPressed: () {
          if (widget.callbackFunction != null) {
            widget.callbackFunction('回撥資料');
          }
        },
    ),
    複製程式碼
  1. 定製路由(自定義路由,自定義轉場)
  • 繼承路由子類,如:PopupRoute、ModalRoute 等

  • 使用 PageRouteBuilder 類通過回撥函式定義路由

    • 從下向上彈出,從上向下收回
    pushQRCodeScannerWithCustomAnimation() {
      Navigator.push(
        context,
        PageRouteBuilder(pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
          return QRCodeScanner();
        }, transitionsBuilder: (context, animation, secondaryAnimation, child) {
          return transitionAnimation(animation, child);
        }),
      );
    }
    
    static SlideTransition transitionAnimation(Animation<double> animation, Widget child) {
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(0.0, 1.0),
          end: const Offset(0.0, 0.0),
        ).animate(animation),
        child: child,
      );
    }
    複製程式碼
    • 頁面旋轉淡出的效果
    pushQRCodeScannerWithCustomAnimation2() async {
      // 頁面旋轉淡出的效果
      transitionAnimation(Animation<double> animation, Widget child) {
        return FadeTransition(
          opacity: animation,
          child: RotationTransition(
            turns: Tween<double>(begin: 0.7, end:1.0).animate(animation),
            child: child,
          ),
        );
      }
    
      Navigator.push(
        context,
        PageRouteBuilder(
          pageBuilder: (context, _, __) => QRCodeScanner(),
          transitionDuration: const Duration(milliseconds: 1000),
          transitionsBuilder: (_, animation, __, child) => transitionAnimation(animation, child),
        ),
      ).then((value){
        Scaffold.of(context).showSnackBar(
          SnackBar(
            content: Text(value),
            duration: const Duration(seconds: 3),
          ),
        );
      });
    }
    複製程式碼
  1. 巢狀路由
  • 一個應用程式可以使用多個路由導航器

  • 可將一個導航器巢狀在另一個導航器下方使用,例如選項卡式導航,使用者註冊,訂單與結賬頁等

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          initialRoute: '/',
          routes: {
            '/': (BuildContext context) => HomePage(),
            '/signup': (BuildContext context) => SignUpPage(),
          },
        );
      }
    }
    
    class SignUpPage extends StatelessWidget {
     @override
     Widget build(BuildContext context) {
       return Navigator(
         initialRoute: 'signup/personal_info',
         onGenerateRoute: (RouteSettings settings) {
           WidgetBuilder builder;
           switch (settings.name) {
             case 'signup/personal_info':
               // 個人資訊完畢後到 'signup/choose_credentials'.
               builder = (BuildContext _) => CollectPersonalInfoPage();
               break;
             case 'signup/choose_credentials':
               // 選完credentials 呼叫 'onSignupComplete()'.
               builder = (BuildContext _) => ChooseCredentialsPage(
                 onSignupComplete: () {
                   // 回到路由根頁面 '/' (HomePage)
                   Navigator.of(context).pop();
                 },
               );
               break;
             default:
               throw Exception('Invalid route: ${settings.name}');
           }
           return MaterialPageRoute(builder: builder, settings: settings);
         },
       );
     }
    }
    複製程式碼

Navigator

  1. 繼承自StatefulWidget,路由堆疊的管理者
  2. 靜態方法詳解
  • pushNamed
  • pushReplacementNamed
  • popAndPushNamed
  • pushNamedAndRemoveUntil
  • push
  • pushReplacement
  • pushAndRemoveUntil
  • replace
  • replaceRouteBelow
  • canPop
  • maybePop
  • popUntil
  • removeRoute
  • removeRouteBelow
push和pushName

pushName結合配置routes,利用命名路由形式將指定頁面例項(帶固定引數)入棧,而push則是在需要跳轉時生成要跳轉的頁面物件,傳遞想要的引數,兩者執行效果沒差別。

pushReplacement和pushReplacementNamed

兩者都是取代棧頂元素,執行效果沒差別。

popAndPushNamed

顧名思義,先pop然後push

pushAndRemoveUntil 和 pushNamedAndRemoveUntil

兩者都是入棧新頁面刪除除該頁面例項外其他棧內例項,將該例項作為新棧底,執行效果沒差別。可用在註冊登入頁登入後跳轉,防止能返回註冊登入頁

canPop

判斷是否可以pop,如果可以返回true,否則返回false

maybePop

表示在該頁面嘗試pop,如果可以則pop到前一頁,否則仍停在該頁面,丟棄該次pop

popUntil

pop回棧內(該頁面例項以下)指定頁面例項,如 Navigator.popUntil(context, ModalRoute.withName('/home'));,回到配置名為home的頁面。

其他知識點

  1. 去除返回按鈕 AppBar中

    automaticalImplyLeading: false
    複製程式碼
  2. Popup routes (彈出路由)

路由不一定要遮擋整個螢幕。 PopupRoutes 使用 ModalRoute.barrierColor 覆蓋螢幕,ModalRoute.barrierColor 只能部分不透明以允許當前螢幕顯示。 彈出路由是“模態”的,因為它們阻止了對下面其他元件的輸入。

有一些方法可以建立和顯示這類彈出路由。 例如:showDialog,showMenu 和 showModalBottomSheet。 如上所述,這些函式返回其推送路由的 Future(非同步資料,參考下面的資料部分)。 執行可以等待返回的值在彈出路由時執行操作。

還有一些元件可以建立彈出路由,如 PopupMenuButton 和 DropdownButton。 這些元件建立 PopupRoute 的內部子類,並使用 Navigator 的push 和 pop 方法來顯示和關閉它們。

參考

上一頁 Flutter10 Icon、Color、Tabbar、BottomNavigationBar
下一頁 Flutter12 IndexedStack、Table、Flow、Warp

相關文章