在螢幕上新增一個 drawer

在 Material Design 設計準則裡,主要提供了兩種導向方式:Tab 和 Drawer。當沒有足夠的空間來支援 tab 導向時,drawer 提供了另一個方便的選擇。

在 Flutter中,我們可以將 Drawer widget 與 Scaffold 結合使用來建立一個具有 Material Design 風格的 Drawer 佈局。請參見如下的步驟:

  1. 建立一個 Scaffold

  2. 新增一個 drawer。

  3. 向 drawer 中新增內容。

  4. 透過程式碼關閉 drawer。

1. 建立一個 Scaffold

為了嚮應用中新增一個 Drawer,我們需要將其放在 Scaffold widget 中。 Scaffold Widget 為遵循 Material 設計規則的應用程式提供了一套統一的視覺化結構。它同樣支援一些特殊的 Material Design 元件,例如 Drawer,AppBar 和 SnackBar 等。

在這個例子中,我們想要建立一個帶有 drawerScaffold

Scaffold(
  appBar: AppBar(
    title: const Text('AppBar without hamburger button'),
  ),
  drawer: // Add a Drawer here in the next step.
);

2. 新增一個 drawer

我們現在可以在 Scaffold 上新增一個 drawer。雖然 drawer 可以是任何 widget,但最好還是使用 Material Library 中的 Drawer widget,因為這樣才符合 Material Design 設計規範。

Scaffold(
  appBar: AppBar(
    title: const Text('AppBar with hamburger button'),
  ),
  drawer: Drawer(
    child: // Populate the Drawer in the next step.
  ),
);

3. 向 drawer 中新增內容

既然已經有了一個 Drawer,我們現在就可以向其中新增內容。在這個例子中,我們將使用 ListView。雖然你也可以使用 Column widget,但是 ListView 在這種情況下將是更好的選擇,因為如果內容所佔用的空間超出了螢幕的話,它將能夠允許使用者進行捲動。

我們將使用 DrawerHeader 和兩個 ListTile widget 填充 ListView。有關使用 List 的更多訊息,請參閱實用教程中的 list recipes

Drawer(
  // Add a ListView to the drawer. This ensures the user can scroll
  // through the options in the drawer if there isn't enough vertical
  // space to fit everything.
  child: ListView(
    // Important: Remove any padding from the ListView.
    padding: EdgeInsets.zero,
    children: [
      const DrawerHeader(
        decoration: BoxDecoration(
          color: Colors.blue,
        ),
        child: Text('Drawer Header'),
      ),
      ListTile(
        title: const Text('Item 1'),
        onTap: () {
          // Update the state of the app.
          // ...
        },
      ),
      ListTile(
        title: const Text('Item 2'),
        onTap: () {
          // Update the state of the app.
          // ...
        },
      ),
    ],
  ),
);

4. 透過程式設計關閉 drawer

我們經常需要在使用者點選某個專案後就將 Drawer 關掉。那麼怎樣才能做到這一點呢?請試試看 Navigator

當用戶開啟 Drawer 時,Flutter 會將 drawer widget 覆蓋在當前的導向堆疊上。因此,要關閉 drawer,我們可以透過呼叫 Navigator.pop(context) 來實現。

ListTile(
  title: const Text('Item 1'),
  onTap: () {
    // Update the state of the app
    // ...
    // Then close the drawer
    Navigator.pop(context);
  },
),

互動式範例

該範例展示了如何在 Scaffold 中使用 DrawerDrawer 中包含三個 ListTile_onItemTapped 方法會改變當前選中的元素,並在 Scaffold 中央展示對應的文字。

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  static const appTitle = 'Drawer Demo';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: appTitle,
      home: MyHomePage(title: appTitle),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 0;
  static const TextStyle optionStyle =
      TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
  static const List<Widget> _widgetOptions = <Widget>[
    Text(
      'Index 0: Home',
      style: optionStyle,
    ),
    Text(
      'Index 1: Business',
      style: optionStyle,
    ),
    Text(
      'Index 2: School',
      style: optionStyle,
    ),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: _widgetOptions[_selectedIndex],
      ),
      drawer: Drawer(
        // Add a ListView to the drawer. This ensures the user can scroll
        // through the options in the drawer if there isn't enough vertical
        // space to fit everything.
        child: ListView(
          // Important: Remove any padding from the ListView.
          padding: EdgeInsets.zero,
          children: [
            const DrawerHeader(
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
              child: Text('Drawer Header'),
            ),
            ListTile(
              title: const Text('Home'),
              selected: _selectedIndex == 0,
              onTap: () {
                // Update the state of the app
                _onItemTapped(0);
                // Then close the drawer
                Navigator.pop(context);
              },
            ),
            ListTile(
              title: const Text('Business'),
              selected: _selectedIndex == 1,
              onTap: () {
                // Update the state of the app
                _onItemTapped(1);
                // Then close the drawer
                Navigator.pop(context);
              },
            ),
            ListTile(
              title: const Text('School'),
              selected: _selectedIndex == 2,
              onTap: () {
                // Update the state of the app
                _onItemTapped(2);
                // Then close the drawer
                Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
    );
  }
}