Điều Hướng, Định Tuyến và Bộ Điều Khiển trong Flutter
- 10-06-2024
- Toanngo92
- 0 Comments
Mục lục
Điều Hướng và Định Tuyến trong Flutter
Trong Flutter, điều hướng là một khái niệm về việc di chuyển tới và từ một Trang/Màn hình. Định tuyến là một cơ chế để chỉ định đường dẫn cho điều hướng. Định tuyến trong Flutter giúp thực hiện điều hướng từ một trang sang trang khác.
Xem xét một ví dụ thực tế đơn giản để hiểu về điều hướng. Khi một khách hàng duyệt qua ứng dụng/web mua sắm bán lẻ và nhấp vào một sản phẩm, anh/chị ấy sẽ được chuyển hướng đến một màn hình/trang mới hiển thị chi tiết sản phẩm. Khi nhấp vào nút hoặc liên kết trở lại, khách hàng có thể quay lại trang trước đó. Đây là điều hướng trong hành động, nơi khách hàng ‘điều hướng’ từ một trang sang trang khác.
Đường dẫn trong Flutter
Mỗi trang trong Flutter đều có một đường dẫn. Trong Navigator, có thể đề cập đến bất kỳ đường dẫn nào theo yêu cầu của người dùng.
Đoạn mã bên dưới thể hiện các đường dẫn mẫu được sử dụng trong một ứng dụng.
routes: {
"/": (context) => const MyHomePage(title: 'Flutter Demo'),
'/second': (context) => const MyHomePage(title: 'Flutter Demo'),
},
Lớp Định Tuyến trong Flutter
Route là một lớp trừu tượng được sử dụng để quản lý việc nhập vào lớp Navigator. Lớp Navigator trong Flutter là một widget quản lý một tập hợp các widget con với nguyên tắc xếp chồng. Lớp Navigator chịu trách nhiệm quản lý các đường dẫn giữa các phần tử này. Một lớp trừu tượng là một lớp có thể có các phương thức trừu tượng không có thân hàm.
MaterialPageRoute
MaterialPageRoute là đường dẫn modal thay thế toàn bộ màn hình với chuyển đổi thích ứng với nền tảng. Trong Android, việc điều hướng đến màn hình tiếp theo diễn ra bằng một hiệu ứng phai mờ trong khi trong iOS, điều hướng diễn ra với một hiệu ứng trượt. Nói một cách đơn giản, MaterialPageRoute chịu trách nhiệm về sự chuyển tiếp giữa các trang trong ứng dụng.
a. Navigator.push(): Phương thức này được sử dụng để đẩy một trang mới vào ngăn xếp.
b. Navigator.pop(): Phương thức này được sử dụng để đóng trang hiện tại từ ngăn xếp.
Triển khai điều hướng trong Ứng dụng Flutter
Điều hướng trong Flutter được sử dụng để điều hướng từ một trang sang trang khác.
Hình bên dưới cho thấy làm thế nào Routes giúp với Điều hướng. Lưu ý rằng Routes được gán cho MaterialApp. Từ MaterialApp, người dùng có thể điều hướng đến các trang cụ thể bằng cách sử dụng các Routes.
Hãy xem đoạn mã bên dưới để hiểu rõ hơn về cách routes hoạt động trong Flutter.
Trong mã này, các routes được xác định cho MaterialApp
và các routes này giúp điều hướng từ trang một sang trang hai.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
"/": (context) => const MyHomePage(title: 'Flutter Demo'),
'/second': (context) => const MyHomePage2(title: 'Flutter Demo'),
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Row Demo'),
const Text(
'page 1',
style: TextStyle(fontSize: 20),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/second');
},
child: Container(
alignment: Alignment.center,
child: const Text("Go to page two with named"),
),
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const MyHomePage2(title: 'Flutter Demo'),
),
);
},
child: Container(
alignment: Alignment.center,
child: const Text('Go to page two without named'),
),
),
],
),
),
);
}
}
class MyHomePage2 extends StatefulWidget {
const MyHomePage2({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage2> createState() => _MyHomePageState2();
}
class _MyHomePageState2 extends State<MyHomePage2> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Row Demo'),
const Text(
'page 2',
style: TextStyle(fontSize: 20),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Container(
alignment: Alignment.center,
child: const Text('Go to page one'),
),
),
],
),
),
);
}
}
2 hình bên dưới hiển thị đầu ra khi ứng dụng được thực thi. Có hai nút tồn tại trên màn hình chính (xem hình đầu tiên). Khi bất kỳ nút nào trong số này được nhấp vào, chúng sẽ đưa người dùng đến trang thứ hai (xem hình tiếp theo). Một nút khác tồn tại trên trang thứ hai để cho phép người dùng điều hướng trở lại màn hình trước đó. Hành động nhảy từ màn hình này sang màn hình khác và quay lại có thể thực hiện được nhờ khái niệm Điều hướng và Đường dẫn. Bộ điều khiển cho trang đầu tiên hoặc trang thứ hai sẽ được tải khi người dùng nhấp vào các nút thích hợp, và bộ định tuyến sẽ hỗ trợ trong việc điều hướng từ trang này sang trang khác.
Các Bộ Điều Khiển (Controllers) trong Flutter
Controllers trong Flutter được sử dụng để kiểm soát các widget. Hành động kiểm soát này bị giới hạn trong các thuộc tính của widget mà controller được áp dụng. Với controllers, yêu cầu sử dụng các khóa toàn cục để truy cập trạng thái của widget được tránh. Controllers có thể được xem như các đối tượng có thể gắn vào một số widget nhất định. Những đối tượng này có thể được sử dụng để hướng dẫn các hành động của widget đó.
Làm việc với Controllers trong Flutter
Controller
có thể được giải thích tốt nhất qua một vài ví dụ.
TextEditingController
TextEditingController
được sử dụng để kiểm soát TextField
hoặc TextFormField
. Nếu TextEditingController
được áp dụng lên một widget TextField
, có thể lấy được văn bản nhập vào từ widget bằng cách sử dụng controller.
ScrollController
ScrollController
được sử dụng để kiểm soát chế độ cuộn (scroll view). Nếu được áp dụng cho một widget ScrollView
, thanh cuộn, loại cuộn và tất cả các thuộc tính khác của widget đó có thể được kiểm soát bằng cách sử dụng ScrollController
.
Đoạn mã bên dưới dưới đây hiển thị lớp MyHomePage và trình bày cách controllers hoạt động trong Flutter.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
TextEditingController tv = TextEditingController();
ScrollController sc = ScrollController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Controller Demo',
),
TextFormField(
controller: tv,
),
SizedBox(
height: 100,
child: SingleChildScrollView(
controller: sc,
child: Column(
children: const [
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
Text(
"Demo",
style: TextStyle(fontSize: 20),
),
],
),
),
),
ElevatedButton(
onPressed: () {
sc.jumpTo(5);
print(tv.text);
},
child: Container(
alignment: Alignment.center,
child: const Text('Go to page two without named'),
),
),
],
),
),
);
}
}
Ở đây, cả TextEditingController
và ScrollController
đều được sử dụng. Khi một nút được nhấp vào, giá trị tương ứng được in ra trong TextFormField
đồng thời, ScrollView
sẽ cuộn xuống chỉ mục thứ năm. Tham khảo 2 hình ảnh bên dưới.
Tạo Bộ Điều Khiển Tùy Chỉnh
Bộ điều khiển tùy chỉnh (Custom controllers) được sử dụng trong Flutter để kiểm soát các widget tùy chỉnh cũng như có thể kết hợp với các bộ điều khiển khác được định nghĩa trong Flutter.
Để tạo các bộ điều khiển tùy chỉnh, người dùng có thể sử dụng lớp ChangeNotifier
. ChangeNotifier
là một lớp có thể được sử dụng để thông báo cho các lớp con về bất kỳ thay đổi nào xảy ra trong lớp cha bằng cách sử dụng phương thức notifyListeners
.
Triển khai Bộ Điều Khiển Tùy Chỉnh sử dụng ChangeNotifier
Để tạo một bộ điều khiển tùy chỉnh, các nhà phát triển phải triển khai ChangeNotifier
vào lớp controller, và khi thay đổi bất kỳ dữ liệu nào như thay đổi màu sắc, hình dạng và kích thước, các nhà phát triển có thể thông báo cho người dùng bằng cách sử dụng notifyListeners
.
Đoạn mã dưới đây hiển thị một lớp CustomController
mẫu, chứa các thuộc tính như buttonShape
, buttonColor
, và buttonHeight
cùng các phương thức để thay đổi chúng.
class CustomController extends ChangeNotifier {
Color buttonColor = Colors.blue;
String buttonShape = 'Rectangle';
double buttonHeight = 10;
void changeColor(Color color) {
buttonColor = color;
notifyListeners();
}
void changeShape(String shape) {
buttonShape = shape;
notifyListeners();
}
void changeHeight(double height) {
buttonHeight = height;
notifyListeners();
}
}
Sử dụng CustomController trong Ứng dụng Flutter
Khi đã tạo xong CustomController
, chúng ta có thể sử dụng nó trong các widget để kiểm soát các thuộc tính của chúng.
Dưới đây là ví dụ về cách sử dụng CustomController
trong một widget để thay đổi màu sắc và hình dạng của một nút.
class CustomButton extends StatefulWidget {
const CustomButton({Key? key}) : super(key: key);
@override
State<CustomButton> createState() => _CustomButtonState();
}
class _CustomButtonState extends State<CustomButton> {
final CustomController _controller = CustomController();
@override
void initState() {
super.initState();
_controller.addListener(() {
setState(() {});
});
}
@override
void dispose() {
_controller.removeListener(() {
setState(() {});
});
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () {
_controller.changeColor(Colors.red);
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(_controller.buttonColor),
),
child: Text(_controller.buttonShape),
),
ElevatedButton(
onPressed: () {
_controller.changeShape('Circle');
},
child: Text('Change Shape'),
),
ElevatedButton(
onPressed: () {
_controller.changeHeight(20);
},
child: Text('Change Height'),
),
],
);
}
}
Trong ví dụ trên, chúng ta sử dụng CustomController
để thay đổi màu sắc và hình dạng của nút khi các nút khác được nhấn. Khi một thuộc tính thay đổi, notifyListeners
sẽ được gọi và widget sẽ được cập nhật tương ứng.