Bỏ qua để đến nội dung

📦 Truyền dữ liệu

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',
),
),
);

// 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}');
}
}

// 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');
}
// 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),
),
);
}
}

// Route
GoRoute(
path: '/user/:userId',
builder: (context, state) {
final userId = state.pathParameters['userId']!;
return UserScreen(userId: userId);
},
),
// Navigate
context.go('/user/123');
// 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);
},
),

// 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;

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'),
);
}
}

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

  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,
});
}

Method Direction Type-safe
Constructor
Route arguments ⚠️ (cần cast)
Pop result
State management
Callback