
Chào các bạn, Creyt đây! Hôm nay chúng ta sẽ mổ xẻ một khái niệm mà nhiều bạn hay nhầm lẫn hoặc chưa khai thác hết sức mạnh của nó: cái gọi là 'DrawerController' trong Flutter. Nghe tên thì hoành tráng, nhưng thực chất, nó là cách chúng ta nắm quyền điều khiển cái 'ngăn kéo bí mật' của ứng dụng – cái Drawer thần thánh đó.
Tưởng tượng ứng dụng của bạn là một cái bàn làm việc. Cái Drawer chính là hộc tủ kéo ra kéo vào, chứa đủ thứ đồ nghề, các tùy chọn điều hướng quan trọng. Bình thường, bạn chỉ cần gạt tay (vuốt từ mép màn hình) là nó tự mở. Nhưng đôi khi, bạn muốn có một cái 'điều khiển từ xa', bấm nút là hộc tủ tự động mở ra, hoặc tự động đóng lại, thay vì phải tự tay kéo. Đó chính là lúc 'DrawerController' (mà thực chất là cơ chế điều khiển Drawer thông qua ScaffoldState) phát huy tác dụng.
Nói cách khác, nó giúp bạn mở/đóng Drawer bằng mã lệnh, không chỉ dựa vào thao tác vuốt của người dùng.
Điều Khiển Ngăn Kéo: Chìa Khóa Nằm Ở Đâu?
Trong Flutter, Drawer là một widget được đặt trong Scaffold. Scaffold chính là khung sườn chính của ứng dụng bạn, nó quản lý AppBar, BottomNavigationBar, và cả cái Drawer này nữa. Để điều khiển được Drawer, chúng ta cần 'nói chuyện' trực tiếp với Scaffold đang chứa nó. Và cách để làm điều đó chính là thông qua ScaffoldState.
ScaffoldState là một đối tượng chứa trạng thái hiện tại của Scaffold, và nó cung cấp các phương thức như openDrawer() hay closeDrawer(). Vấn đề là làm sao để có được ScaffoldState này từ bất kỳ đâu trong cây widget của bạn?
Có hai cách chính, và cách phổ biến nhất, 'chuẩn chỉ Harvard' nhất, là dùng GlobalKey<ScaffoldState>. Đây giống như việc bạn dán một cái 'mã số định danh' duy nhất lên cái Scaffold của mình, sau đó từ bất cứ đâu, bạn chỉ cần gọi cái mã số đó là có thể 'gọi điện' cho Scaffold và ra lệnh cho nó.
Cách thứ hai là dùng Scaffold.of(context). Cách này tiện hơn nếu bạn đang ở sâu bên trong cây widget và biết chắc chắn có một Scaffold ở phía trên. Tuy nhiên, nó yêu cầu context phải là con cháu của Scaffold đó, nếu không sẽ báo lỗi.
Hôm nay, chúng ta sẽ tập trung vào GlobalKey vì nó linh hoạt hơn và cho phép bạn điều khiển Drawer từ bất kỳ đâu, kể cả từ một widget không phải là con trực tiếp của Scaffold.

Code Ví Dụ Minh Họa: Nắm Quyền Điều Khiển
Để minh họa, chúng ta sẽ tạo một ứng dụng Flutter đơn giản với một Drawer và các nút để mở/đóng nó bằng mã lệnh.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'DrawerController Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Bước 1: Khai báo một GlobalKey cho ScaffoldState
// Đây là "mã số định danh" duy nhất của Scaffold của chúng ta
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
return Scaffold(
// Bước 2: Gán GlobalKey này vào Scaffold
// Giờ thì Scaffold này đã có một "điều khiển từ xa"
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Điều khiển Ngăn kéo (Drawer)'),
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
// Bước 3: Sử dụng GlobalKey để truy cập ScaffoldState
// và gọi phương thức openDrawer()
// Giống như bấm nút "mở hộc tủ" trên điều khiển từ xa
if (_scaffoldKey.currentState != null && !_scaffoldKey.currentState!.isDrawerOpen) {
_scaffoldKey.currentState!.openDrawer();
} else if (_scaffoldKey.currentState != null && _scaffoldKey.currentState!.isDrawerOpen) {
_scaffoldKey.currentState!.closeDrawer();
}
},
),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'Menu Chính',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
leading: const Icon(Icons.home),
title: const Text('Trang Chủ'),
onTap: () {
// Đóng Drawer sau khi chọn
_scaffoldKey.currentState?.closeDrawer();
// Xử lý hành động Trang Chủ
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Cài Đặt'),
onTap: () {
// Đóng Drawer sau khi chọn
_scaffoldKey.currentState?.closeDrawer();
// Xử lý hành động Cài Đặt
},
),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Bấm nút Menu trên AppBar hoặc nút dưới đây để điều khiển Drawer.',
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// Bạn cũng có thể mở/đóng Drawer từ body
// Kiểm tra trạng thái hiện tại để quyết định mở hay đóng
if (_scaffoldKey.currentState != null) {
if (_scaffoldKey.currentState!.isDrawerOpen) {
_scaffoldKey.currentState!.closeDrawer();
} else {
_scaffoldKey.currentState!.openDrawer();
}
}
},
child: const Text('Mở/Đóng Drawer'),
),
],
),
),
);
}
}
Mẹo (Best Practices) Để Ghi Nhớ và Ứng Dụng Thực Tế
- Khi nào dùng? Khi bạn muốn mở Drawer từ một nút bấm không phải nút mặc định trên AppBar, hoặc muốn tự động đóng Drawer sau một hành động nào đó (ví dụ, sau khi chọn một mục trong menu). Hoặc thậm chí tự động mở Drawer khi người dùng lần đầu vào ứng dụng để hướng dẫn.
- Tránh lạm dụng
GlobalKey:GlobalKeymạnh mẽ nhưng cũng có thể gây khó hiểu nếu dùng quá nhiều. Hãy dùng nó khi thực sự cần truy cập vào trạng thái của một widget từ xa, không phải là con trực tiếp của nó. - Kiểm tra
currentState: Luôn luôn kiểm tra_scaffoldKey.currentState != nulltrước khi gọi các phương thức nhưopenDrawer()hoặccloseDrawer(). Đôi khi, widget chưa được gắn vào cây widget hoặc đã bị hủy, việc truy cậpcurrentStatetrực tiếp có thể gây lỗi. - Dùng
Scaffold.of(context)khi có thể: Nếu bạn đang ở trong một widget là con củaScaffoldvà chỉ cần truy cậpScaffoldStatetừ đó,Scaffold.of(context)sẽ gọn gàng và dễ đọc hơnGlobalKey. Ví dụ, trongonTapcủa mộtListTiletrongDrawer, bạn có thể dùngNavigator.pop(context)(thực chất là đóng Drawer) hoặcScaffold.of(context).closeDrawer().
Ứng Dụng Thực Tế Các Website/Ứng Dụng Đã Ứng Dụng
Hầu hết các ứng dụng có Drawer đều sử dụng cơ chế này để điều khiển nó. Ví dụ điển hình:
- Gmail: Khi bạn bấm vào biểu tượng menu ba gạch ở góc trên bên trái, Drawer sẽ mở ra. Đây chính là
openDrawer()được gọi từIconButtontrênAppBar. - Facebook/LinkedIn: Các ứng dụng này thường có Drawer hoặc một dạng navigation panel tương tự, cho phép bạn truy cập các phần khác nhau của ứng dụng. Việc đóng Drawer sau khi chọn một mục là một ví dụ của
closeDrawer(). - Các ứng dụng quản lý tác vụ (Todoist, Trello): Thường có một menu bên cạnh để chuyển đổi giữa các dự án hoặc danh sách, và cơ chế điều khiển Drawer giúp quản lý trạng thái hiển thị của menu đó.
Thuộc Series: Flutter
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!