Flutter: Giới thiệu về Provider package
· 5 min read
Provider là một trong những package phổ biến nhất để quản lý state trong Flutter. Bài viết này sẽ giới thiệu về Provider và cách sử dụng nó hiệu quả.
1. Giới thiệu về Provider
Provider là một giải pháp quản lý state được phát triển bởi Remi Rousselet, một trong những core team member của Flutter. Provider được thiết kế để đơn giản hóa việc quản lý state và dependency injection trong ứng dụng Flutter.
1.1. Cài đặt Provider
Thêm Provider vào file pubspec.yaml
:
dependencies:
provider: ^6.0.0
1.2. Các loại Provider
Provider cung cấp nhiều loại provider khác nhau:
Provider
: Cung cấp một giá trịChangeNotifierProvider
: Cung cấp một ChangeNotifierListenableProvider
: Cung cấp một ListenableValueListenableProvider
: Cung cấp một ValueListenableStreamProvider
: Cung cấp một StreamFutureProvider
: Cung cấp một Future
2. Sử dụng Provider
2.1. Tạo Provider
class CounterProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
void decrement() {
_count--;
notifyListeners();
}
}
2.2. Cung cấp Provider
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterProvider(),
child: MaterialApp(
home: HomeScreen(),
),
);
}
}
2.3. Sử dụng Provider
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Provider Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<CounterProvider>(
builder: (context, counter, child) {
return Text(
'Count: ${counter.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
ElevatedButton(
onPressed: () {
context.read<CounterProvider>().increment();
},
child: Text('Increment'),
),
],
),
),
);
}
}
3. Các Pattern phổ biến
3.1. MultiProvider
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CounterProvider()),
ChangeNotifierProvider(create: (_) => ThemeProvider()),
ChangeNotifierProvider(create: (_) => UserProvider()),
],
child: MaterialApp(
home: HomeScreen(),
),
);
}
}
3.2. ProxyProvider
class UserProvider extends ChangeNotifier {
String _name = '';
String get name => _name;
void updateName(String name) {
_name = name;
notifyListeners();
}
}
class UserProfileProvider extends ChangeNotifier {
final UserProvider userProvider;
String _greeting = '';
UserProfileProvider(this.userProvider) {
userProvider.addListener(_updateGreeting);
_updateGreeting();
}
String get greeting => _greeting;
void _updateGreeting() {
_greeting = 'Hello, ${userProvider.name}!';
notifyListeners();
}
@override
void dispose() {
userProvider.removeListener(_updateGreeting);
super.dispose();
}
}
// Sử dụng
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserProvider()),
ChangeNotifierProxyProvider<UserProvider, UserProfileProvider>(
create: (context) => UserProfileProvider(
Provider.of<UserProvider>(context, listen: false),
),
update: (context, userProvider, previous) =>
UserProfileProvider(userProvider),
),
],
child: MyApp(),
)
4. Best Practices
4.1. Tối ưu hóa hiệu suất
// Sử dụng Consumer thay vì Provider.of
Consumer<CounterProvider>(
builder: (context, counter, child) {
return Column(
children: [
Text('Count: ${counter.count}'),
child!, // Widget không thay đổi
],
);
},
child: const StaticWidget(), // Widget được tạo một lần
)
// Sử dụng Selector để chỉ rebuild khi cần
Selector<CounterProvider, int>(
selector: (_, provider) => provider.count,
builder: (context, count, child) {
return Text('Count: $count');
},
)
4.2. Xử lý lỗi
class DataProvider extends ChangeNotifier {
String? _error;
bool _isLoading = false;
List<Data> _data = [];
String? get error => _error;
bool get isLoading => _isLoading;
List<Data> get data => _data;
Future<void> fetchData() async {
try {
_isLoading = true;
_error = null;
notifyListeners();
_data = await api.fetchData();
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
}
5. Ví dụ thực tế
5.1. Ứng dụng Todo
class TodoProvider extends ChangeNotifier {
List<Todo> _todos = [];
bool _isLoading = false;
List<Todo> get todos => _todos;
bool get isLoading => _isLoading;
Future<void> addTodo(Todo todo) async {
_isLoading = true;
notifyListeners();
try {
final savedTodo = await api.saveTodo(todo);
_todos.add(savedTodo);
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> toggleTodo(String id) async {
final todo = _todos.firstWhere((t) => t.id == id);
final updatedTodo = todo.copyWith(completed: !todo.completed);
try {
await api.updateTodo(updatedTodo);
final index = _todos.indexWhere((t) => t.id == id);
_todos[index] = updatedTodo;
notifyListeners();
} catch (e) {
// Xử lý lỗi
}
}
}
class TodoScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Todo List')),
body: Consumer<TodoProvider>(
builder: (context, provider, child) {
if (provider.isLoading) {
return Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: provider.todos.length,
itemBuilder: (context, index) {
final todo = provider.todos[index];
return TodoItem(
todo: todo,
onToggle: () => provider.toggleTodo(todo.id),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Hiển thị dialog thêm todo mới
},
child: Icon(Icons.add),
),
);
}
}
5.2. Ứng dụng Authentication
class AuthProvider extends ChangeNotifier {
User? _user;
bool _isLoading = false;
User? get user => _user;
bool get isLoading => _isLoading;
bool get isAuthenticated => _user != null;
Future<void> login(String email, String password) async {
_isLoading = true;
notifyListeners();
try {
_user = await authService.login(email, password);
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> logout() async {
_isLoading = true;
notifyListeners();
try {
await authService.logout();
_user = null;
} finally {
_isLoading = false;
notifyListeners();
}
}
}
class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: Consumer<AuthProvider>(
builder: (context, auth, child) {
if (auth.isLoading) {
return Center(child: CircularProgressIndicator());
}
return LoginForm(
onLogin: (email, password) {
auth.login(email, password);
},
);
},
),
);
}
}
Kết luận
Provider là một giải pháp quản lý state đơn giản nhưng mạnh mẽ trong Flutter. Nó giúp bạn tổ chức code tốt hơn, dễ bảo trì và mở rộng. Việc hiểu và sử dụng Provider đúng cách sẽ giúp bạn xây dựng ứng dụng Flutter hiệu quả hơn.
Tài liệu tham khảo: