📦 Truyền dữ liệu
1. Truyền qua Constructor
Phần tiêu đề “1. Truyền qua Constructor”Cách đơn giản và type-safe nhất:
// Screen nhận dataclass ProfileScreen extends StatelessWidget { final String userId; final String userName;
const ProfileScreen({ super.key, required this.userId, required this.userName, });
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(userName)), body: Text('User ID: $userId'), ); }}
// NavigateNavigator.push( context, MaterialPageRoute( builder: (_) => ProfileScreen( userId: '123', userName: 'John', ), ),);2. Truyền qua Named Route Arguments
Phần tiêu đề “2. Truyền qua Named Route Arguments”// Navigate với argumentsNavigator.pushNamed( context, '/profile', arguments: ProfileArguments(userId: '123', userName: 'John'),);
// Arguments classclass ProfileArguments { final String userId; final String userName; ProfileArguments({required this.userId, required this.userName});}
// Nhận trong screenclass ProfileScreen extends StatelessWidget { @override Widget build(BuildContext context) { final args = ModalRoute.of(context)!.settings.arguments as ProfileArguments; return Text('User: ${args.userName}'); }}3. Nhận data khi Pop
Phần tiêu đề “3. Nhận data khi Pop”Sử dụng Navigator.pop với result
Phần tiêu đề “Sử dụng Navigator.pop với result”// Screen B - Pop với dataNavigator.pop(context, 'Selected value');
// Screen A - Nhận data sau khi pushfinal result = await Navigator.push( context, MaterialPageRoute(builder: (_) => SelectionScreen()),);
if (result != null) { print('Selected: $result');}Ví dụ: Color Picker
Phần tiêu đề “Ví dụ: Color Picker”// Color picker screenclass ColorPickerScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Pick a Color')), body: ListView( children: [ ListTile( title: Text('Red'), tileColor: Colors.red, onTap: () => Navigator.pop(context, Colors.red), ), ListTile( title: Text('Blue'), tileColor: Colors.blue, onTap: () => Navigator.pop(context, Colors.blue), ), ListTile( title: Text('Green'), tileColor: Colors.green, onTap: () => Navigator.pop(context, Colors.green), ), ], ), ); }}
// Main screenclass MainScreen extends StatefulWidget { @override State<MainScreen> createState() => _MainScreenState();}
class _MainScreenState extends State<MainScreen> { Color _selectedColor = Colors.grey;
Future<void> _pickColor() async { final color = await Navigator.push<Color>( context, MaterialPageRoute(builder: (_) => ColorPickerScreen()), );
if (color != null) { setState(() => _selectedColor = color); } }
@override Widget build(BuildContext context) { return Scaffold( body: Center( child: Container( width: 100, height: 100, color: _selectedColor, ), ), floatingActionButton: FloatingActionButton( onPressed: _pickColor, child: Icon(Icons.color_lens), ), ); }}4. Với GoRouter
Phần tiêu đề “4. Với GoRouter”Path Parameters
Phần tiêu đề “Path Parameters”// RouteGoRoute( path: '/user/:userId', builder: (context, state) { final userId = state.pathParameters['userId']!; return UserScreen(userId: userId); },),
// Navigatecontext.go('/user/123');Extra Data
Phần tiêu đề “Extra Data”// Navigate với objectcontext.go('/product', extra: product);
// RouteGoRoute( path: '/product', builder: (context, state) { final product = state.extra as Product; return ProductScreen(product: product); },),5. Với State Management
Phần tiêu đề “5. Với State Management”Provider
Phần tiêu đề “Provider”// Provider lưu selected itemclass SelectionProvider extends ChangeNotifier { Product? _selectedProduct;
Product? get selectedProduct => _selectedProduct;
void select(Product product) { _selectedProduct = product; notifyListeners(); }}
// Screen A - Selectcontext.read<SelectionProvider>().select(product);Navigator.pushNamed(context, '/detail');
// Screen B - Usefinal product = context.watch<SelectionProvider>().selectedProduct;6. Callback Function
Phần tiêu đề “6. Callback Function”Truyền callback để nhận data:
// Parent screenclass ParentScreen extends StatefulWidget { @override State<ParentScreen> createState() => _ParentScreenState();}
class _ParentScreenState extends State<ParentScreen> { String _result = '';
void _handleResult(String value) { setState(() => _result = value); }
@override Widget build(BuildContext context) { return Column( children: [ Text('Result: $_result'), ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (_) => ChildScreen(onSelect: _handleResult), ), ); }, child: Text('Open Child'), ), ], ); }}
// Child screenclass ChildScreen extends StatelessWidget { final Function(String) onSelect;
const ChildScreen({super.key, required this.onSelect});
@override Widget build(BuildContext context) { return ElevatedButton( onPressed: () { onSelect('Selected value'); Navigator.pop(context); }, child: Text('Select'), ); }}7. So sánh các cách
Phần tiêu đề “7. So sánh các cách”| Cách | Pros | Cons |
|---|---|---|
| Constructor | Type-safe, simple | Chỉ một chiều |
| Arguments | Dùng với named routes | Không type-safe |
| Pop result | Hai chiều | Async, cần await |
| State Management | Chia sẻ giữa nhiều screens | Setup overhead |
| Callback | Type-safe, hai chiều | Coupling |
8. Best Practices
Phần tiêu đề “8. Best Practices”- Ưu tiên constructor cho one-way data
- Dùng pop result cho selection screens
- Dùng state management cho shared state
- Type-safe arguments với custom classes
// Good: Type-safe argument classclass ProductDetailArgs { final String productId; final String productName; final bool isEditing;
ProductDetailArgs({ required this.productId, required this.productName, this.isEditing = false, });}📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Method | Direction | Type-safe |
|---|---|---|
| Constructor | → | ✅ |
| Route arguments | → | ⚠️ (cần cast) |
| Pop result | ← | ✅ |
| State management | ↔ | ✅ |
| Callback | ↔ | ✅ |