Flutter路由官方文檔學(xué)習(xí)筆記

在Flutter中,screen和pages都叫做路由(routes)。一個route就是一個widget,使用Navigator導(dǎo)航到新的一個route。

1.導(dǎo)航到新的頁面并返回

兩個route之間的導(dǎo)航由以下步驟:

  • 創(chuàng)建兩個route
  • 使用Navigator.push()導(dǎo)航到第二個route
  • 使用Navigator.pop()返回到第一個route

1.1 創(chuàng)建兩個routes

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Open route'),
          onPressed: () {
            // Navigate to second route when tapped.
          },
        ),
      ),
    );
  }
}

class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // Navigate back to first route when tapped.
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

1.2 使用Navigator.push()導(dǎo)航到第二個route

// Within the `FirstRoute` widget
onPressed: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );
}

1.3 使用Navigator.pop()返回到第一個route

// Within the SecondRoute widget
onPressed: () {
  Navigator.pop(context);
}

2. 使用命名routes進(jìn)行導(dǎo)航

上面通過使用創(chuàng)建route并將其推送到導(dǎo)航器來導(dǎo)航到新的頁面。

如果你的應(yīng)用中需要由很多地方導(dǎo)航到同一個頁面,如果在使用以上的方法那么代碼就會重復(fù)。解決辦法就是定義命名路由,通過命名路由進(jìn)行導(dǎo)航。

使用命名路由步驟:

  • 創(chuàng)建兩個頁面
  • 定義routes
  • 使用Navigator.pushNamed()導(dǎo)航到第二個頁面
  • 使用Navigator.pop()返回到第一個頁面

2.1 創(chuàng)建兩個頁面

代碼同上1.1

2.2 定義routes

通過MaterialApp構(gòu)造函數(shù)提供的附加屬性來定義routes:initialRoutes和routes它本身。

initialRoutes的屬性定義應(yīng)用程序從哪個route開始。
routes屬性定義可用的命名路由以及導(dǎo)航到這些路由時要構(gòu)建的widget。

MaterialApp(
  // Start the app with the "/" named route. In this case, the app starts
  // on the FirstScreen widget.
  initialRoute: '/',
  routes: {
    // When navigating to the "/" route, build the FirstScreen widget.
    '/': (context) => FirstScreen(),
    // When navigating to the "/second" route, build the SecondScreen widget.
    '/second': (context) => SecondScreen(),
  },
);

2.3 使用Navigator.pushNamed()導(dǎo)航到第二個頁面

// Within the `FirstScreen` widget
onPressed: () {
  // Navigate to the second screen using a named route.
  Navigator.pushNamed(context, '/second');
}

2.4 使用Navigator.pop()返回到第一個頁面

代碼同上1.3

3.將參數(shù)傳遞給命名路由

Navigator提供了使用公共標(biāo)識符從應(yīng)用程序的任何地方傳遞給命名路由。

使用Navigator.pushNamed()方法的arguments參數(shù)完成參數(shù)傳遞。

以下步驟將參數(shù)傳遞給命名路由并使用ModalRoute.of()和onGenerateRoute()讀取參數(shù):

  • 定義需要傳遞的參數(shù)。
  • 創(chuàng)建一個提取參數(shù)的widget。
  • 在路由表中注冊widget。
  • 導(dǎo)航到widget。

3.1 定義需要傳遞的參數(shù)

// You can pass any object to the arguments parameter.
// In this example, create a class that contains a customizable
// title and message.
class ScreenArguments {
  final String title;
  final String message;

  ScreenArguments(this.title, this.message);
}

3.2 創(chuàng)建一個提取參數(shù)的widget

創(chuàng)建一個widget從ScreenArguments獲取并顯示title和message。要訪問ScreenArguments,請使用ModalRoute.of()方法。此方法返回帶有參數(shù)的當(dāng)前路由。

// A widget that extracts the necessary arguments from the ModalRoute.
class ExtractArgumentsScreen extends StatelessWidget {
  static const routeName = '/extractArguments';

  @override
  Widget build(BuildContext context) {
    // Extract the arguments from the current ModalRoute settings and cast
    // them as ScreenArguments.
    final ScreenArguments args = ModalRoute.of(context).settings.arguments;

    return Scaffold(
      appBar: AppBar(
        title: Text(args.title),
      ),
      body: Center(
        child: Text(args.message),
      ),
    );
  }
}

3.3 在路由表中注冊widget

MaterialApp(
  routes: {
    ExtractArgumentsScreen.routeName: (context) => ExtractArgumentsScreen(),
  },     
);

3.4 導(dǎo)航到widget

// A button that navigates to a named route. The named route
// extracts the arguments by itself.
RaisedButton(
  child: Text("Navigate to screen that extracts arguments"),
  onPressed: () {
    // When the user taps the button, navigate to the specific route
    // and provide the arguments as part of the RouteSettings.
    Navigator.pushNamed(
      context,
      ExtractArgumentsScreen.routeName,
      arguments: ScreenArguments(
        'Extract Arguments Screen',
        'This message is extracted in the build method.',
      ),
    );
  },
);

使用onGenerateRoute提取參數(shù)

也可以在onGenerateRoute()函數(shù)中提取參數(shù)并將它們傳遞給widget,而不是直接在窗口小部件中提取參數(shù)。
onGenerateRoute()函數(shù)根據(jù)給定的RouteSettings創(chuàng)建正確的路由。

MaterialApp(
  // Provide a function to handle named routes. Use this function to
  // identify the named route being pushed, and create the correct
  // screen.
  onGenerateRoute: (settings) {
    // If you push the PassArguments route
    if (settings.name == PassArgumentsScreen.routeName) {
      // Cast the arguments to the correct type: ScreenArguments.
      final ScreenArguments args = settings.arguments;

      // Then, extract the required data from the arguments and
      // pass the data to the correct screen.
      return MaterialPageRoute(
        builder: (context) {
          return PassArgumentsScreen(
            title: args.title,
            message: args.message,
          );
        },
      );
    }
  },
);

4. 帶數(shù)據(jù)從頁面返回

有些時候我們需要從打開頁面帶數(shù)據(jù)返回到導(dǎo)航頁面。

使用Navigator.pop()方法帶回數(shù)據(jù)的步驟:

  • 定義主屏幕
  • 添加一個啟動選擇屏幕的按鈕
  • 使用兩個按鈕顯示選擇屏幕
  • 點(diǎn)擊按鈕時,關(guān)閉選擇屏幕
  • 在主屏幕上顯示選擇的snackbar

4.1 定義主屏幕

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Returning Data Demo'),
      ),
      // Create the SelectionButton widget in the next step.
      body: Center(child: SelectionButton()),
    );
  }
}

4.2 添加一個啟動選擇屏幕的按鈕

創(chuàng)建SelectionButton按鈕,它將做以下兩件事:

  • 點(diǎn)擊時打開選中頁面
  • 等待選中頁面返回結(jié)果
class SelectionButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      onPressed: () {
        _navigateAndDisplaySelection(context);
      },
      child: Text('Pick an option, any option!'),
    );
  }

  // A method that launches the SelectionScreen and awaits the
  // result from Navigator.pop.
  _navigateAndDisplaySelection(BuildContext context) async {
    // Navigator.push returns a Future that completes after calling
    // Navigator.pop on the Selection Screen.
    final result = await Navigator.push(
      context,
      // Create the SelectionScreen in the next step.
      MaterialPageRoute(builder: (context) => SelectionScreen()),
    );
  }
}

4.3 使用兩個按鈕顯示選擇屏幕

現(xiàn)在,構(gòu)建一個包含兩個按鈕的選擇屏幕。當(dāng)用戶點(diǎn)擊按鈕時,該應(yīng)用關(guān)閉選擇屏幕并讓主屏幕知道點(diǎn)擊了哪個按鈕。

class SelectionScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Pick an option'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: RaisedButton(
                onPressed: () {
                  // Pop here with "Yep"...
                },
                child: Text('Yep!'),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: RaisedButton(
                onPressed: () {
                  // Pop here with "Nope"
                },
                child: Text('Nope.'),
              ),
            )
          ],
        ),
      ),
    );
  }
}

