티스토리 뷰

반응형

Material Design을 사용하는 앱에서는 내비게이션을 위한 두 가지 주요 옵션으로 탭과 드로어가 있습니다. 탭을 지원하기에 충분한 공간이 없을 때 드로어는 유용한 대안이 됩니다.

 

Flutter에서, 드로어 위젯과 Scaffold를 조합하여 Material Design 드로어가 포함된 레이아웃을 만들 수 있습니다.

 

이 문서에서는 다음 단계를 사용합니다:

  1. Scaffold 생성
  2. 드로어 추가
  3. 드로어에 항목 채우기
  4. 드로어를 프로그래밍 방식으로 닫기

 

 

 

1. Scaffold 생성

앱에 드로어를 추가하려면, 이를 Scaffold 위젯으로 감싸야 합니다. Scaffold 위젯은 Material Design 가이드라인을 따르는 앱에 일관된 시각적 구조를 제공합니다. 또한, 드로어, AppBar, SnackBar와 같은 특별한 Material Design 컴포넌트들을 지원합니다.

 

이 예제에서는 드로어가 포함된 Scaffold를 생성합니다:

Scaffold(
  appBar: AppBar(
    title: const Text('햄버거 버튼 없는 AppBar'),
  ),
  drawer: // 다음 단계에서 여기에 드로어를 추가합니다.
);

 

 

 

2. 드로어 추가

이제 Scaffold에 드로어를 추가합니다. 드로어는 어떤 위젯이라도 될 수 있지만, Material 라이브러리의 Drawer 위젯을 사용하는 것이 좋습니다. 이 위젯은 Material Design 사양을 따릅니다.

Scaffold(
  appBar: AppBar(
    title: const Text('햄버거 버튼 있는 AppBar'),
  ),
  drawer: Drawer(
    child: // 다음 단계에서 드로어에 내용을 채웁니다.
  ),
);

 

 

 

3. 드로어에 항목 채우기

이제 드로어가 준비되었으므로, 여기에 콘텐츠를 추가합니다. 이 예제에서는 ListView를 사용합니다. Column 위젯을 사용할 수도 있지만, ListView는 콘텐츠가 화면을 초과하게 되면 사용자가 드로어를 스크롤할 수 있게 해 줍니다.

 

ListView에 DrawerHeader와 두 개의 ListTile 위젯을 추가합니다. 목록 작업에 대한 자세한 내용은 리스트 뷰 문서를 참조하세요.

Drawer(
  // 드로어에 ListView를 추가합니다. 이는 콘텐츠가 화면을 초과하는 경우 사용자가 드로어를 스크롤할 수 있게 합니다.
  child: ListView(
    // 중요: ListView에서 모든 패딩을 제거합니다.
    padding: EdgeInsets.zero,
    children: [
      const DrawerHeader(
        decoration: BoxDecoration(
          color: Colors.blue,
        ),
        child: Text('드로어 헤더'),
      ),
      ListTile(
        title: const Text('항목 1'),
        onTap: () {
          // 앱의 상태를 업데이트합니다.
          // ...
        },
      ),
      ListTile(
        title: const Text('항목 2'),
        onTap: () {
          // 앱의 상태를 업데이트합니다.
          // ...
        },
      ),
    ],
  ),
);

 

 

 

4. 드로어를 프로그래밍 방식으로 열기

일반적으로 드로어를 열기 위해 별도의 코드를 작성할 필요는 없습니다. 왜냐하면, leading 위젯이 null인 경우 AppBar의 기본 구현은 DrawerButton이기 때문입니다.

 

하지만 드로어를 자유롭게 제어하려면 Builder를 사용하여 Scaffold.of(context).openDrawer()를 호출할 수 있습니다.

Scaffold(
  appBar: AppBar(
    title: const Text('햄버거 버튼 있는 AppBar'),
    leading: Builder(
      builder: (context) {
        return IconButton(
          icon: const Icon(Icons.menu),
          onPressed: () {
            Scaffold.of(context).openDrawer();
          },
        );
      },
    ),
  ),
  drawer: Drawer(
    child: // 마지막 단계에서 드로어에 내용을 채웁니다.
  ),
);

 

 

 

5. 드로어를 프로그래밍 방식으로 닫기

사용자가 항목을 탭한 후 드로어를 닫고 싶을 수 있습니다. 이를 위해 Navigator를 사용할 수 있습니다.

 

사용자가 드로어를 열면 Flutter는 드로어를 내비게이션 스택에 추가합니다. 따라서, 드로어를 닫으려면 Navigator.pop(context)를 호출합니다.

ListTile(
  title: const Text('항목 1'),
  onTap: () {
    // 앱의 상태를 업데이트합니다
    // ...
    // 그런 다음 드로어를 닫습니다
    Navigator.pop(context);
  },
);

 

 

 

전체 예제

이 예제는 Scaffold 위젯 내에서 사용되는 Drawer를 보여줍니다. 드로어에는 세 개의 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),
        leading: Builder(
          builder: (context) {
            return IconButton(
              icon: const Icon(Icons.menu),
              onPressed: () {
                Scaffold.of(context).openDrawer();
              },
            );
          },
        ),
      ),
      body: Center(
        child: _widgetOptions[_selectedIndex],
      ),
      drawer: Drawer(
        // 드로어에 ListView를 추가합니다. 
        // ListView의 내용이 화면을 초과하게 되면 스크롤할 수 있게 합니다.
        child: ListView(
          // 중요: 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: () {
                // 앱의 상태를 업데이트합니다.
                _onItemTapped(0);
                // 그런 다음 드로어를 닫습니다.
                Navigator.pop(context);
              },
            ),
            ListTile(
              title: const Text('Business'),
              selected: _selectedIndex == 1,
              onTap: () {
                // 앱의 상태를 업데이트합니다.
                _onItemTapped(1);
                // 그런 다음 드로어를 닫습니다.
                Navigator.pop(context);
              },
            ),
            ListTile(
              title: const Text('School'),
              selected: _selectedIndex == 2,
              onTap: () {
                // 앱의 상태를 업데이트합니다.
                _onItemTapped(2);
                // 그런 다음 드로어를 닫습니다.
                Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
    );
  }
}

 

반응형
댓글
공지사항