跳至主要內容

傳送資料到新畫面

通常,您不僅希望導航到新的螢幕,還希望將資料傳遞到該螢幕。例如,您可能希望傳遞有關已點擊項目的資訊。

請記住:螢幕只是 Widget。在此範例中,建立一個待辦事項列表。當點擊一個待辦事項時,導航到一個新的螢幕(Widget),該螢幕顯示有關該待辦事項的資訊。此食譜使用以下步驟

  1. 定義一個待辦事項類別。
  2. 顯示待辦事項列表。
  3. 建立一個可以顯示待辦事項資訊的詳細螢幕。
  4. 導航並將資料傳遞到詳細螢幕。

1. 定義一個待辦事項類別

#

首先,您需要一個簡單的方法來表示待辦事項。在此範例中,建立一個包含兩個資料片段的類別:標題和描述。

dart
class Todo {
  final String title;
  final String description;

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

2. 建立一個待辦事項列表

#

其次,顯示待辦事項列表。在此範例中,產生 20 個待辦事項,並使用 ListView 顯示它們。有關使用列表的更多資訊,請參閱使用列表食譜。

產生待辦事項列表

#
dart
final todos = List.generate(
  20,
  (i) => Todo(
    'Todo $i',
    'A description of what needs to be done for Todo $i',
  ),
);

使用 ListView 顯示待辦事項列表

#
dart
ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
    );
  },
)

到目前為止,一切順利。這會產生 20 個待辦事項,並將它們顯示在 ListView 中。

3. 建立一個待辦事項螢幕來顯示列表

#

為此,我們建立一個 StatelessWidget。我們稱之為 TodosScreen。由於此頁面的內容在執行期間不會變更,因此我們必須在此 Widget 的範圍內要求待辦事項列表。

我們將 ListView.builder 作為我們返回給 build() 的 Widget 的主體傳入。這會將列表呈現到螢幕上,讓您可以開始使用!

dart
class TodosScreen extends StatelessWidget {
  // Requiring the list of todos.
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todos'),
      ),
      //passing in the ListView.builder
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
          );
        },
      ),
    );
  }
}

使用 Flutter 的預設樣式,您可以順利進行,而無需為您以後想做的事情煩惱!

4. 建立一個詳細螢幕來顯示待辦事項的資訊

#

現在,建立第二個螢幕。螢幕的標題包含待辦事項的標題,螢幕的主體顯示描述。

由於詳細螢幕是正常的 StatelessWidget,因此要求使用者在 UI 中輸入 Todo。然後,使用給定的待辦事項建立 UI。

dart
class DetailScreen extends StatelessWidget {
  // In the constructor, require a Todo.
  const DetailScreen({super.key, required this.todo});

  // Declare a field that holds the Todo.
  final Todo todo;

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

5. 導航並將資料傳遞到詳細螢幕

#

有了 DetailScreen 後,您就可以執行導航了。在此範例中,當使用者點擊列表中的待辦事項時,導航到 DetailScreen。將待辦事項傳遞到 DetailScreen

若要擷取使用者在 TodosScreen 中的點擊,請為 ListTile Widget 撰寫 onTap() 回呼。在 onTap() 回呼中,使用 Navigator.push() 方法。

dart
body: 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 through to it.
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => DetailScreen(todo: todos[index]),
          ),
        );
      },
    );
  },
),

互動式範例

#
import 'package:flutter/material.dart';

class Todo {
  final String title;
  final String description;

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

void main() {
  runApp(
    MaterialApp(
      title: 'Passing Data',
      home: TodosScreen(
        todos: List.generate(
          20,
          (i) => Todo(
            'Todo $i',
            'A description of what needs to be done for Todo $i',
          ),
        ),
      ),
    ),
  );
}

class TodosScreen extends StatelessWidget {
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todos'),
      ),
      body: 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 through to it.
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailScreen(todo: todos[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  // In the constructor, require a Todo.
  const DetailScreen({super.key, required this.todo});

  // Declare a field that holds the Todo.
  final Todo todo;

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

或者,使用 RouteSettings 傳遞引數

#

重複前兩個步驟。

建立一個詳細螢幕來提取引數

#

接下來,建立一個詳細螢幕,從 Todo 中提取並顯示標題和描述。若要存取 Todo,請使用 ModalRoute.of() 方法。此方法會傳回具有引數的目前路由。

dart
class DetailScreen extends StatelessWidget {
  const DetailScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final todo = ModalRoute.of(context)!.settings.arguments as Todo;

    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Text(todo.description),
      ),
    );
  }
}
#

最後,當使用者使用 Navigator.push() 點擊 ListTile Widget 時,導航到 DetailScreen。將引數作為 RouteSettings 的一部分傳遞。DetailScreen 會提取這些引數。

dart
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 through to it.
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => const DetailScreen(),
            // Pass the arguments as part of the RouteSettings. The
            // DetailScreen reads the arguments from these settings.
            settings: RouteSettings(
              arguments: todos[index],
            ),
          ),
        );
      },
    );
  },
)

完整範例

#
dart
import 'package:flutter/material.dart';

class Todo {
  final String title;
  final String description;

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

void main() {
  runApp(
    MaterialApp(
      title: 'Passing Data',
      home: TodosScreen(
        todos: List.generate(
          20,
          (i) => Todo(
            'Todo $i',
            'A description of what needs to be done for Todo $i',
          ),
        ),
      ),
    ),
  );
}

class TodosScreen extends StatelessWidget {
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todos'),
      ),
      body: 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 through to it.
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const DetailScreen(),
                  // Pass the arguments as part of the RouteSettings. The
                  // DetailScreen reads the arguments from these settings.
                  settings: RouteSettings(
                    arguments: todos[index],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  const DetailScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final todo = ModalRoute.of(context)!.settings.arguments as Todo;

    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Text(todo.description),
      ),
    );
  }
}