4.4 點(diǎn)擊按鈕時,關(guān)閉選擇屏幕

現(xiàn)在,更新兩個按鈕的onPressed()回調(diào)。要將數(shù)據(jù)返回到第一個屏幕,請使用Navigator.pop()方法,該方法接受名為result的可選第二個參數(shù)。任何結(jié)果都會在SelectionButton中返回到Future。

Yep button
RaisedButton(
  onPressed: () {
    // The Yep button returns "Yep!" as the result.
    Navigator.pop(context, 'Yep!');
  },
  child: Text('Yep!'),
);
Nope button
RaisedButton(
  onPressed: () {
    // The Nope button returns "Nope!" as the result.
    Navigator.pop(context, 'Nope!');
  },
  child: Text('Nope!'),
);

4.5 在主屏幕上顯示選擇的snackbar

現(xiàn)在您正在啟動選擇屏幕并等待結(jié)果,您將需要對返回的信息執(zhí)行某些操作。

_navigateAndDisplaySelection(BuildContext context) async {
  final result = await Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SelectionScreen()),
  );

  // After the Selection Screen returns a result, hide any previous snackbars
  // and show the new result.
  Scaffold.of(context)
    ..removeCurrentSnackBar()
    ..showSnackBar(SnackBar(content: Text("$result")));
}

5. 將數(shù)據(jù)發(fā)送到新頁面

通常,您不僅要導(dǎo)航到新屏幕,還要將數(shù)據(jù)傳遞到屏幕。例如,您可能希望傳遞有關(guān)已點(diǎn)擊的項目的信息

請記?。浩聊恢皇切〔考?。在此示例中,創(chuàng)建待辦事項列表。輕觸待辦事項時,導(dǎo)航到顯示有關(guān)待辦事項信息的新屏幕(widget)。此配方使用以下步驟:

  • 定義待辦事項類
  • 顯示待辦事項列表
  • 創(chuàng)建一個可以顯示待辦事項信息的詳細(xì)信息屏幕
  • 導(dǎo)航并將數(shù)據(jù)傳遞到詳細(xì)信息屏幕

5.1 定義待辦事項類

首先,您需要一種簡單的方式來表示待辦事項。對于此示例,請創(chuàng)建一個包含兩個數(shù)據(jù)的類:標(biāo)題和說明。

class Todo {
  final String title;
  final String description;

  Todo(this.title, this.description);
}

5.2 顯示待辦事項列表

其次,顯示待辦事項列表。在此示例中,生成20個待辦事項并使用ListView顯示它們。

生成待辦事項列表
final todos = List<Todo>.generate(
  20,
  (i) => Todo(
        'Todo $i',
        'A description of what needs to be done for Todo $i',
      ),
);
使用ListView顯示待辦事項列表
ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
    );
  },
);

5.3 創(chuàng)建一個可以顯示待辦事項信息的詳細(xì)信息屏幕

現(xiàn)在,創(chuàng)建第二個屏幕。屏幕標(biāo)題包含待辦事項的標(biāo)題,屏幕正文顯示說明。

class DetailScreen extends StatelessWidget {
  // Declare a field that holds the Todo.
  final Todo todo;

  // In the constructor, require a Todo.
  DetailScreen({Key key, @required this.todo}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text(todo.description),
      ),
    );
  }
}

5.4 導(dǎo)航并將數(shù)據(jù)傳遞到詳細(xì)信息屏幕

有了DetailScreen,您就可以執(zhí)行導(dǎo)航了。在此示例中,當(dāng)用戶點(diǎn)擊列表中的待辦事項時,導(dǎo)航到DetailScreen。將待辦事項傳遞給DetailScreen。

ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
      // When a user taps the ListTile, navigate to the DetailScreen.
      // Notice that you're not only creating a DetailScreen, you're
      // also passing the current todo to it.
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => DetailScreen(todo: todos[index]),
          ),
        );
      },
    );
  },
);

參考
flutter.dev Navigation
flutter_route sample

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容