Skip to main content

3 posts tagged with "StatefulWidget"

View All Tags

Flutter: State trong StatefulWidget

· 5 min read

State là một khái niệm quan trọng trong Flutter, đặc biệt khi làm việc với StatefulWidget. Bài viết này sẽ giúp bạn hiểu rõ về cách quản lý state và các phương pháp hiệu quả.

StatefulWidget & State

1. StatefulWidget và State

1.1. Cấu trúc cơ bản

class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $_counter'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}

1.2. Vòng đời của State

class _MyWidgetState extends State<MyWidget> {
@override
void initState() {
super.initState();
// Khởi tạo state
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
// Xử lý khi dependencies thay đổi
}

@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// Xử lý khi widget được cập nhật
}

@override
void dispose() {
// Dọn dẹp tài nguyên
super.dispose();
}
}

2. Quản lý State

2.1. Local State

class _FormState extends State<FormWidget> {
final _formKey = GlobalKey<FormState>();
String _name = '';
String _email = '';

void _submitForm() {
if (_formKey.currentState!.validate()) {
setState(() {
// Cập nhật state
});
}
}

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
onChanged: (value) {
setState(() {
_name = value;
});
},
),
TextFormField(
onChanged: (value) {
setState(() {
_email = value;
});
},
),
],
),
);
}
}

2.2. State với Animation

class _AnimatedWidgetState extends State<AnimatedWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: YourWidget(),
);
}
}

3. Best Practices

3.1. Tối ưu hóa setState

class _OptimizedWidgetState extends State<OptimizedWidget> {
int _counter = 0;
bool _isLoading = false;

void _updateCounter() {
// Chỉ cập nhật các widget cần thiết
setState(() {
_counter++;
});
}

void _loadData() async {
setState(() {
_isLoading = true;
});

// Thực hiện tác vụ bất đồng bộ
await Future.delayed(Duration(seconds: 1));

setState(() {
_isLoading = false;
});
}
}

3.2. Xử lý State phức tạp

class _ComplexWidgetState extends State<ComplexWidget> {
// Sử dụng các biến riêng biệt cho từng loại state
int _counter = 0;
String _selectedItem = '';
List<String> _items = [];
bool _isLoading = false;

// Tách logic xử lý state
void _handleCounterChange() {
setState(() {
_counter++;
});
}

void _handleItemSelection(String item) {
setState(() {
_selectedItem = item;
});
}

Future<void> _loadItems() async {
setState(() {
_isLoading = true;
});

try {
final items = await fetchItems();
setState(() {
_items = items;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
// Xử lý lỗi
}
}
}

4. Ví dụ thực tế

4.1. Form với Validation

class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
String _email = '';
String _password = '';
bool _isLoading = false;

Future<void> _submitForm() async {
if (_formKey.currentState!.validate()) {
setState(() {
_isLoading = true;
});

try {
await login(_email, _password);
// Xử lý đăng nhập thành công
} catch (e) {
// Xử lý lỗi
} finally {
setState(() {
_isLoading = false;
});
}
}
}

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
onChanged: (value) => _email = value,
validator: (value) {
if (value?.isEmpty ?? true) {
return 'Please enter your email';
}
return null;
},
),
TextFormField(
onChanged: (value) => _password = value,
obscureText: true,
validator: (value) {
if (value?.isEmpty ?? true) {
return 'Please enter your password';
}
return null;
},
),
ElevatedButton(
onPressed: _isLoading ? null : _submitForm,
child: _isLoading
? CircularProgressIndicator()
: Text('Login'),
),
],
),
);
}
}

4.2. List với CRUD Operations

class _TodoListState extends State<TodoList> {
List<Todo> _todos = [];
bool _isLoading = false;

Future<void> _loadTodos() async {
setState(() {
_isLoading = true;
});

try {
final todos = await fetchTodos();
setState(() {
_todos = todos;
});
} finally {
setState(() {
_isLoading = false;
});
}
}

Future<void> _addTodo(Todo todo) async {
setState(() {
_todos.add(todo);
});
await saveTodo(todo);
}

Future<void> _updateTodo(Todo todo) async {
setState(() {
final index = _todos.indexWhere((t) => t.id == todo.id);
if (index != -1) {
_todos[index] = todo;
}
});
await updateTodo(todo);
}

Future<void> _deleteTodo(String id) async {
setState(() {
_todos.removeWhere((todo) => todo.id == id);
});
await deleteTodo(id);
}
}

Kết luận

Quản lý state trong StatefulWidget là một kỹ năng quan trọng trong phát triển ứng dụng Flutter. Việc hiểu rõ và áp dụng đúng cách sẽ giúp bạn tạo ra ứng dụng có hiệu suất tốt và dễ bảo trì.


Tài liệu tham khảo:

Stateless và Stateful Widgets trong Flutter

· 4 min read

Stateless vs Stateful Widgets

Trong Flutter, có hai loại widget cơ bản: StatelessWidget và StatefulWidget. Việc hiểu rõ sự khác biệt giữa hai loại widget này là rất quan trọng để xây dựng ứng dụng Flutter hiệu quả.

1. StatelessWidget

StatelessWidget là widget không có state (trạng thái). Nó là immutable (không thể thay đổi) sau khi được tạo.

Đặc điểm của StatelessWidget:

  • Không có state
  • Không thể thay đổi sau khi được tạo
  • Phù hợp cho UI tĩnh
  • Hiệu năng tốt hơn vì không cần rebuild

Ví dụ về StatelessWidget:

class GreetingWidget extends StatelessWidget {
final String name;

const GreetingWidget({
super.key,
required this.name
});

@override
Widget build(BuildContext context) {
return Text('Hello, $name!');
}
}

Khi nào sử dụng StatelessWidget:

  • Hiển thị thông tin tĩnh
  • Widget chỉ phụ thuộc vào các tham số đầu vào
  • Không cần thay đổi UI theo thời gian
  • Không cần lưu trữ dữ liệu

2. StatefulWidget

StatefulWidget là widget có state (trạng thái). Nó có thể thay đổi trong quá trình sử dụng.

Đặc điểm của StatefulWidget:

  • Có state
  • Có thể thay đổi sau khi được tạo
  • Phù hợp cho UI động
  • Cần rebuild khi state thay đổi

Ví dụ về StatefulWidget:

class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});

@override
State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;

void _increment() {
setState(() {
_count++;
});
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}

Khi nào sử dụng StatefulWidget:

  • UI cần thay đổi theo thời gian
  • Cần lưu trữ dữ liệu
  • Có tương tác người dùng
  • Cần thực hiện các tác vụ bất đồng bộ

3. So sánh StatelessWidget và StatefulWidget

Đặc điểmStatelessWidgetStatefulWidget
StateKhông có
ImmutableKhông
Hiệu năngTốt hơnKém hơn
Sử dụngUI tĩnhUI động
CodeĐơn giảnPhức tạp hơn

4. Best Practices

4.1. Sử dụng StatelessWidget khi có thể

  • Ưu tiên sử dụng StatelessWidget nếu không cần state
  • Tách các phần UI tĩnh thành StatelessWidget riêng

4.2. Quản lý State hiệu quả

  • Chỉ lưu trữ state cần thiết
  • Sử dụng setState một cách hợp lý
  • Tránh rebuild không cần thiết

4.3. Tổ chức code

  • Tách biệt logic và UI
  • Sử dụng các widget có thể tái sử dụng
  • Đặt tên rõ ràng cho các widget

5. Ví dụ thực tế

5.1. StatelessWidget - ProductCard

class ProductCard extends StatelessWidget {
final String name;
final double price;
final String imageUrl;

const ProductCard({
super.key,
required this.name,
required this.price,
required this.imageUrl,
});

@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Image.network(imageUrl),
Text(name),
Text('\$$price'),
],
),
);
}
}

5.2. StatefulWidget - ShoppingCart

class ShoppingCart extends StatefulWidget {
const ShoppingCart({super.key});

@override
State<ShoppingCart> createState() => _ShoppingCartState();
}

class _ShoppingCartState extends State<ShoppingCart> {
final List<CartItem> _items = [];

void _addItem(CartItem item) {
setState(() {
_items.add(item);
});
}

void _removeItem(int index) {
setState(() {
_items.removeAt(index);
});
}

@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_items[index].name),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => _removeItem(index),
),
);
},
);
}
}

Kết luận

Việc lựa chọn giữa StatelessWidget và StatefulWidget phụ thuộc vào yêu cầu của ứng dụng. StatelessWidget đơn giản và hiệu quả cho UI tĩnh, trong khi StatefulWidget linh hoạt hơn cho UI động. Hiểu rõ sự khác biệt giữa hai loại widget này sẽ giúp bạn xây dựng ứng dụng Flutter tốt hơn.


Tài liệu tham khảo:

Widget Tree và các loại Widget trong Flutter

· 4 min read

Flutter Widget Tree

Widget là thành phần cơ bản trong Flutter, mọi thứ bạn nhìn thấy trên màn hình đều là widget. Bài viết này sẽ giúp bạn hiểu rõ về cấu trúc Widget Tree và các loại widget phổ biến trong Flutter.

1. Widget Tree là gì?

Widget Tree là cấu trúc phân cấp của các widget trong ứng dụng Flutter. Mỗi widget có thể chứa các widget con, tạo thành một cây các widget.

MaterialApp
└── Scaffold
├── AppBar
│ └── Text
└── Column
├── Text
├── SizedBox
└── ElevatedButton
└── Text

Đặc điểm của Widget Tree:

  • Cấu trúc phân cấp
  • Widget cha chứa widget con
  • Mỗi widget có thể có nhiều widget con
  • Widget con kế thừa các thuộc tính từ widget cha

2. Các loại Widget cơ bản

2.1. StatelessWidget vs StatefulWidget

StatelessWidget

  • Widget không có state (trạng thái)
  • Không thể thay đổi sau khi được tạo
  • Phù hợp cho UI tĩnh
class GreetingWidget extends StatelessWidget {
final String name;

const GreetingWidget({super.key, required this.name});

@override
Widget build(BuildContext context) {
return Text('Hello, $name!');
}
}

StatefulWidget

  • Widget có state (trạng thái)
  • Có thể thay đổi trong quá trình sử dụng
  • Phù hợp cho UI động
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});

@override
State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;

void _increment() {
setState(() {
_count++;
});
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $_count'),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}

2.2. Các Widget phổ biến

Layout Widgets

  1. Container

    • Widget linh hoạt nhất
    • Có thể định dạng padding, margin, border, background
    Container(
    padding: const EdgeInsets.all(16),
    margin: const EdgeInsets.all(8),
    decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(8),
    ),
    child: const Text('Hello'),
    )
  2. Row và Column

    • Sắp xếp các widget theo chiều ngang/dọc
    Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: [
    Text('Left'),
    Text('Center'),
    Text('Right'),
    ],
    )
  3. Stack

    • Xếp chồng các widget lên nhau
    Stack(
    children: [
    Image.network('url'),
    Positioned(
    bottom: 16,
    right: 16,
    child: Text('Overlay'),
    ),
    ],
    )

Input Widgets

  1. TextField

    • Nhập liệu văn bản
    TextField(
    decoration: InputDecoration(
    labelText: 'Username',
    hintText: 'Enter your username',
    ),
    )
  2. ElevatedButton

    • Nút bấm có hiệu ứng nổi
    ElevatedButton(
    onPressed: () {
    // Handle press
    },
    child: const Text('Click me'),
    )

Display Widgets

  1. Text

    • Hiển thị văn bản
    Text(
    'Hello World',
    style: TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    ),
    )
  2. Image

    • Hiển thị hình ảnh
    Image.network(
    'https://example.com/image.jpg',
    width: 200,
    height: 200,
    )

3. Quản lý State trong Widget Tree

3.1. Local State

  • Sử dụng setState trong StatefulWidget
  • Phù hợp cho state đơn giản, chỉ ảnh hưởng đến widget hiện tại

3.2. Global State

  • Sử dụng state management (Provider, Bloc, GetX)
  • Phù hợp cho state phức tạp, được chia sẻ giữa nhiều widget

4. Best Practices

  1. Tổ chức Widget Tree

    • Tách các widget phức tạp thành các widget nhỏ hơn
    • Sử dụng các widget có thể tái sử dụng
    • Tránh widget tree quá sâu
  2. Performance

    • Sử dụng const constructor khi có thể
    • Tránh rebuild không cần thiết
    • Sử dụng ListView.builder cho danh sách dài
  3. State Management

    • Chọn giải pháp state management phù hợp
    • Tránh prop drilling
    • Tách biệt logic và UI

Kết luận

Hiểu rõ về Widget Tree và các loại widget là nền tảng quan trọng trong phát triển Flutter. Với kiến thức này, bạn có thể xây dựng UI phức tạp và quản lý state hiệu quả.


Tài liệu tham khảo: