Flutter 高亮当前选中的导航项
Posted
技术标签:
【中文标题】Flutter 高亮当前选中的导航项【英文标题】:Flutter highlight current selected nav item 【发布时间】:2020-07-03 07:11:39 【问题描述】:我为个人项目创建了一个 Flutter 入门工具包。
一切正常,但我有一个问题,我无法突出显示抽屉中当前选定的项目。
我不知道应该把确定当前选定项目的代码放在哪里。
下面是我的代码!
class _MdDrawerState extends State<MdDrawer>
with SingleTickerProviderStateMixin<MdDrawer>
final _animationDuration = const Duration(milliseconds: 350);
AnimationController _animationController;
Stream<bool> isDrawerOpenStream;
StreamController<bool> isDrawerOpenStreamController;
StreamSink<bool> isDrawerOpenSink;
.....
@override
void dispose()
_animationController.dispose();
isDrawerOpenStreamController.close();
isDrawerOpenSink.close();
super.dispose();
void onIconPressed()
final animationStatus = _animationController.status;
final isAnimationCompleted = animationStatus == AnimationStatus.completed;
.....
@override
Widget build(BuildContext context)
final screenWidth = MediaQuery.of(context).size.width;
return StreamBuilder<bool>(
initialData: false,
stream: isDrawerOpenStream,
builder: (context, isLeftDrawerOpenedAsync)
return AnimatedPositioned(
duration: _animationDuration,
top: 0,
bottom: 0,
left: isLeftDrawerOpenedAsync.data ? 0 : -screenWidth,
right: isLeftDrawerOpenedAsync.data ? 0 : screenWidth - 45,
child: Row(
children: <Widget>[
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
color: Theme.of(context).backgroundColor,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
SizedBox(
height: 30,
),
ListTile(
title: Text('First - Last',
style: Theme.of(context).textTheme.headline),
subtitle: Text('something@gmail.com',
style: Theme.of(context).textTheme.subhead),
leading: CircleAvatar(
child: Icon(
Icons.perm_identity,
color: Theme.of(context).iconTheme.color,
),
radius: 40,
),
),
Divider(
height: 30,
),
MdNavItem(
icon: Icons.home,
title: 'Home',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.HomeClickedEvent);
,
),
MdNavItem(
icon: Icons.account_box,
title: 'Account',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.AccountClickedEvent);
,
),
MdNavItem(
icon: Icons.shopping_basket,
title: 'Orders',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.OrderClickedEvent);
,
),
MdNavItem(
icon: Icons.card_giftcard,
title: 'Wishlist',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.WishlistClickedEvent);
,
),
Divider(
height: 30,
),
MdNavItem(
icon: Icons.settings,
title: 'Settings',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.SettingsClickedEvent);
,
),
MdNavItem(
icon: Icons.exit_to_app,
title: 'Logout',
),
Divider(
height: 45,
),
],
),
],
),
),
),
......
],
),
);
,
);
还有 MdNavItem 类
class MdNavItem extends StatelessWidget
final IconData icon;
final String title;
final Function onTap;
const MdNavItem(this.icon, this.title, this.onTap);
@override
Widget build(BuildContext context)
return GestureDetector(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Container(
color: Theme.of(context).backgroundColor,
child: Row(
children: <Widget>[
Icon(
icon,
size: 25,
color: Theme.of(context).iconTheme.color,
),
SizedBox(
width: 20,
),
Text(
title,
style: Theme.of(context).textTheme.headline,
),
],
),
),
),
);
【问题讨论】:
【参考方案1】:编辑:
第一种方法:
将此代码添加到您的抽屉中:
class _MdDrawerState extends State<MdDrawer>
with SingleTickerProviderStateMixin<MdDrawer>
final _animationDuration = const Duration(milliseconds: 350);
AnimationController _animationController;
Stream<bool> isDrawerOpenStream;
StreamController<bool> isDrawerOpenStreamController;
StreamSink<bool> isDrawerOpenSink;
final List<bool> isTaped = [true, false, false, false, false]; // the first is true because when the app
//launch the home needs to be in red(or the
//color you choose)
void changeHighlight(int index)
for(int indexTap = 0; indexTap < isTaped.length; indexTap++)
if (indexTap == index)
isTaped[index] = true; //used to change the value of the bool list
else
isTaped[indexTap] = false;
.....
@override
void dispose()
_animationController.dispose();
isDrawerOpenStreamController.close();
isDrawerOpenSink.close();
super.dispose();
void onIconPressed()
final animationStatus = _animationController.status;
final isAnimationCompleted = animationStatus == AnimationStatus.completed;
.....
@override
Widget build(BuildContext context)
final screenWidth = MediaQuery.of(context).size.width;
return StreamBuilder<bool>(
initialData: false,
stream: isDrawerOpenStream,
builder: (context, isLeftDrawerOpenedAsync)
return AnimatedPositioned(
duration: _animationDuration,
top: 0,
bottom: 0,
left: isLeftDrawerOpenedAsync.data ? 0 : -screenWidth,
right: isLeftDrawerOpenedAsync.data ? 0 : screenWidth - 45,
child: Row(
children: <Widget>[
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
color: Theme.of(context).backgroundColor,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
SizedBox(
height: 30,
),
ListTile(
title: Text('First - Last',
style: Theme.of(context).textTheme.headline),
subtitle: Text('something@gmail.com',
style: Theme.of(context).textTheme.subhead),
leading: CircleAvatar(
child: Icon(
Icons.perm_identity,
color: Theme.of(context).iconTheme.color,
),
radius: 40,
),
),
Divider(
height: 30,
),
MdNavItem(
wasTaped: isTaped[0],
icon: Icons.home,
title: 'Home',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.HomeClickedEvent);
changeHighlight(0);
,
),
MdNavItem(
wasTaped: isTaped[1],
icon: Icons.account_box,
title: 'Account',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.AccountClickedEvent);
changeHighlight(1);
,
),
MdNavItem(
wasTaped: isTaped[2],
icon: Icons.shopping_basket,
title: 'Orders',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.OrderClickedEvent);
changeHighlight(2);
,
),
MdNavItem(
wasTaped: isTaped[3],
icon: Icons.card_giftcard,
title: 'Wishlist',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.WishlistClickedEvent);
changeHighlight(3);
,
),
Divider(
height: 30,
),
MdNavItem(
wasTaped: isTaped[4],
icon: Icons.settings,
title: 'Settings',
onTap: ()
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.SettingsClickedEvent);
changeHighlight(4);
,
),
MdNavItem(
icon: Icons.exit_to_app,
title: 'Logout',
),
Divider(
height: 45,
),
],
),
],
),
),
),
......
],
),
);
,
);
这是你的 MdNavItem:
class MdNavItem extends StatelessWidget
final IconData icon;
final String title;
final Function onTap;
final bool wasTaped; //receiving the bool value (if was taped or not)
const MdNavItem(this.icon, this.title, this.onTap, this.wasTaped);
@override
Widget build(BuildContext context)
return GestureDetector(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Container(
color: Theme.of(context).backgroundColor,
child: Row(
children: <Widget>[
Icon(
icon,
size: 25,
color: wasTaped ? Colors.red : Theme.of(context).iconTheme.color, //the condition to change the color
),
SizedBox(
width: 20,
),
Text(
title,
style: wasTaped ? TextStyle(
color: Colors.red,
) : Theme.of(context).textTheme.headline,
),
],
),
),
),
);
旧答案,第二种方式:
第一个屏幕,PageView 所在的位置:
class FirstScreen extends StatelessWidget
final PageController pageController = PageController(initialPage: 0);
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text('Kit App'),
),
drawer: CustomDrawer(
pageController: pageController,
),
body: PageView(
controller: pageController,
physics: NeverScrollableScrollPhysics(), //to prevent scroll
children: <Widget>[
HomeScreen(),
AccountScreen(), //your pages
OrdersScreen(),
WishListScreen(),
],
),
);
自定义抽屉:
class CustomDrawer extends StatelessWidget
CustomDrawer(@required this.pageController);
final PageController pageController;
@override
Widget build(BuildContext context)
return Drawer(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 20),
child: Column(
children: <Widget>[
DrawerItem(
onTap: ()
Navigator.pop(context); //to close the drawer
pageController.jumpToPage(0);
,
leading: Icons.home,
title: 'Home',
index: 0,
controller: pageController,
),
DrawerItem(
onTap: ()
Navigator.pop(context);
pageController.jumpToPage(1);
,
leading: Icons.account_box,
title: 'Account',
index: 1,
controller: pageController,
),
DrawerItem(
onTap: ()
Navigator.pop(context);
pageController.jumpToPage(2);
,
leading: Icons.shopping_cart,
title: 'Orders',
index: 2,
controller: pageController,
),
DrawerItem(
onTap: ()
Navigator.pop(context);
pageController.jumpToPage(3);
,
leading: Icons.card_travel,
title: 'Wishlist',
index: 3,
controller: pageController,
),
],
),
),
);
还有DrawerItem,其中放置了改变物品颜色的条件:
class DrawerItem extends StatelessWidget
DrawerItem(
@required this.onTap,
@required this.leading,
@required this.title,
@required this.index,
@required this.controller,
);
final VoidCallback onTap;
final IconData leading;
final String title;
final int index;
final PageController controller;
@override
Widget build(BuildContext context)
return GestureDetector(
onTap: onTap,
child: ListTile(
leading: Icon(
leading,
color: controller.page.round() == index ? Colors.red : Colors.grey,
),
title: Text(
title,
style: TextStyle(
color: controller.page.round() == index ? Colors.red : Colors.grey,
),
),
),
);
结果:
现在你只需要在你的代码上实现它。
【讨论】:
如果不使用PageView
,就没有办法解决这个问题吗?我正在为我的抽屉使用自定义视图。
好的,我建议使用页面视图,因为我认为这是一种更简洁的方式,但我会用其他形式编辑我的答案。
我已将更新后的代码添加到我的项目中。但是我遇到了一个问题。在const MdNavItem(this.icon, this.title, this.onTap, this.wasTaped);
上,我收到了错误消息-Failed assertion: boolean expression must not be null
。我将初始值设置为 false
并没有帮助
我很乐意提供帮助!以上是关于Flutter 高亮当前选中的导航项的主要内容,如果未能解决你的问题,请参考以下文章
elementUI的导航栏在刷新页面的时候选中状态消失的解决
更改 ListView 中当前顶部可见项的颜色 - Flutter