Skip to main content

One post tagged with "Data Passing"

View All Tags

Flutter: Truyền dữ liệu giữa các widget

· 5 min read

Truyền dữ liệu giữa các widget là một khía cạnh quan trọng trong phát triển ứng dụng Flutter. Bài viết này sẽ giới thiệu các phương pháp khác nhau để truyền dữ liệu giữa các widget.

Widget Data Passing

1. Truyền dữ liệu qua Constructor

1.1. Truyền dữ liệu đơn giản

class ParentWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChildWidget(
title: 'Hello',
count: 42,
);
}
}

class ChildWidget extends StatelessWidget {
final String title;
final int count;

const ChildWidget({
Key? key,
required this.title,
required this.count,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Column(
children: [
Text(title),
Text('Count: $count'),
],
);
}
}

1.2. Truyền Callback Functions

class ParentWidget extends StatelessWidget {
void _handleButtonPress() {
print('Button pressed!');
}

@override
Widget build(BuildContext context) {
return ChildWidget(
onButtonPressed: _handleButtonPress,
);
}
}

class ChildWidget extends StatelessWidget {
final VoidCallback onButtonPressed;

const ChildWidget({
Key? key,
required this.onButtonPressed,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onButtonPressed,
child: Text('Press Me'),
);
}
}

2. InheritedWidget

2.1. Tạo InheritedWidget

class UserData extends InheritedWidget {
final String username;
final String email;

const UserData({
Key? key,
required this.username,
required this.email,
required Widget child,
}) : super(key: key, child: child);

static UserData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserData>()!;
}

@override
bool updateShouldNotify(UserData oldWidget) {
return username != oldWidget.username || email != oldWidget.email;
}
}

2.2. Sử dụng InheritedWidget

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return UserData(
username: 'john_doe',
email: 'john@example.com',
child: MaterialApp(
home: HomeScreen(),
),
);
}
}

class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final userData = UserData.of(context);
return Column(
children: [
Text('Username: ${userData.username}'),
Text('Email: ${userData.email}'),
],
);
}
}

3. Provider Package

3.1. Tạo Provider

class UserProvider extends ChangeNotifier {
String _username = '';
String _email = '';

String get username => _username;
String get email => _email;

void updateUser(String username, String email) {
_username = username;
_email = email;
notifyListeners();
}
}

3.2. Sử dụng Provider

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => UserProvider(),
child: MaterialApp(
home: HomeScreen(),
),
);
}
}

class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final userProvider = Provider.of<UserProvider>(context);
return Column(
children: [
Text('Username: ${userProvider.username}'),
Text('Email: ${userProvider.email}'),
ElevatedButton(
onPressed: () {
userProvider.updateUser('new_user', 'new@example.com');
},
child: Text('Update User'),
),
],
);
}
}

4. Stream và StreamBuilder

4.1. Tạo Stream

class DataStream {
final _controller = StreamController<String>();

Stream<String> get stream => _controller.stream;

void addData(String data) {
_controller.add(data);
}

void dispose() {
_controller.close();
}
}

4.2. Sử dụng StreamBuilder

class StreamWidget extends StatelessWidget {
final DataStream dataStream;

const StreamWidget({
Key? key,
required this.dataStream,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return StreamBuilder<String>(
stream: dataStream.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Data: ${snapshot.data}');
}
return CircularProgressIndicator();
},
);
}
}

5. Best Practices

5.1. Chọn phương pháp phù hợp

  • Constructor: Cho dữ liệu đơn giản và tĩnh
  • InheritedWidget: Cho dữ liệu được chia sẻ rộng rãi
  • Provider: Cho state management phức tạp
  • Stream: Cho dữ liệu thay đổi theo thời gian thực

5.2. Tối ưu hóa hiệu suất

// Sử dụng const constructor khi có thể
const ChildWidget({
required this.title,
required this.count,
});

// Tránh rebuild không cần thiết
class OptimizedWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<UserProvider>(
builder: (context, userProvider, child) {
return Column(
children: [
// Widget thay đổi
Text(userProvider.username),
// Widget không thay đổi
child!,
],
);
},
child: const StaticWidget(),
);
}
}

6. Ví dụ thực tế

6.1. Ứng dụng Todo

class TodoProvider extends ChangeNotifier {
List<Todo> _todos = [];

List<Todo> get todos => _todos;

void addTodo(Todo todo) {
_todos.add(todo);
notifyListeners();
}

void removeTodo(String id) {
_todos.removeWhere((todo) => todo.id == id);
notifyListeners();
}
}

class TodoList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<TodoProvider>(
builder: (context, todoProvider, child) {
return ListView.builder(
itemCount: todoProvider.todos.length,
itemBuilder: (context, index) {
final todo = todoProvider.todos[index];
return TodoItem(todo: todo);
},
);
},
);
}
}

6.2. Ứng dụng Chat

class ChatProvider extends ChangeNotifier {
final _messages = <Message>[];
final _streamController = StreamController<Message>();

Stream<Message> get messageStream => _streamController.stream;

void sendMessage(Message message) {
_messages.add(message);
_streamController.add(message);
notifyListeners();
}

@override
void dispose() {
_streamController.close();
super.dispose();
}
}

class ChatScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: StreamBuilder<Message>(
stream: context.read<ChatProvider>().messageStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return MessageBubble(message: snapshot.data!);
}
return Container();
},
),
),
MessageInput(
onSend: (text) {
context.read<ChatProvider>().sendMessage(
Message(text: text, timestamp: DateTime.now()),
);
},
),
],
);
}
}

Kết luận

Việc chọn phương pháp truyền dữ liệu phù hợp là rất quan trọng trong phát triển ứng dụng Flutter. Mỗi phương pháp có ưu điểm và trường hợp sử dụng riêng. Hiểu rõ các phương pháp này 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: