Skip to Content
Flutter🧭 Navigation📦 Truyền dữ liệu

Truyền dữ liệu giữa các Screens

1. Truyền qua Constructor

Cách đơn giản và type-safe nhất:

// Screen nhận data class 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'), ); } } // Navigate Navigator.push( context, MaterialPageRoute( builder: (_) => ProfileScreen( userId: '123', userName: 'John', ), ), );

2. Truyền qua Named Route Arguments

// Navigate với arguments Navigator.pushNamed( context, '/profile', arguments: ProfileArguments(userId: '123', userName: 'John'), ); // Arguments class class ProfileArguments { final String userId; final String userName; ProfileArguments({required this.userId, required this.userName}); } // Nhận trong screen class 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

Sử dụng Navigator.pop với result

// Screen B - Pop với data Navigator.pop(context, 'Selected value'); // Screen A - Nhận data sau khi push final result = await Navigator.push( context, MaterialPageRoute(builder: (_) => SelectionScreen()), ); if (result != null) { print('Selected: $result'); }

Ví dụ: Color Picker

// Color picker screen class 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 screen class 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

Path Parameters

// Route GoRoute( path: '/user/:userId', builder: (context, state) { final userId = state.pathParameters['userId']!; return UserScreen(userId: userId); }, ), // Navigate context.go('/user/123');

Extra Data

// Navigate với object context.go('/product', extra: product); // Route GoRoute( path: '/product', builder: (context, state) { final product = state.extra as Product; return ProductScreen(product: product); }, ),

5. Với State Management

Provider

// Provider lưu selected item class SelectionProvider extends ChangeNotifier { Product? _selectedProduct; Product? get selectedProduct => _selectedProduct; void select(Product product) { _selectedProduct = product; notifyListeners(); } } // Screen A - Select context.read<SelectionProvider>().select(product); Navigator.pushNamed(context, '/detail'); // Screen B - Use final product = context.watch<SelectionProvider>().selectedProduct;

6. Callback Function

Truyền callback để nhận data:

// Parent screen class 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 screen class 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

CáchProsCons
ConstructorType-safe, simpleChỉ một chiều
ArgumentsDùng với named routesKhông type-safe
Pop resultHai chiềuAsync, cần await
State ManagementChia sẻ giữa nhiều screensSetup overhead
CallbackType-safe, hai chiềuCoupling

8. Best Practices

  1. Ưu tiên constructor cho one-way data
  2. Dùng pop result cho selection screens
  3. Dùng state management cho shared state
  4. Type-safe arguments với custom classes
// Good: Type-safe argument class class ProductDetailArgs { final String productId; final String productName; final bool isEditing; ProductDetailArgs({ required this.productId, required this.productName, this.isEditing = false, }); }

📝 Tóm tắt

MethodDirectionType-safe
Constructor
Route arguments⚠️ (cần cast)
Pop result
State management
Callback
Last updated on