Flutter如何实现Dashboard中可点击跳转的Card卡片组件
Flutter Dashboard卡片点击跳转实现方案
Card组件本身没有自带点击事件回调,要实现点击跳转效果,只需要用可响应点击的组件包裹Card,在点击回调中触发路由跳转即可,优先推荐用InkWell包裹,自带Material Design规范的水波纹点击反馈,体验更好。
实现步骤
- 先创建好每个功能卡片对应的目标页面,所有页面都是普通的Flutter页面组件,包含Scaffold根布局即可
- 给每个Card外层套一层
InkWell,设置和Card完全一致的圆角参数,避免水波纹效果溢出直角 - 在
InkWell的onTap回调中,调用Navigator.push方法传入对应目标页的实例,完成跳转
关键代码示例
单卡片修改写法:
// 功能介绍卡片 InkWell( borderRadius: BorderRadius.circular(8), // 和Card圆角保持一致 onTap: () { // 点击执行路由跳转 Navigator.push( context, MaterialPageRoute(builder: (context) => const DescriptionPage()), ); }, child: Card( shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular(8) ), elevation: 4, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CircleAvatar( radius: 64, backgroundImage: NetworkImage(''), ), Text( 'Description', style: cardTextStyle, ) ], ), ), )
目标页面最简示例(其余卡片对应页面都可以参照这个结构编写):
class DescriptionPage extends StatelessWidget { const DescriptionPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('功能介绍')), body: const Center(child: Text('这里放介绍页的业务内容')), ); } }
注意事项
- 不要把InkWell写在Card的child属性里,否则点击热区只会覆盖卡片内部内容,无法响应整个卡片区域的点击
- 如果不需要水波纹点击反馈,可以把InkWell替换为
GestureDetector,onTap回调的写法完全一致,只是没有默认的材质交互效果 - 路由跳转使用的context必须是位于MaterialApp子树中的上下文,否则会出现找不到路由的报错
- 剩余的新闻、信息、报名等卡片,只需要复制上述InkWell包裹Card的结构,替换onTap中跳转的目标页面类即可,后续要扩充到6个功能卡片逻辑完全相同
修改后的完整可运行代码
import 'package:flutter/material.dart'; class HomeScreen extends StatefulWidget { @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { @override Widget build(BuildContext context) { var size = MediaQuery.of(context).size; var cardTextStyle = TextStyle( fontFamily: "Open Sans", fontSize: 14, color: Color.fromRGBO(63, 63, 63, 1)); return Scaffold( body: Stack( children: <Widget>[ Container( height: size.height * .3, decoration: BoxDecoration( image: DecorationImage( alignment: Alignment.topCenter, image: AssetImage('')), ), ), SafeArea( child: Padding( padding: EdgeInsets.all(16.0), child: Column( children: <Widget>[ Container( height: 64, margin: EdgeInsets.only(bottom: 20), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ CircleAvatar( radius: 32, backgroundImage: NetworkImage( 'https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Crystal_Clear_kdm_user_female.svg/2048px-Crystal_Clear_kdm_user_female.svg.png'), ), SizedBox( width: 16, ), Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( 'User', style: TextStyle( fontFamily: "Open Sans", color: Colors.black, fontSize: 20), ), Text( 'ID:4101410141', style: TextStyle( fontSize: 14, color: Colors.black, fontFamily: "Open Sans"), ) ], ) ], ), ), Expanded( child: GridView.count( mainAxisSpacing: 10, crossAxisSpacing: 10, primary: false, crossAxisCount: 2, // 6个功能推荐改成2列布局展示效果更好,可根据需求自行调整 children: <Widget>[ // 介绍卡片 InkWell( borderRadius: BorderRadius.circular(8), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const DescriptionPage())); }, child: Card( shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular(8) ), elevation: 4, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CircleAvatar( radius: 32, backgroundImage: NetworkImage(''), ), SizedBox(height: 8), Text( 'Description', style: cardTextStyle, ) ], ), ), ), // 新闻卡片 InkWell( borderRadius: BorderRadius.circular(8), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const NewsPage())); }, child: Card( shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular(8) ), elevation: 4, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CircleAvatar( radius: 32, backgroundImage: NetworkImage(''), ), SizedBox(height: 8), Text( 'News', style: cardTextStyle, ) ], ), ), ), // 信息卡片 InkWell( borderRadius: BorderRadius.circular(8), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const InformationPage())); }, child: Card( shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular(8) ), elevation: 4, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CircleAvatar( radius: 32, backgroundImage: NetworkImage( 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Messagebox_info.svg/1200px-Messagebox_info.svg.png'), ), SizedBox(height: 8), Text( 'Information', style: cardTextStyle, ) ], ), ), ), // 报名卡片 InkWell( borderRadius: BorderRadius.circular(8), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => const RegistrationPage())); }, child: Card( shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular(8) ), elevation: 4, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ CircleAvatar( radius: 32, backgroundImage: NetworkImage(''), ), SizedBox(height: 8), Text( 'Registration', style: cardTextStyle, ) ], ), ), ), // 剩下两个功能卡片照着上面的结构新增即可 ], ), ), ], ), ), ), ], ), ); } } // 以下是各个目标页的最简示例,实际业务内容可以自行替换 class DescriptionPage extends StatelessWidget { const DescriptionPage({super.key}); @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: const Text('功能介绍')), body: const Center(child: Text('介绍页'))); } } class NewsPage extends StatelessWidget { const NewsPage({super.key}); @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: const Text('新闻资讯')), body: const Center(child: Text('新闻页'))); } } class InformationPage extends StatelessWidget { const InformationPage({super.key}); @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: const Text('通知信息')), body: const Center(child: Text('信息页'))); } } class RegistrationPage extends StatelessWidget { const RegistrationPage({super.key}); @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: const Text('报名登记')), body: const Center(child: Text('报名页'))); } }
内容的提问来源于stack exchange,提问作者NewUser556
相关产品推荐
相关产品推荐

