การใช้งาน Drawer กำหนด SideMenu ใน Flutter เบื้องต้น

เขียนเมื่อ 4 ปีก่อน โดย Ninenik Narkdee
useraccountsdrawerheader font awesome circleavatar flutter drawer menu sidemenu

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ useraccountsdrawerheader font awesome circleavatar flutter drawer menu sidemenu

ดูแล้ว 14,145 ครั้ง




เนื้อหาตอนต่อไปนี้ เราจะมาประยุกต์ใช้งาน Drawer
ในโปรเจ็ค App ของเราต่อ จากตอนที่แล้ว โดย Drawer ก็คือ
SideMenu หรือเมนูด้านข้างที่สามารถเลื่อนเพื่อแสดงในแนวนอน
    ทบทวนเนื้อหาตอนที่แล้วได้ที่
    การใช้งาน Theme และรู้จักกับ InheritedWidget ใน Flutter http://niik.in/959 
 

 

รู้จัก Widget เพิ่มเติม

    การใช้งาน Drawer Widget

    Drawer เป็นส่วนของเมนู ที่สามารถปัดเลื่อนจากขอบด้านข้างของ App เพื่อแสดงหรือซ่อน
หรือสามารถใช้ปุ่มเมนูใน AppBar เพื่อปิดและเปิดเมนูได้ โดยปุ่มเมนูจะแสดงอัตโนมัติถ้าเรามีการ
ใช้งาน Drawer widget
    เราสามารถกำหนดใช้งาน Drawer widget โดยกำหนดใน drawer property ของ Scaffold อีกที
นั่นคือ หน้าที่มีการใช้งาน Scaffold ก็สามารถกำหนด drawer ได้ ซึ่งส่วนใหญ่แล้ว ก็จะกำหนดใน
หน้าหลักๆ ที่สำคัญเพียงหน้าเดียว  และเนื่องจาก drawer เป็นเหมือนเมนูสำหรับ Navigation ไปยัง
หน้าต่างๆ การเปลี่ยนใน widget นี้อาจจะมีน้อยมาก เราจะสร้างเป็น component หนึ่งเพื่อให้สามารถเรียก
ใช้งาน และแก้ไขได้ง่ายขึ้น
 
Scaffold(
  drawer: // เพิ่ม Drawer เข้าไปใน drawer property ของ scaffold
);
    การใช้งาน drawer ส่วนใหญ่จะใช้ ListView แสดงรายการเมนูที่ต้องการ แล้วแยกส่วนที่เป็น Header 
โดยใช้ DrawerHeader ส่วนลิสรายการเมนู ก็กำหนดโดยใช้ ListTile ตามรูปแบบคร่าวๆ ดังนี้
 
drawer: Drawer(
        child: ListView(
            padding: EdgeInsets.zero,
            children: <Widget>[
                DrawerHeader(
                    child: Text('Drawer Header'),
                    decoration: BoxDecoration(
                        color: Theme.of(context).primaryColor,
                    ),
                ),
                ListTile(
                    title: Text('Item 1'),
                    onTap: () {}
                ),
                Divider(),
                ListTile(
                    title: Text('Item 2'),
                    onTap: () {},
                ),
            ],
        ),
),

 

 
 
    หรือถ้าต้องการให้สามารถปรับ ได้มากขึ้นก็อาจจะใช้ Column widget จัดการก็ได้ โดยกำหนด Header
เป็น DrawerHeader และลิสรายการเมนูเป็น ListTile ในรูปแบบคล้ายกับการใช้งาน ListView widget แต่ข้อดี
ของการใช้งาน Column คือเราสามารถจัดการลิสรายการในรูปแบบ Flex ที่มีความยืดหยุ่นได้มากกว่า เช่น 
สามารถใช้งาน Expanded widget เพื่อขยายความสูงของเมนูในสัดส่วนที่ต้องการได้
    ในส่วนของ Header นอกจากเราสามารถใช้งาน DrawerHeader แล้ว เรายังสามาระใช้งาน UserAccountsDrawerHeader
ซึ่งน่าจะพอคุ้น กับรูปแบบที่แสดงรูปโพรไฟล์ของผู้ใช้ ชื่อผู้ใช้ หรืออีเมล ตัวอย่างเช่น
 
drawer: Drawer(
        child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
                UserAccountsDrawerHeader(
                    accountName: Text('Ebiwayo'),
                    accountEmail: Text('ebiwayo@ebiwayo.com'),
                    currentAccountPicture: CircleAvatar(
                        child: FlutterLogo(size: 42.0,),
                        backgroundColor: Colors.white,
                    ),
                    otherAccountsPictures: <Widget>[
                        CircleAvatar(
                            child: Text("N"),
                            foregroundColor: Colors.white,
                            backgroundColor: Colors.orange,
                        ),
                        CircleAvatar(
                            child: Icon(Icons.add),
                            foregroundColor: Colors.white,
                            backgroundColor: Colors.grey,
                        )
                    ],
                ),
                ListTile(
                    title: Text('Item 1'),
                    onTap: () {}
                ),
                ListTile(
                    title: Text('Item 2'),
                    onTap: () {},
                ),
                Divider(),
                Expanded(
                    child: Align(
                        alignment: Alignment.bottomLeft,
                        child: ListTile(
                            title: Text('Item 3'),
                            onTap: () {},
                        ),
                    ),
                ),
            ],
        ),
),


 
 
    ในรูปแบบที่สอง เราสามารถปรับแต่งเพิ่มเติมได้ง่ายขึ้น UserAccountsDrawerHeader ในตัวอย่างเราใช้ CircleAvatar เพื่อกำหนด
การแสดงรูปโพรไฟล์ โดยจำลองใช้ Flutter โลโก้แทนรูป นอกจากนั้นเรายังสามารถเพิ่ม otherAccountsPictures property เพื่อแสดง
บัญชีผู้ใช้เพิ่มเติม ถ้า App ของเรารองรับการใช้งานหลายบัญชีพร้อมกัน  ในส่วนของการลิสรายการ เรามีการใช้งาน Expended เพื่อขยาย
พื้นที่เหลือว่างให้เติม หรือใช้รูปแบบ Flex เข้ามาช่วย จากนั้นเราก็จัดด้วย Align widget เพื่อกำหนดให้เฉพาะ ลิสรายการเมนูตัวสุดท้าย
แสดงแยกออกมาด้านล่างสุด 
 
     ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ Drawer Widget API    
 
 
  

ติดตั้ง Font Awesome สำหรับ Flutter

    ก่อนเข้าสู่เนื้อหาการปะรยุกต์ การใช้งาน Drawer กับโปรเจ็ค App ของเรา ขอแนะนำ package ดีๆ สำหรับใช้งาน Icon เพิ่มเติม
แทนการใช้งาน Icon ของ Flutter เดิม นั่นคือ Font Awesome สำหรับ Flutter ที่รวบรวม icon ต่างๆ มากมายสำหรับใช้งานใน Flutter
และเราก็จะนำมาประยุกต์ใช้กับโปรเจ็คของเราด้วย 
    ตัวอย่างการใช้งาน
 
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

class MyWidget extends StatelessWidget {
  Widget build(BuildContext context) {
    return IconButton(
      // Use the FontAwesomeIcons class for the IconData
      icon: Icon(FontAwesomeIcons.gamepad), 
      onPressed: () { print("Pressed"); }
     );
  }
}
    Font Awesome สำหรับ Flutter 
    ดูชื่อ Icon ฟรีของ Font Awesome Free Icons
    ดูแนวทางการติดตั้ง package จากภายนอก ตามลิ้งค์บทความด้านล่าง หลังติดตั้งแล้ว อย่าลืม หยุด App แล้วรันใหม่อีกครั้ง
เพราะอาจจะมีค่า cache ทำให้ icon ไม่แสดง
    การติดตั้งและใช้งาน Package ภายนอก ใน Flutter เบื้องต้น http://niik.in/956 
 

 
 

ประยุกต์การใช้งาน Drawer

    เพื่อให้การประยุกต์ใช้งานดูไม่ซับซ้อน เรียบง่าย เข้าใจไม่ยุ่งยาก จะขอปรับโครงสร้างโปรเจ็ค App ใหม่ และยกเลิกโค้ดเดิมบางส่วน
เพื่อใช้โครงสร้างจากโปรเจ็คใหม่ และจะใช้ เป็นต้นแบบสำหรับแนะนำเนื้อหาในตอนต่อไป ดังนี้
 
 

 
 
    ในโฟลเดอร์ screen จะเป็นส่วนของ package สำหรับจัดการหน้าต่างๆ และใช้ชื่อไฟล์เพื่อสื่อความหมายการทำงานในหน้านั้นๆ โดยตรง
โฟลเดอร์ components สำหรับเก็บส่วนองค์ประกอบ ที่เราสามารถนำไปเสริมหรือแยกไว้ สำหรับใช้งานเฉพาะส่วน ในตัวอย่างมีไฟล์
sidemenu.dart สำหรับจัดการ Drawer ไว้ใช้งาน  และส่วนสุดท้ายก็ไฟล์ main.dart ในโปรเจ็คนี้ เราใช้งาน font awesome package
เสริมเข้ามา ตามที่ได้แนะนำไปตอนต้น
    จะได้ไฟล์ทั้งหมดดังนี้
 
    ไฟล์ main.dart
import 'package:flutter/material.dart';
import './screens/home.dart';
import './screens/contact.dart';
import './screens/profile.dart';
import './screens/about.dart';
import './screens/settings.dart';
 
void main() {  runApp(const MyApp());}
 
// ส่วนของ Stateless widget
class MyApp extends StatelessWidget{
   const MyApp({Key? key}) : super(key: key);
   
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
                theme: ThemeData(
                      primarySwatch: Colors.pink
                ),
                title: 'First Flutter App',
                initialRoute: '/', // สามารถใช้ home แทนได้
                routes: {
                    Home.routeName: (context) => Home(),
                    About.routeName: (context) => About(),
                    Profile.routeName: (context) => Profile(),
                    Contact.routeName: (context) => Contact(),
                    Settings.routeName: (context) => Settings(),
                },
        );
    }
}
    ไฟล์ home.dart
import 'package:flutter/material.dart';
import '../components/sidemenu.dart';
 
class Home extends StatefulWidget {
    static const routeName = '/';

    const Home({Key? key}) : super(key: key);
 
    @override
    State<StatefulWidget> createState() {
        return _HomeState();
    }
}
 
class _HomeState extends State<Home> {
 
    @override
    Widget build(BuildContext context) {
 
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Text('Home Screen'),
                    ],
                )
            ),
            drawer: SideMenu(),
        );
    }
}
    ไฟล์ about.dart
import 'package:flutter/material.dart';
 
class About extends StatefulWidget {
    static const routeName = '/about';

    const About({Key? key}) : super(key: key);
 
    @override
    State<StatefulWidget> createState() {
        return _AboutState();
    }
}
 
class _AboutState extends State<About> {
 
    @override
    Widget build(BuildContext context) {
 
        return Scaffold(
            appBar: AppBar(
                title: Text('About Us'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Text('About Us Screen'),
                    ],
                )
            ),
        );
    }
}
    ไฟล์ profile.dart
import 'package:flutter/material.dart';
 
class Profile extends StatefulWidget {
    static const routeName = '/profile';

    const Profile({Key? key}) : super(key: key);
 
    @override
    State<StatefulWidget> createState() {
        return _ProfileState();
    }
}
 
class _ProfileState extends State<Profile> {
 
    @override
    Widget build(BuildContext context) {
 
        return Scaffold(
            appBar: AppBar(
                title: Text('Profile'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Text('Profile Screen'),
                    ],
                )
            ),
        );
    }
}
    ไฟล์ contact.dart
import 'package:flutter/material.dart';
 
class Contact extends StatefulWidget {
    static const routeName = '/contact';

    const Contact({Key? key}) : super(key: key);
 
    @override
    State<StatefulWidget> createState() {
        return _ContactState();
    }
}
 
class _ContactState extends State<Contact> {
 
    @override
    Widget build(BuildContext context) {
 
        return Scaffold(
            appBar: AppBar(
                title: Text('Contact Us'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Text('Contact Screen'),
                    ],
                )
            ),
        );
    }
}
    ไฟล์ settings.dart
import 'package:flutter/material.dart';
 
class Settings extends StatefulWidget {
    static const routeName = '/settings';

    const Settings({Key? key}) : super(key: key);
 
    @override
    State<StatefulWidget> createState() {
        return _SettingsState();
    }
}
 
class _SettingsState extends State<Settings> {
 
    @override
    Widget build(BuildContext context) {
 
        return Scaffold(
            appBar: AppBar(
                title: Text('Settings'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Text('Settings Screen'),
                    ],
                )
            ),
        );
    }
}
    ไฟล์ sidemenu.dart
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../screens/home.dart';
import '../screens/contact.dart';
import '../screens/profile.dart';
import '../screens/about.dart';
import '../screens/settings.dart';
 
class SideMenu extends StatelessWidget {

    const SideMenu({Key? key}) : super(key: key);
 
    @override
    Widget build(BuildContext context) {
        return Drawer(
            child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.max,
                children: <Widget>[
                    UserAccountsDrawerHeader(
                        accountName: Text('AccountName'),
                        accountEmail: Text('email@example.com'),
                        currentAccountPicture: CircleAvatar(
                            backgroundImage: NetworkImage(
                                "https://www.ninenik.com/images/ninenik_page_logo.jpg"
                            ),
                            backgroundColor: Colors.white,
                        ),
                    ),
                    ListTile(
                        leading: Icon(FontAwesomeIcons.home),
                        title: Text('Home'),
                        onTap: () {
                            Navigator.pushReplacementNamed(
                                context,
                                Home.routeName
                            );					
                        },
                    ),                    
                    ListTile(
                        leading: Icon(FontAwesomeIcons.info),
                        title: Text('About Us'),
                        onTap: () {
                            Navigator.pushNamed(
                                context,
                                About.routeName
                            );
                        },
                    ),
                    ListTile(
                        leading: Icon(FontAwesomeIcons.user),
                        title: Text('Profile'),
                        onTap: () {
                            Navigator.pushNamed(
                                context,
                                Profile.routeName
                            );
                        },
                    ),
                    ListTile(
                        leading: Icon(FontAwesomeIcons.addressCard),
                        title: Text('Contact Us'),
                        onTap: () {
                            Navigator.pushNamed(
                                context,
                                Contact.routeName
                            );
                        },
                    ),
                    Divider(),
                    Expanded(
                        child: Align(
                            alignment: Alignment.bottomLeft,
                            child: ListTile(
                                leading: Icon(FontAwesomeIcons.cog),
                                title: Text('Settings'),
                                onTap: () {
                                    Navigator.pushNamed(
                                        context,
                                        Settings.routeName
                                    );
                                },
                            ),
                        ),
                    ),
                ],
            ),
        );
    }
}
 
    ดูตัวอย่างผลัพธ์การใช้งาน Drawer 
 
 

 
 
    ตอนนี้เราได้แนวทางเบื้องต้นในการสร้าง Drawer ซึ่งเรากำหนดไว้เฉพาะในหน้าแรก ไม่จำเป็นที่เราต้องแทรก Drawer ไว้ทุกๆ หน้า
ทั้งนี้ก็เพราะอย่างที่เราทราบมาในตอนต้นแล้วว่า การใช้งาน Route จะเป็นรูปแบบของการซ้อนกันของ Navigator Stack เป็นชั้นๆ 
นั่นคือ ถ้าเราเปิดเมนู Drawer ขึ้นมา และเลือกเมนู ไปยังหน้าที่ต้องการ หน้าที่เราเลือก ก็จะแสดงอยู่เหนือเมนู Drawer และเมื่อเรา
กด "back" กลับมา เมนู Drawer ก็ยังแสดงอยู่ ให้เราสามารถเลือกไปยังหน้าอื่นๆ ได้ ดังนั้น จังไม่จำเป็นที่เราต้องกำหนด Drawer 
ในทุกๆ หน้า เช่นกันกับกรณีหน้า Home ใน Drawer เราก็ไม่จำเป็นต้องกำหนดเมนู ให้กลับมาหน้า Home ทั้งนี้ก็เพราะ เมื่อเราเปิด 
Drawer จากหน้า Home  เมนู Drawer ก็จะแสดงอยู่หน้า Home อยู่แล้ว หากต้องการกลับหน้า Home เราก็แค่ Slide เพื่อเลื่อนเมนู
ให้ปิดลงไป หน้า Home ก็จะแสดงขึ้นมาปกติ
 
    สำหรับรูปแบบโครงสร้าง App ใหม่ จะเป็นต้นแบบสำหรับเนื้อหาในตอนต่อๆ ไป ไม่ว่าจะเป็นการใช้งาน Form หรือ widget อื่นๆ 
ก็จะได้นำมาประยุกต์กับหน้า screen ใหม่ ทำให้เข้าใจโค้ดง่ายขึ้น
 
    หวังว่าเนื้อหาเล็กน้อยเกี่ยวกับการใช้งาน Drawer widget จะทำให้เห็นภาพรวมของ App มากขึ้น สำหรับตอนหน้า
เนื้อหา จะเป็นอะไร รอติดตาม


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