Flutter 导航与路由
本节将介绍 Flutter 中的导航和路由机制,包括页面跳转、参数传递和命名路由。
Navigator 基础
Flutter 使用 Navigator 进行页面管理,基于栈(stack)结构实现页面跳转。
实例:基本页面跳转
// 跳转到新页面
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// 返回上一页
Navigator.of(context).pop();
// 带结果返回
Navigator.of(context).pop('返回的数据');
// 替换当前页面(不可返回)
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const HomePage(),
),
);
// 回到根页面
Navigator.of(context).popUntil((route) => route.isFirst);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// 返回上一页
Navigator.of(context).pop();
// 带结果返回
Navigator.of(context).pop('返回的数据');
// 替换当前页面(不可返回)
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const HomePage(),
),
);
// 回到根页面
Navigator.of(context).popUntil((route) => route.isFirst);
MaterialPageRoute vs CupertinoPageRoute
实例:不同平台的页面切换动画
// Android 风格 - 从底部升起
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// iOS 风格 - 从右侧滑入
Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) => const DetailPage(),
),
);
// 自定义过渡效果
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return const DetailPage();
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
// 淡入淡出效果
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 300),
),
);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
// iOS 风格 - 从右侧滑入
Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) => const DetailPage(),
),
);
// 自定义过渡效果
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
return const DetailPage();
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
// 淡入淡出效果
return FadeTransition(
opacity: animation,
child: child,
);
},
transitionDuration: const Duration(milliseconds: 300),
),
);
页面参数传递
< h2 class="example">实例:传递参数到新页面
// 跳转到详情页并传递参数
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProductDetailPage(productId: 123),
),
);
// 详情页接收参数
class ProductDetailPage extends StatelessWidget {
final int productId;
const ProductDetailPage({
super.key,
required this.productId,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('商品 $productId')),
body: Center(child: Text('商品 ID: $productId')),
);
}
}
// 传递多个参数
class UserProfilePage extends StatelessWidget {
final String name;
final int age;
const UserProfilePage({
super.key,
required this.name,
required this.age,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('$name 的主页')),
body: Center(child: Text('年龄: $age')),
);
}
}
// 跳转
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const UserProfilePage(
name: '张三',
age: 25,
),
),
);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProductDetailPage(productId: 123),
),
);
// 详情页接收参数
class ProductDetailPage extends StatelessWidget {
final int productId;
const ProductDetailPage({
super.key,
required this.productId,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('商品 $productId')),
body: Center(child: Text('商品 ID: $productId')),
);
}
}
// 传递多个参数
class UserProfilePage extends StatelessWidget {
final String name;
final int age;
const UserProfilePage({
super.key,
required this.name,
required this.age,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('$name 的主页')),
body: Center(child: Text('年龄: $age')),
);
}
}
// 跳转
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const UserProfilePage(
name: '张三',
age: 25,
),
),
);
命名路由
使用命名路由可以简化页面跳转配置。
实例:命名路由配置
void main() {
runApp(
MaterialApp(
// 定义路由表
initialRoute: '/',
routes: {
'/': (context) => const HomePage(),
'/detail': (context) => const DetailPage(),
'/user': (context) => const UserPage(),
'/settings': (context) => const SettingsPage(),
},
),
);
}
// 跳转
Navigator.pushNamed(context, '/detail');
// 带参数跳转
Navigator.pushNamed(context, '/user', arguments: {'name': '张三', 'age': 25});
// 在目标页面获取参数
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
// 获取传递的参数
final args = ModalRoute.of(context)?.settings.arguments as Map?;
return Scaffold(
appBar: AppBar(title: const Text('详情页')),
body: Center(
child: Text('参数: $args'),
),
);
}
}
runApp(
MaterialApp(
// 定义路由表
initialRoute: '/',
routes: {
'/': (context) => const HomePage(),
'/detail': (context) => const DetailPage(),
'/user': (context) => const UserPage(),
'/settings': (context) => const SettingsPage(),
},
),
);
}
// 跳转
Navigator.pushNamed(context, '/detail');
// 带参数跳转
Navigator.pushNamed(context, '/user', arguments: {'name': '张三', 'age': 25});
// 在目标页面获取参数
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
// 获取传递的参数
final args = ModalRoute.of(context)?.settings.arguments as Map?;
return Scaffold(
appBar: AppBar(title: const Text('详情页')),
body: Center(
child: Text('参数: $args'),
),
);
}
}
路由传值的另一种方式
实例:onGenerateRoute
MaterialApp(
onGenerateRoute: (settings) {
// 根据路由名称返回不同页面
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (_) => const HomePage(),
);
case '/detail':
// 从 arguments 获取参数
final args = settings.arguments as Map<String, dynamic>?;
return MaterialPageRoute(
builder: (_) => DetailPage(
id: args?['id'] ?? 0,
title: args?['title'] ?? '',
),
);
case '/product':
final productId = settings.name?.split('/').last;
return MaterialPageRoute(
builder: (_) => ProductPage(id: productId ?? ''),
);
default:
return MaterialPageRoute(
builder: (_) => const NotFoundPage(),
);
}
},
)
onGenerateRoute: (settings) {
// 根据路由名称返回不同页面
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (_) => const HomePage(),
);
case '/detail':
// 从 arguments 获取参数
final args = settings.arguments as Map<String, dynamic>?;
return MaterialPageRoute(
builder: (_) => DetailPage(
id: args?['id'] ?? 0,
title: args?['title'] ?? '',
),
);
case '/product':
final productId = settings.name?.split('/').last;
return MaterialPageRoute(
builder: (_) => ProductPage(id: productId ?? ''),
);
default:
return MaterialPageRoute(
builder: (_) => const NotFoundPage(),
);
}
},
)
对于简单的应用,基础 Navigator.push 就足够了;对于中大型应用,建议使用命名路由或路由管理库来统一管理页面。
