Hướng dẫn và ví dụ Flutter GetX obs Obx
Trong Flutter GetX, Obx là một widget quan trọng được sử dụng để vẽ ra một vùng giao diện có khả năng tự động vẽ lại nếu một biến đơn của GetxController mà nó đang quan sát thay đổi.
Lưu ý : Thư viện Rx là trung tâm của khả năng phản ứng của GetX. Thư viện này cung cấp các đối tượng quan sát được, luồng phản ứng (reactive streams) và các phương thức tiện ích để xử lý dữ liệu phản ứng. Obx là một widget nằm trong thư viện này mà bài viết này sẽ đề cập tới.
Khác với Obx, GetBuilder được sử dụng để vẽ ra một vùng giao diện và chỉ tự động vẽ lại khi GetxController thay đổi và bạn chủ động gọi phương thức update() của GetxController.
2. Ví dụ với kiểu cơ bản
Khi bắt đầu học Flutter bạn đã làm quen với ví dụ "Counter" nổi tiếng. Bây giờ chúng ta sẽ trở lại với ví dụ này theo cách của Flutter GetX.
Trong Flutter GetX, GetMaterialApp được sử dụng để thay cho MaterialApp. Về cơ bản chúng có vai trò giống nhau và có một vài khác biệt nhỏ.
main_ex1.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'page_main_ex1.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const GetMaterialApp( // <---------------------
title: 'Flutter GetX Demo',
debugShowCheckedModeBanner: false,
home: MainPageEx1(),
);
}
}
Flutter GetX sử dụng Controller để quản lý trạng thái thay cho StatefulWidget. Hãy nhìn vào lớp CounterController dưới đây:
counter_controller.dart
import 'package:get/get.dart';
class CounterController extends GetxController {
var count = 0.obs; // <--- RxInt (extends Rx<int>)
increment() => count++;
}
| Trong ví dụ này thuộc tính "count" của CounterController là "thứ có thể quan sát được bởi Obx".
|
| Về cơ bản, bạn có thể biến bất kỳ đối tượng nào thành "thứ có thể quan sát được" bằng cách gọi thuộc tính mở rộng "obs" của nó.
|
GetX sử dụng StatelessWidget kết hợp với một GetxController để thay thế cho StatefulWidget.
main_page_ex1.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'counter_controller.dart';
import 'page2_ex1.dart';
class MainPageEx1 extends StatelessWidget {
const MainPageEx1({super.key});
@override
Widget build(BuildContext context) {
// Instantiate your class using Get.put()
// to make it available for all "child" routes there.
final CounterController controller = Get.put(CounterController());
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Flutter GetX Demo 1"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Obx(() => Text("Clicked: ${controller.count}")), // <---------
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Get.to(() => Page2Ex1());
},
child: const Text("Go to Page2"),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
controller.increment(); // <-----------
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Đoạn mã Get.put(CounterController()) được sử dụng để tạo ra một đối tượng CounterController và đặt nó vào "Objects Container" của trang. Nếu đã sẵn có đối tượng CounterController trong "Objects Container" thì đối tượng cũ sẽ bị thay thế. Các đối tượng này sẽ sẵn có để sử dụng cho Route hiện tại và tất cả các Route(s) con. Tại Route hiện tại và các Route(s) con, bạn có thể tìm thấy đối tượng này thông qua phương thức Get.find().
|
|
Các thay đổi của ".obs" sẽ được lắng nghe bởi "Obx". | "Obx" sẽ được vẽ lại một cách tự động khi giá trị thuộc tính ".obs" thay đổi. |
Các đối tượng được đặt trong "Objects Container" của trang sẽ sẵn có để sử dụng cho Route hiện tại và các Routes con.
page2_ex1.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'counter_controller.dart';
class Page2Ex1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
CounterController controller = Get.find();
return Scaffold(
body: Center(
child: Column(
children: [
Text("Clicked: ${controller.count}"),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Get.back();
},
child: const Text("Back"),
),
],
),
),
);
}
}
3. Ví dụ với kiểu Object
Trong ví dụ trên chúng ta đã biến một giá trị nguyên thuỷ (int, double, bool, num, String) thành thứ có thể quan sát được, sự thay đổi giá trị của nó được nhận biết bởi Obx. Mọi thứ có vẻ rất dễ dàng. Tuy nhiên, với một Object sẽ có một chút khác biệt và một vài điều cần lưu ý.
late Rx<Product> productRx;
@override
void onInit() {
super.onInit();
Product product = provider.findExampleProduct();
productRx = product.obs;
}
Trong ví dụ này chúng ta sẽ biến một đối tượng Product thành thứ có thể quan sát được bởi Obx.
_product_model.dart
class Product {
int id;
String name;
double price;
Product({
required this.id,
required this.name,
required this.price,
});
Product copy() {
return Product(id: id, name: name, price: price);
}
}
_product_provider.dart
import '_product_model.dart';
class ProductProvider {
// Query from Database and return a Product object.
Product findExampleProduct() {
return Product(
id: 1,
name: "Samsung Galaxy S24 Ultra",
price: 1799.99,
);
}
// Save to Database ...
// Query product and return new instance of Product...
Product updateProduct(Product product) {
Product copied = product.copy();
copied.price = copied.price + 1;
return copied;
}
}
ProductController là một lớp mở rộng từ GetxController. Thuộc tính productRx của nó là thứ có thể quan sát được bởi Obx.
_product_controller.dart
import 'package:get/get.dart';
import '_product_model.dart';
import '_product_provider.dart';
class ProductController extends GetxController {
ProductProvider provider = Get.put(ProductProvider());
late Rx<Product> productRx;
@override
void onInit() {
super.onInit();
Product product = provider.findExampleProduct();
productRx = product.obs;
}
void updateProduct() {
// Use way 1 or way 2 or way 3:
updateProduct_way1();
// updateProduct_way2();
// updateProduct_way3();
}
// Way 1
void updateProduct_way1() {
Product current = productRx.value;
current.price = current.price + 1;
// Call Rx<>.refresh()
productRx.refresh(); // IMPORTANT!!!
}
// Way 2
void updateProduct_way2() {
Product current = productRx.value;
// Update current Product and return new instance.
Product newInstance = provider.updateProduct(current);
// Replace value of "Rx" with the new instance.
productRx.value = newInstance; // IMPORTANT!!!
}
// Way 3
void updateProduct_way3() {
productRx.update(
(Product? currentProduct) {
// IMPORTANT!!!
// Update current product and return new instance.
Product newInstance = provider.updateProduct(currentProduct!);
return newInstance;
},
);
}
}
main_ex2.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '_product_controller.dart';
class MainPageEx2 extends StatelessWidget {
const MainPageEx2({super.key});
@override
Widget build(BuildContext context) {
final ProductController controller = Get.put(ProductController());
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Flutter GetX Obx Demo 2"),
),
body: Center(
child: Obx(
() => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Product Name: ${controller.productRx.value.name}"),
const SizedBox(height: 10),
Text("Product Price: ${controller.productRx.value.price}"),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
controller.updateProduct();
},
child: const Text("Update Product"),
),
],
),
),
),
);
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const GetMaterialApp(
title: 'Flutter GetX Demo 2',
debugShowCheckedModeBanner: false,
home: MainPageEx2(),
);
}
}
4. Ví dụ với kiểu List
Một trường hợp phổ biến trong Flutter GetX đó là biến một đối tượng List thành thứ có thể quan sát được bởi Obx:
late RxList<Employee> employeeRxList;
@override
void onInit() {
super.onInit();
List<Employee> employeeList = provider.queryEmployee();
employeeRxList = employeeList.obs;
}
Khi bạn cập nhập danh sách bạn cần gọi phương thức rxList.refresh() để chắc chắn rằng các thay đổi này được thông báo tới Obx.
void updateFirstEmployee() {
Employee first = employeeRxList[0];
first.salary = first.salary + 1;
employeeRxList.refresh(); // <--------
}
Chú ý: Trong một vài tình huống được liệt kê dưới đây sự thay đổi của List sẽ được thông báo tới Obx mà không cần gọi phương thức rxList.update().
Thay thế rxList.value bởi một đối tượng List mới. |
|
Thay thế một phần tử nào đó trong RxList bởi một phần tử khác. |
|
Thay đổi vị trí một phần tử nào đó của RxList. |
|
Số lượng các phần tử trong RxList thay đổi do gọi một trong các phương thức của RxList như:
|
|
Xem một ví dụ đầy đủ:
_employee_model.dart
class Employee {
int id;
String name;
int salary;
Employee({
required this.id,
required this.name,
required this.salary,
});
Employee copy() {
return Employee(id: id, name: name, salary: salary);
}
}
_employee_provider.dart
import '_employee_model.dart';
class EmployeeProvider {
// Query from Database and return a Product object.
List<Employee> queryEmployee() {
return [
Employee(id: 1, name: "Tom", salary: 1100),
Employee(id: 2, name: "Jerry", salary: 1000),
Employee(id: 3, name: "Donald", salary: 1200),
];
}
}
_employee_controller.dart
import 'package:get/get.dart';
import '_employee_model.dart';
import '_employee_provider.dart';
class EmployeeController extends GetxController {
final EmployeeProvider provider = Get.put(EmployeeProvider());
late RxList<Employee> employeeRxList;
@override
void onInit() {
super.onInit();
List<Employee> employeeList = provider.queryEmployee();
employeeRxList = employeeList.obs;
}
void updateFirstEmployee() {
Employee first = employeeRxList[0];
first.salary = first.salary + 1;
employeeRxList.refresh(); // <--------
}
}
main_ex3.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '_employee_controller.dart';
class MainPageEx3 extends StatelessWidget {
const MainPageEx3({super.key});
@override
Widget build(BuildContext context) {
final EmployeeController controller = Get.put(EmployeeController());
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Flutter GetX Obx Demo 3"),
),
body: Column(
children: [
SizedBox(
height: 200,
child: Obx(
() => ListView(
children: controller.employeeRxList
.map(
(e) => Card(
child: ListTile(
title: Text(e.name),
subtitle: Text("Salary: ${e.salary}"),
),
),
)
.toList(),
),
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
controller.updateFirstEmployee();
},
child: const Text("Update Salary for first Employee"),
),
],
),
);
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const GetMaterialApp(
title: 'Flutter GetX Demo 2',
debugShowCheckedModeBanner: false,
home: MainPageEx3(),
);
}
}
Các hướng dẫn lập trình Flutter
- Hướng dẫn và ví dụ Flutter Column
- Hướng dẫn và ví dụ Flutter Stack
- Hướng dẫn và ví dụ Flutter IndexedStack
- Hướng dẫn và ví dụ Flutter Spacer
- Hướng dẫn và ví dụ Flutter Expanded
- Hướng dẫn và ví dụ Flutter SizedBox
- Hướng dẫn và ví dụ Flutter Tween
- Cài đặt Flutter SDK trên Windows
- Cài đặt Flutter Plugin cho Android Studio
- Tạo ứng dụng Flutter đầu tiên của bạn - Hello Flutter
- Hướng dẫn và ví dụ Flutter Scaffold
- Hướng dẫn và ví dụ Flutter AppBar
- Hướng dẫn và ví dụ Flutter BottomAppBar
- Hướng dẫn và ví dụ Flutter SliverAppBar
- Hướng dẫn và ví dụ Flutter TextButton
- Hướng dẫn và ví dụ Flutter ElevatedButton
- Hướng dẫn và ví dụ Flutter ShapeBorder
- Hướng dẫn và ví dụ Flutter EdgeInsetsGeometry
- Hướng dẫn và ví dụ Flutter EdgeInsets
- Hướng dẫn và ví dụ Flutter CircularProgressIndicator
- Hướng dẫn và ví dụ Flutter LinearProgressIndicator
- Hướng dẫn và ví dụ Flutter Center
- Hướng dẫn và ví dụ Flutter Align
- Hướng dẫn và ví dụ Flutter Row
- Hướng dẫn và ví dụ Flutter SplashScreen
- Hướng dẫn và ví dụ Flutter Alignment
- Hướng dẫn và ví dụ Flutter Positioned
- Hướng dẫn và ví dụ Flutter ListTile
- Hướng dẫn và ví dụ Flutter SimpleDialog
- Hướng dẫn và ví dụ Flutter AlertDialog
- Navigation và Routing trong Flutter
- Hướng dẫn và ví dụ Flutter Navigator
- Hướng dẫn và ví dụ Flutter TabBar
- Hướng dẫn và ví dụ Flutter Banner
- Hướng dẫn và ví dụ Flutter BottomNavigationBar
- Hướng dẫn và ví dụ Flutter FancyBottomNavigation
- Hướng dẫn và ví dụ Flutter Card
- Hướng dẫn và ví dụ Flutter Border
- Hướng dẫn và ví dụ Flutter ContinuousRectangleBorder
- Hướng dẫn và ví dụ Flutter RoundedRectangleBorder
- Hướng dẫn và ví dụ Flutter CircleBorder
- Hướng dẫn và ví dụ Flutter StadiumBorder
- Hướng dẫn và ví dụ Flutter Container
- Hướng dẫn và ví dụ Flutter RotatedBox
- Hướng dẫn và ví dụ Flutter CircleAvatar
- Hướng dẫn và ví dụ Flutter TextField
- Hướng dẫn và ví dụ Flutter IconButton
- Hướng dẫn và ví dụ Flutter FlatButton
- Hướng dẫn và ví dụ Flutter SnackBar
- Hướng dẫn và ví dụ Flutter Drawer
- Ví dụ Flutter Navigator pushNamedAndRemoveUntil
- Hiển thị hình ảnh trên Internet trong Flutter
- Hiển thị ảnh Asset trong Flutter
- Flutter TextInputType các kiểu bàn phím
- Hướng dẫn và ví dụ Flutter NumberTextInputFormatter
- Hướng dẫn và ví dụ Flutter Builder
- Làm sao xác định chiều rộng của Widget cha trong Flutter
- Bài thực hành Flutter thiết kế giao diện màn hình đăng nhập
- Bài thực hành Flutter thiết kế giao diện trang (1)
- Khuôn mẫu thiết kế Flutter với các lớp trừu tượng
- Bài thực hành Flutter thiết kế trang Profile với Stack
- Bài thực hành Flutter thiết kế trang profile (2)
- Hướng dẫn và ví dụ Flutter ListView
- Hướng dẫn và ví dụ Flutter GridView
- Bài thực hành Flutter với gói http và dart:convert (2)
- Bài thực hành Flutter với gói http và dart:convert (1)
- Ứng dụng Flutter Responsive với Menu Drawer
- Flutter GridView với SliverGridDelegate tuỳ biến
- Hướng dẫn và ví dụ Flutter image_picker
- Flutter upload ảnh sử dụng http và ImagePicker
- Hướng dẫn và ví dụ Flutter SharedPreferences
- Chỉ định cổng cố định cho Flutter Web trên Android Studio
- Tạo Module trong Flutter
- Hướng dẫn và ví dụ Flutter SkeletonLoader
- Hướng dẫn và ví dụ Flutter Slider
- Hướng dẫn và ví dụ Flutter Radio
- Bài thực hành Flutter SharedPreferences
- Hướng dẫn và ví dụ Flutter InkWell
- Hướng dẫn và ví dụ Flutter GetX GetBuilder
- Hướng dẫn và ví dụ Flutter GetX obs Obx
- Hướng dẫn và ví dụ Flutter flutter_form_builder
- Xử lý lỗi 404 trong Flutter GetX
- Ví dụ đăng nhập và đăng xuất với Flutter Getx
- Hướng dẫn và ví dụ Flutter multi_dropdown
Show More