
Chào các homies Gen Z mê code! Hôm nay, anh Creyt sẽ dẫn mấy đứa đi khám phá một cái “đồng hồ báo thức” cực xịn trong Flutter, đó là TimePickerDialog. Nghe tên thì hơi học thuật nhưng thực ra nó là ông hoàng của việc chọn giờ trong app, giúp app mình trông chuyên nghiệp và dễ dùng hơn rất nhiều.
TimePickerDialog là gì mà "chill" thế?
Thực ra, TimePickerDialog nó như một cái bảng điều khiển thời gian mini, bật lên cái là cho người dùng chọn giờ và phút một cách trực quan, nhanh gọn lẹ. Thay vì phải gõ tay từng số, từng chữ số 0, hay loay hoay với format 12h/24h, thì anh bạn này sẽ show ra một giao diện đẹp đẽ, chuẩn Material Design để người dùng chỉ việc "chạm và chọn".
Để làm gì ư? Đơn giản là để app của mấy đứa có thể hỏi người dùng "Mấy giờ bạn muốn đặt lịch?", "Mấy giờ bạn muốn hẹn giờ báo thức?", hay "Mấy giờ ship đồ ăn đến nhà?". Nó là mảnh ghép không thể thiếu cho các ứng dụng có yếu tố thời gian, giúp trải nghiệm người dùng mượt mà như lướt TikTok vậy.
Code Ví Dụ: Gọi "Thần Đèn" TimePickerDialog ra sao?
Để triệu hồi TimePickerDialog, chúng ta sẽ dùng hàm showTimePicker. Nó là một Future, nên kết quả trả về sẽ là một TimeOfDay? (có thể là null nếu người dùng hủy bỏ).
Cứ hình dung thế này: mấy đứa bấm nút "Chọn Giờ", Flutter sẽ hỏi "Mấy giờ?". Người dùng chọn xong, Flutter sẽ trả lại cái giờ đó cho mình xử lý. Easy peasy!
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: 'TimePicker Demo của Creyt',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const TimePickerScreen(),
);
}
}
class TimePickerScreen extends StatefulWidget {
const TimePickerScreen({super.key});
@override
State<TimePickerScreen> createState() => _TimePickerScreenState();
}
class _TimePickerScreenState extends State<TimePickerScreen> {
TimeOfDay? _selectedTime; // Biến để lưu giờ đã chọn
// Hàm bất đồng bộ để hiển thị TimePickerDialog
Future<void> _selectTime(BuildContext context) async {
// Gọi showTimePicker và chờ kết quả
final TimeOfDay? pickedTime = await showTimePicker(
context: context, // Context cần thiết để hiển thị dialog
initialTime: _selectedTime ?? TimeOfDay.now(), // Giờ khởi tạo (nếu chưa chọn thì lấy giờ hiện tại)
builder: (BuildContext context, Widget? child) {
// Đây là chỗ để tùy chỉnh theme cho dialog, cho nó 'tone-sur-tone' với app mình
return Theme(
data: ThemeData.light().copyWith(
primaryColor: Colors.teal, // Màu chủ đạo của dialog (phần header)
colorScheme: const ColorScheme.light(primary: Colors.teal, onPrimary: Colors.white), // Màu sắc cho các thành phần chính
buttonTheme: const ButtonThemeData(textTheme: ButtonTextTheme.primary), // Màu chữ nút
),
child: child!, // Đừng quên trả về child!
);
},
);
// Kiểm tra xem người dùng có chọn giờ không (không phải null) và có khác giờ cũ không
if (pickedTime != null && pickedTime != _selectedTime) {
setState(() {
_selectedTime = pickedTime; // Cập nhật lại giờ đã chọn và render lại UI
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Chọn Giờ Cùng Creyt'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
_selectedTime == null
? 'Chưa chọn giờ nào cả, bấm nút đi bro!'
: 'Giờ bạn chọn là: ${_selectedTime!.format(context)}',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
ElevatedButton.icon(
onPressed: () => _selectTime(context), // Gọi hàm chọn giờ khi nhấn nút
icon: const Icon(Icons.access_time),
label: const Text('Chọn Giờ Ngay!', style: TextStyle(fontSize: 18)),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
),
],
),
),
);
}
}
Giải thích nhanh:
_selectedTime: BiếnTimeOfDay?để lưu trữ giờ mà người dùng chọn. Dấu?có nghĩa là nó có thểnull(chưa chọn hoặc người dùng hủy)._selectTime(BuildContext context): Hàmasyncnày sẽ gọishowTimePicker.initialTime: Cái này quan trọng nè. Nó là giờ mặc định khi dialog hiện ra. Nếu_selectedTimeđã có giá trị thì dùng nó, không thì lấyTimeOfDay.now()(giờ hiện tại).builder: Đây là "phù thủy" giúp mấy đứa tùy chỉnh theme cho cái dialog, cho nó khớp với màu sắc của app mình. Đừng để nó lạc quẻ nha!setState: Sau khi người dùng chọn giờ vàpickedTimekhông null, chúng ta dùngsetStateđể cập nhật biến_selectedTimevà làm mới giao diện.

Mẹo của Creyt: Dùng sao cho "đỉnh của chóp"?
- Luôn có
initialTimehợp lý: Đừng để người dùng phải cuộn mãi mới đến giờ hiện tại. Hãy setinitialTimelà giờ hiện tại hoặc giờ đã được chọn trước đó. - Kiểm tra
nullcẩn thận: Kết quả từshowTimePickercó thể lànullnếu người dùng nhấn nút "Cancel" hoặc click ra ngoài. Luôn kiểm traif (pickedTime != null)trước khi xử lý. - Tùy chỉnh
Themequabuilder: Như trong ví dụ, dùngbuilderđể đảm bảoTimePickerDialogcó màu sắc, font chữ đồng bộ với app. Đừng để nó trông như "con ghẻ" nha! - Localization auto-magic: Hàm
_selectedTime!.format(context)rất hay ở chỗ nó sẽ tự động định dạng giờ theo ngôn ngữ và cài đặt của thiết bị (ví dụ: 12h AM/PM ở Mỹ, 24h ở Việt Nam). Khỏi lo vụ đa ngôn ngữ! - Tối ưu UX: Đặt nút gọi
TimePickerDialogở vị trí dễ nhìn, dễ chạm. Đừng bắt người dùng phải tìm kiếm như chơi trốn tìm.
Ứng dụng thực tế: "TimePickerDialog" đi đâu cũng gặp!
Nhìn quanh đi, mấy đứa sẽ thấy TimePickerDialog (hoặc các phiên bản tương tự) xuất hiện khắp nơi:
- Google Calendar / Lịch của Apple: Khi tạo một sự kiện mới, mấy đứa chọn giờ bắt đầu/kết thúc.
- Ứng dụng đặt báo thức: Như cái app Đồng Hồ của điện thoại đó, chọn giờ báo thức là y chang.
- Các app giao đồ ăn / đặt xe: Chọn giờ giao hàng, giờ xe đến đón.
- Ứng dụng quản lý công việc / nhắc nhở: Set deadline, set thời gian cho một task cụ thể.
Nói chung, cứ cái gì liên quan đến việc "chọn một mốc thời gian" là y như rằng có mặt anh bạn này.
Khi nào nên dùng và khi nào nên "né"?
Nên dùng khi:
- Mấy đứa cần người dùng chọn một mốc thời gian cụ thể (giờ và phút) mà không cần ngày tháng. Nó sinh ra là để làm việc này mà.
- Mấy đứa muốn giao diện chọn giờ chuẩn Material Design, nhất quán và đã được tối ưu về UX.
- Mấy đứa muốn tiết kiệm thời gian, không muốn tự code lại một cái picker phức tạp.
Nên "né" khi:
- Cần chọn cả ngày và giờ: Lúc này, mấy đứa sẽ cần kết hợp
showDatePickervớishowTimePicker, hoặc dùng một thư viện bên thứ ba nhưflutter_datetime_pickerđể có một dialog chọn cả hai trong một. - Cần chọn khoảng thời gian (duration): Ví dụ như "30 phút" hay "1 giờ 15 phút".
TimePickerDialogchỉ chọn mốc thời gian, không phải độ dài thời gian. - Yêu cầu giao diện quá "dị": Nếu app của mấy đứa có một thiết kế chọn giờ cực kỳ độc đáo, không theo chuẩn Material Design, thì có thể phải tự vẽ (custom widget) hoặc tìm thư viện khác. Nhưng anh Creyt khuyên là hạn chế, vì nó tốn công và dễ phát sinh lỗi.
Kinh nghiệm của anh Creyt: Trong 90% trường hợp, TimePickerDialog của Flutter là đủ và là lựa chọn tốt nhất. Đừng cố gắng "phát minh lại bánh xe" trừ khi có lý do cực kỳ chính đáng. Nó đã được Flutter team tối ưu rất kỹ rồi, cứ thế mà dùng thôi!
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é!