Bottom Navigation Not Persisting On Some Pages - flutter

I have been trying to make the bottom navigation bar persisting on all page screens but it looks like it is only persisting for the pages that are on the bottom navigation only i.e HomeScreen(), DiscoverScreen(), GivingScreen(), EventsScreen() and SettingsScreen(). Other pages are not getting the bottom navigation. Here is the code of my bottom navigation bar. How can I add the bottom nav bar on all pages I have in the app?
class CustomBottomNavBar extends StatefulWidget {
const CustomBottomNavBar({Key key}) : super(key: key);
#override
_CustomBottomNavBarState createState() => _CustomBottomNavBarState();
}
class _CustomBottomNavBarState extends State<CustomBottomNavBar> {
int currentIndex = 0;
final screens = [
HomeScreen(),
DiscoverScreen(),
GivingScreen(),
EventsScreen(),
SettingsScreen(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: currentIndex,
children: screens,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: kPrimaryColor,
selectedItemColor: kSelectedItemColor,
iconSize: kBottomNavIconSize,
unselectedItemColor: kAlternativeColor,
selectedFontSize: kBottomNavFontSize,
unselectedFontSize: kBottomNavFontSize,
showUnselectedLabels: true,
showSelectedLabels: true,
currentIndex: currentIndex,
onTap: (index) => setState(() => currentIndex = index),
items: [
BottomNavigationBarItem(
icon: Icon(
CupertinoIcons.mic_fill,
),
label: 'Sermons',
),
BottomNavigationBarItem(
icon: Icon(
CupertinoIcons.wand_stars_inverse,
),
label: 'Discover',
),
BottomNavigationBarItem(
icon: Icon(
CupertinoIcons.heart_fill,
),
label: 'Giving',
),
BottomNavigationBarItem(
icon: Icon(
CupertinoIcons.calendar_today,
),
label: 'Events',
),
BottomNavigationBarItem(
icon: Icon(
CupertinoIcons.gear_alt_fill,
),
label: 'Settings',
),
],
),
);
}
}
Here is a picture of a view that has a bottom nav
Here is a picture of a view that has not a bottom nav
import 'package:church_app/components/widgets/animated_like_button.dart';
import 'package:church_app/components/widgets/custom_bottom_nav_bar.dart';
import 'package:church_app/components/widgets/navigation_drawer.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:church_app/utilities/constants.dart';
import 'package:church_app/components/widgets/action_and_text.dart';
class SermonDescriptionScreen extends StatefulWidget {
#override
_SermonDescriptionScreenState createState() =>
_SermonDescriptionScreenState();
}
class _SermonDescriptionScreenState extends State<SermonDescriptionScreen> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
endDrawer: NavigationDrawer(),
appBar: AppBar(
leading:
(ModalRoute.of(context)?.canPop ?? false) ? BackButton() : null,
iconTheme: IconThemeData(color: kPrimaryColor),
elevation: 0,
title: Text(
'Protect The Vessel',
style: kMainStyling,
),
centerTitle: true,
),
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SafeArea(
left: true,
right: true,
top: true,
bottom: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 250,
decoration: BoxDecoration(
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.grey[300],
blurRadius: 30,
offset: Offset(0, 10),
),
],
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage('assets/images/pastor.jpg'),
),
),
),
addVSpace(30),
Container(
margin: EdgeInsets.only(left: 10, right: 10, bottom: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Protect The Vessel',
style: kDescriptionTitle,
softWrap: true,
),
addVSpace(3),
Text(
'Pastor James Wiseman',
style: kMainStyling.copyWith(
color: kPrimaryColor.withOpacity(.8),
),
softWrap: true,
),
addVSpace(3),
Row(
children: [
Text(
'Jan 25, 2021',
style: kMainStyling.copyWith(
color: kPrimaryColor.withOpacity(.8),
),
softWrap: true,
),
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: kPrimaryColor,
),
width: 8,
height: 8,
),
),
Container(
child: AnimatedLikeButton(),
),
],
),
addVSpace(20),
Text(
'Are you protecting what matters? In Protect What Matters, Pastor James Wiseman reminds us that we are vessels that can either foster bitterness or make way for the healing hand of God in our lives.',
style: kMainStyling,
softWrap: true,
textAlign: TextAlign.justify,
maxLines: 6,
),
addVSpace(30),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ActionAndText(
correspondingIcon:
CupertinoIcons.arrow_down_to_line_alt,
correspondingText: 'Download',
gesture: () {},
),
ActionAndText(
correspondingIcon: CupertinoIcons.heart,
correspondingText: 'Like',
gesture: () {},
),
ActionAndText(
correspondingIcon: CupertinoIcons.play,
correspondingText: 'Listen',
gesture: () {
Navigator.pushNamed(context, '/playerScreen');
},
),
ActionAndText(
correspondingIcon: CupertinoIcons.square_arrow_up,
correspondingText: 'Share',
gesture: () {},
),
],
),
],
),
),
],
),
),
),
),
);
}
}

to get this you can use pageView with bottomNavigationBar... this is a sample code. this will give idea how to go about it..
Scaffold buildAuthScreen() {
return Scaffold(
body: PageView(
scrollDirection: Axis.horizontal,
children: [
Timeline(),
ActivityFeedItem(),
Search(),
Upload(),
Profile(
currentUser: _auth.currentUser.uid,
),
],
controller: _pageController,
onPageChanged: (value) {
setState(() {
pageIndex = value;
});
},
physics: BouncingScrollPhysics(),
),
bottomNavigationBar: CurvedNavigationBar(
key: _bottomNavigationKey,
index: pageIndex,
height: 50.0,
color: Theme.of(context).primaryColor,
items: [
Icon(Icons.whatshot, color: Colors.white),
Icon(Icons.notifications_active, color: Colors.white),
Icon(Icons.search, color: Colors.white),
Icon(Icons.photo_camera, color: Colors.white),
Icon(Icons.account_circle, color: Colors.white),
],
animationDuration: Duration(milliseconds: 200),
buttonBackgroundColor: Theme.of(context).accentColor,
backgroundColor: Colors.white,
onTap: (int pageIndex1) {
_pageController.animateToPage(pageIndex1,
duration: Duration(microseconds: 300), curve: Curves.bounceInOut);
},
),
);
}

Use custom_navigator 0.3.0 package from pub dev website

You can use this package https://pub.dev/packages/persistent_bottom_nav_bar for persistent bottom navigator in all Screen,
And when try to navigate to new screen use this below navigator function,
pushNewScreen(
context,
screen: MainScreen(),
withNavBar: true, // OPTIONAL VALUE. True by default.
pageTransitionAnimation: PageTransitionAnimation.cupertino,
);

The problem with inbuilt BottomNavigationBar doesn't have ability to show to every screen,there is plugin called Presistant_bottom_nav_bar,here is some sample code for more checkout other properties of PersistentTabView,animation,state,hide bottombar when keyboard comes up kind of stuff
Also checkout this material guideline do's and don'ts bottom-navigation if you are not interested in using the plugin
PersistentTabController _controller =PersistentTabController(initialIndex:
0);
//Screens for each nav items.
List<Widget> _NavScreens() {
return [
HomeScreen(),
DiscoverScreen(),
GivingScreen(),
EventsScreen(),
SettingsScreen(),
];
}
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
icon: Icon(CupertinoIcons.mic_fill),
title: ("Home"),
activeColor: CupertinoColors.activeBlue,
inactiveColor: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(CupertinoIcons.wand_stars_inverse),
title: ("Discover"),
activeColor: CupertinoColors.activeGreen,
inactiveColor: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(CupertinoIcons.heart_fill),
title: ("Giving"),
activeColor: CupertinoColors.systemRed,
inactiveColor: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(CupertinoIcons.calendar_today),
title: ("Events"),
activeColor: CupertinoColors.systemIndigo,
inactiveColor: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(CupertinoIcons.gear_alt_fill),
title: ("Settings"),
activeColor: CupertinoColors.systemIndigo,
inactiveColor: CupertinoColors.systemGrey,
),
];
}
#override
Widget build(BuildContext context) {
return Center(
child: PersistentTabView(
controller: _controller,
screens: _NavScreens(),
items: _navBarsItems(),
confineInSafeArea: true,
backgroundColor: Colors.white,
handleAndroidBackButtonPress: true,
resizeToAvoidBottomInset: true,
hideNavigationBarWhenKeyboardShows: true,
decoration: NavBarDecoration(
borderRadius: BorderRadius.circular(10.0),
),
popAllScreensOnTapOfSelectedTab: true,
navBarStyle: NavBarStyle.style9,
),
);
}
You can use this navigate and use the BottomNavigationBar with navigator provided by the plugin
pushNewScreen(
context,
screen: SubScreenNames(),
withNavBar: true,
pageTransitionAnimation: PageTransitionAnimation.cupertino,
);

Related

Persistent Bottom Navigation Bar (Plugin) on every products/pages

how i can make my "Persistent Bottom Navigation Bar" effectively persistent , on every route (Products page/Food page/ecc..)?
I'm not that expert with dart programming and most of the result that i have achieved are from previous's people experience and common sense, for this reason i'm trying to learn from my mistakes and do better, but at the moment i'm stuck with this problem, can someone help me and give me some advices on "How to do it", it would be so appreciated!
This is my route helper
class RouteHelper{
static const String initial='/';
static const String popularProduct="/popular-
product";
static const String
recommendedProduct="/category-recommended";
static String getInitial()=>'$initial';
static String getPopularProduct(int
pageId)=>'$popularProduct?pageId=$pageId';
static String getRecommendedProduct(int
pageId)=>'$recommendedProduct?pageId=$pageId';
static List<GetPage> routes=[
GetPage(name: initial, binding:
DashBoardBinding(), page: ()=>MyDashBoard()),
GetPage(name:popularProduct, page:(){
var pageId=Get.parameters['pageId'];
return
PopularProductDetail(pageId:int.parse(pageId!));
},
transition: Transition.rightToLeft,
transitionDuration: Duration(milliseconds: 250)
),
Meanwhile this is my Popular products buildcard:
Widget _buildCard(int index, ProductModel popularProductList) =>
GestureDetector(
onTap: (){
Get.toNamed(RouteHelper.getPopularProduct(index));
},
child: Container(
width: Dimensions.width120,
margin: EdgeInsets.only (top: Dimensions.height20, left: Dimensions.width15),
child: Column(
children: [
Expanded(
child: AspectRatio(aspectRatio: 4/3,
child: ClipRRect(borderRadius: BorderRadius.circular(Dimensions.radius20),
child: Image.network(AppConstants.BASE_URL+'/uploads/'+popularProductList.img!,fit: BoxFit.cover
),),
),
),
const SizedBox(height: 4),
Text(popularProductList.name!,style: TextStyle(fontSize: 12, fontWeight: FontWeight.w700)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(popularProductList.price!.toString(),style: TextStyle(color: AppColors.mainColor,fontWeight: FontWeight.w700),),
SizedBox(width: Dimensions.width5,),
Text('EUR',style: TextStyle(color: AppColors.mainColor),)
],
)
],
),
),
);
}
And finally my Persistent Bottom Navigation Bar setup:
enter code hereList<Widget> _buildScreens() {
return [
MainFoodPage(),
SearchPage(),
CartPage(),
WishlistPage(),
UserPage(),
];
}
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
icon: Icon(Icons.home),
title: ("Home"),
activeColorPrimary: CupertinoColors.white,
inactiveColorPrimary: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.search_outlined),
title: ("Search"),
activeColorPrimary: CupertinoColors.white,
inactiveColorPrimary: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.shopping_cart_outlined),
title: ("Cart"),
activeColorPrimary: CupertinoColors.white,
inactiveColorPrimary: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.favorite_border_outlined),
title: ("Wishlist"),
activeColorPrimary: CupertinoColors.white,
inactiveColorPrimary: CupertinoColors.systemGrey,
),
PersistentBottomNavBarItem(
icon: Icon(Icons.account_circle),
title: ("Account"),
activeColorPrimary: CupertinoColors.white,
inactiveColorPrimary: CupertinoColors.systemGrey,
),
];
}
#override
Widget build(BuildContext context) {
return PersistentTabView(
context,
controller: _controller,
screens: _buildScreens(),
items: _navBarsItems(),
confineInSafeArea: true,
backgroundColor: AppColors.mainColor, // Default is Colors.white.
handleAndroidBackButtonPress: true, // Default is true.
resizeToAvoidBottomInset: true, // This needs to be true if you want to move up the screen when keyboard appears. Default is true.
stateManagement: true, // Default is true.
hideNavigationBarWhenKeyboardShows: true, // Recommended to set 'resizeToAvoidBottomInset' as true while using this argument. Default is true.
decoration: NavBarDecoration(
colorBehindNavBar: Colors.white,
),
popAllScreensOnTapOfSelectedTab: true,
popActionScreens: PopActionScreensType.all,
itemAnimationProperties: ItemAnimationProperties( // Navigation Bar's items animation properties.
duration: Duration(milliseconds: 200),
curve: Curves.ease,
),
screenTransitionAnimation: ScreenTransitionAnimation( // Screen transition animation on change of selected tab.
animateTabTransition: true,
curve: Curves.ease,
duration: Duration(milliseconds: 200),
),
navBarStyle: NavBarStyle.style13, // Choose the nav bar style with this property.
);
}
Thank you

navigation bar issue after clicking on card flutter

[![When I click on any card the navigation bar doesn't work anymore ][2]][2]
the 1st image shows my navigation bar which works very well. The moment I click on a card the navigation bar doesn't work anymore and I didn't find out what is the reason
**This is my navigation bar Widget code that I'm using after clicking on the card **
class MenuBarWidget extends StatefulWidget {
#override
_State createState() => _State();
}
class _State extends State<MenuBarWidget> {
int _index = 0;
#override
Widget build(BuildContext context) {
return Container(
height: 70,
decoration: BoxDecoration(
gradient: AppGradients.linear,
),
child: BottomNavigationBar(
onTap: (newIndex) => setState(() => _index = newIndex),
currentIndex: _index,
unselectedItemColor: Colors.white70,
selectedItemColor: Colors.white,
elevation: 0,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: 'Principal',
backgroundColor: Colors.transparent,
),
BottomNavigationBarItem(
icon: Icon(Icons.assignment_outlined),
label: 'Pedidos',
backgroundColor: Colors.transparent,
),
BottomNavigationBarItem(
icon: Icon(Icons.chat_outlined),
label: 'Chat',
backgroundColor: Colors.transparent,
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: 'Perfil',
backgroundColor: Colors.transparent,
),
],
),
);
}
}
this is my code after clicking on the card
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: MenuBarWidget(),
appBar: PreferredSize(
preferredSize: Size.fromHeight(100),
child: Stack(children: [
Container(
height: 128,
decoration: BoxDecoration(
color: AppColors.green,
border: Border(
bottom: BorderSide(
color: AppColors.blueButton,
width: 2,
),
),
image: DecorationImage(
image: AssetImage(AppImages.dots),
fit: BoxFit.cover,
),
),
),
Align(
alignment: Alignment(-0.7, -0.3),
child: RichText(
text: TextSpan(
text: serviceName,
style: TextStyle(
color: AppColors.white,
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
),
),
Align(
alignment: Alignment(-0.6, 0.4),
child: RichText(
text: TextSpan(
text: 'Confira abaixo todos os profissionais desta categoria',
style: TextStyle(
color: AppColors.white,
fontSize: 9,
fontWeight: FontWeight.bold,
),
),
),
),
]),
),
**Any help is appreciated thanks **
create a new list of screens and add the screens you created as the list items
List screens = [
HomeScreen();
CarpentryScreen();
...
...
];
then make the body of your MenuBarWidget() return screens[_index]
class MenuBarWidget extends StatefulWidget {
#override
_State createState() => _State();
}
class _State extends State<MenuBarWidget> {
int _index = 0;
// new list of your screens
List<Widget> screens = [
PrincipalScreen(),
PedidosScreen(),
Chatscreen(),
Perfilscreen(),
];
#override
Widget build(BuildContext context) {
// return the screens
return Scaffold(
body: screens[_index],
bottomNavigationBar: BottomNavigationBar(
onTap: (newIndex) => setState(() => _index = newIndex),
currentIndex: _index,
unselectedItemColor: Colors.white70,
selectedItemColor: Colors.white,
elevation: 0,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: 'Principal',
backgroundColor: Colors.transparent,
),
BottomNavigationBarItem(
icon: Icon(Icons.assignment_outlined),
label: 'Pedidos',
backgroundColor: Colors.transparent,
),
BottomNavigationBarItem(
icon: Icon(Icons.chat_outlined),
label: 'Chat',
backgroundColor: Colors.transparent,
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: 'Perfil',
backgroundColor: Colors.transparent,
),
],
),
);
}
}
then you don't have to add bottomNavigationBar to the screens individually, this way, the screen is persistent across all screens

bottomNavigationBar OnTap does not trigger for navigate to another page

#override
void initState() {
super.initState();
}
int selectedPage = 0;
void changePage(int index) {
setState(() {
selectedPage = index;
});
}
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: true,
currentIndex: selectedPage,
onTap: showPage,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Main',
backgroundColor: Colors.blue),
BottomNavigationBarItem(
icon: Icon(Icons.category_outlined),
label: 'Category',
backgroundColor: Colors.blue),
BottomNavigationBarItem(
icon: Icon(Icons.book_online),
label: 'Photos',
backgroundColor: Colors.blue),
BottomNavigationBarItem(
icon: Icon(Icons.video_call),
label: 'Video',
backgroundColor: Colors.blue),
],
Widget showPage(int selectedPage) {
if (selectedPage == 0) {
return NewsViewDetail(id: '0');
} else if (selectedPage == 1) {
return NewsLoading(text: 'load');
}
return NewsLoading(text: '1');
}
When I tap first or second item on the there is no reaction from UI. It seems onTap does not navigate to different pages.
Could you please help me why this code is not working?
Edit: I think the problem causing Scaffold body. Current Scaffold body is:
body: TabBarView(
children: [
for (final tab in filteredList)
NewsView(
id: tab.id!,
),
],
),
How can I integrate showPage(_selectedIndex), into Scaffold Body without hurt the TabbarView?
here is the TabBarController
return DefaultTabController(
// length: snapshot.data!.data!.length,
length: filteredList.length,
child: Scaffold(
appBar: AppBar(
backgroundColor: (Colors.white),
iconTheme: const IconThemeData(color: Colors.black),
title: Transform.translate(
offset: const Offset(-24.0, 0.0),
child: Image.asset("assets/images/lo.png",
fit: BoxFit.contain, height: 22),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(30.00),
child: ColoredBox(
color: Colors.white,
child: TabBar(
labelColor: Colors.purple[100],
indicatorColor: Colors.purple,
isScrollable: true,
labelPadding:
const EdgeInsets.symmetric(horizontal: 8.0),
tabs: tabs),
),
),
),
```
Check it, it will be helped you to solve your solution
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyNavigationBar(),
);
}
}
class MyNavigationBar extends StatefulWidget {
MyNavigationBar({Key key}) : super(key: key);
#override
_MyNavigationBarState createState() => _MyNavigationBarState();
}
class _MyNavigationBarState extends State<MyNavigationBar>
with TickerProviderStateMixin {
int _selectedIndex = 0;
void changePage(int index) {
setState(() {
_selectedIndex = index;
});
}
Widget showPage(int selectedPage) {
if (selectedPage == 0) {
return Container(
child: Text('Main Page',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
);
} else if (selectedPage == 1) {
return Container(
child: Text('Category page',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
);
} else if (selectedPage == 2) {
return Container(
child: Text('Photo page',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
);
}
return Container(
child: Text('video Page',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
);
}
TabController tabController;
#override
void initState() {
// TODO: implement initState
super.initState();
tabController = TabController(initialIndex: 0, length: 2, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.0),
//This is for bottom border that is needed
border:
Border(bottom: BorderSide(color: Colors.grey, width: 0.8))),
child: TabBar(
indicatorWeight: 2,
controller: tabController,
indicatorColor: Colors.purple,
labelColor: Colors.purple,
unselectedLabelColor: Color(0xff002540).withOpacity(0.7),
tabs: [
Tab(
child: Text(
"First",
),
),
Tab(
child: Text(
"Second",
),
),
],
),
),
backgroundColor: Colors.green,
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
height: MediaQuery.of(context).size.height,
child: TabBarView(controller: tabController, children: [
Container(
child: Center(
child: showPage(_selectedIndex),
),
),
Container(
child: Center(
child: showPage(_selectedIndex),
),
),
]),
),
],
)),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Main',
backgroundColor: Colors.blue),
BottomNavigationBarItem(
icon: Icon(Icons.category_outlined),
label: 'Category',
backgroundColor: Colors.blue),
BottomNavigationBarItem(
icon: Icon(Icons.book_online),
label: 'Photos',
backgroundColor: Colors.blue),
BottomNavigationBarItem(
icon: Icon(Icons.video_call),
label: 'Video',
backgroundColor: Colors.blue),
],
type: BottomNavigationBarType.shifting,
currentIndex: _selectedIndex,
selectedItemColor: Colors.black,
iconSize: 40,
onTap: changePage,
elevation: 5),
);
}
}
Output:

I wanna create two lists gridview.count, but in result i have free space in list, how do it correctly?

I try to put items from one list to two screens.
I have a list of items with categories (just part of list here):
class Sweets with ChangeNotifier {
List<Sweet> _allSweets = [
Sweet(
categories: 'Данкины',
id: '1',
images: 'assets/images/donut_pink_resize.png',
title: 'Красный вельвет',
price: 65,
rating: 4.8,
isFavorite: false,
isDonuts: true,
isBurgers: false,
),
Sweet(
categories: 'Данкины',
id: '2',
images: 'assets/images/donut_chocolate.png',
title: 'Шоколадный',
price: 75,
rating: 4.1,
isFavorite: false,
isDonuts: true,
isBurgers: false,
),
Sweet(
categories: 'Бургеры',
id: '7',
images: 'assets/images/chiken_burger.png',
title: 'Сочная курочка',
price: 125,
rating: 4.8,
isFavorite: false,
isDonuts: false,
isBurgers: true,
),
Sweet(
categories: 'Бургеры',
id: '8',
images: 'assets/images/meat_burger.png',
title: 'Мясной',
price: 65,
rating: 4.8,
isFavorite: false,
isDonuts: false,
isBurgers: true,
),
I want to create screens with several lists, first screen only with burgers:
child: GridView.count(
shrinkWrap: true,
mainAxisSpacing: 15.0,
crossAxisSpacing: 20.0,
crossAxisCount: 2,
childAspectRatio:
(SizeConfig.itemWidth / SizeConfig.itemHeight),
children: [
...List.generate(sweets.length, (index) {
if (sweets[index].isBurgers)
return ChangeNotifierProvider.value(
value: sweets[index],
child: SweetCard(),
);
return SizedBox.shrink();
}),
],
),
and second screen only with donuts:
child: GridView.count(
shrinkWrap: true,
mainAxisSpacing: 15.0,
crossAxisSpacing: 20.0,
crossAxisCount: 2,
childAspectRatio:
(SizeConfig.itemWidth / SizeConfig.itemHeight),
children: [...List.generate(sweets.length, (index) {
if (sweets[index].isDonuts)
return ChangeNotifierProvider.value(
value: sweets[index],
child: SweetCard(),
);
return SizedBox.shrink();
}),
],
),
first screen
second screen
but in result i have free space in screen, it looks like invisible items betweet different categories on the screen, how i can fix it?
Parent widget:
class HomeScreen extends StatefulWidget {
static String routeName = '/dindon_main';
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
_tabController.addListener(_handleTabSelecion);
}
void _handleTabSelecion() {
setState(() {});
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
initialIndex: 3,
child: SafeArea(
child: Scaffold(
drawer: Drawer(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.person_add),
title: Text(
'Профиль',
style: TextStyle(
fontSize: 18,
),
),
onTap: () {
Navigator.pushNamed(context, ProfileScreen.routeName);
},
),
ListTile(
leading: Icon(FontAwesomeIcons.solidHeart),
title: Text(
'Любимое',
style: TextStyle(
fontSize: 18,
),
),
onTap: () {
Navigator.pushNamed(context, FavoriteScreen.routeName);
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text(
'Настройки',
style: TextStyle(
fontSize: 18,
),
),
onTap: () {
Navigator.pushNamed(context, SettingsScreen.routeName);
},
),
ListTile(
leading: Icon(Icons.add_shopping_cart),
title: Text(
'Корзина',
style: TextStyle(
fontSize: 18,
),
),
onTap: () {
Navigator.pushNamed(context, CartScreen.routeName);
},
),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text(
'Выйти',
style: TextStyle(
fontSize: 18,
),
),
onTap: () {
context.read<AuthentificationService>().signOut();
Navigator.pushNamed(
context, AuthentificationPage.routename);
},
),
ListTile(
leading: Icon(Icons.exit_to_app),
title: Text(
'Выйти из Google',
style: TextStyle(
fontSize: 18,
),
),
onTap: () {
// ! - Добавить страницу logout google
Navigator.pushNamed(context, GoogleLogoutPage.routeName);
},
),
],
),
),
appBar: AppBar(
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
tooltip:
MaterialLocalizations.of(context).openAppDrawerTooltip,
);
},
),
actions: [
Padding(
padding: EdgeInsets.only(
top: getProportionateScreenWidth(10),
right: getProportionateScreenWidth(10),
),
child: SizedBox(
width: getProportionateScreenWidth(60),
height: getProportionateScreenHeight(60),
child: Stack(
fit: StackFit.expand,
children: [
InkWell(
onTap: () {
Navigator.pushNamed(context, ProfileScreen.routeName);
},
child: CircleAvatar(
backgroundImage:
AssetImage('assets/images/avatar_circle2.png'),
),
),
],
),
),
),
],
bottom: TabBar(
controller: _tabController,
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
tabs: [
Tab(
text: 'Пончики',
icon: SvgPicture.asset(
'assets/icons/donut32-min.svg',
color:
_tabController.index == 0 ? Colors.black : Colors.grey,
),
),
Tab(
text: 'Бургеры',
icon: SvgPicture.asset(
'assets/icons/burger_32-min.svg',
color:
_tabController.index == 1 ? Colors.black : Colors.grey,
),
),
Tab(
text: 'Блинчики',
icon: SvgPicture.asset(
'assets/icons/puncake2_32.svg',
color:
_tabController.index == 2 ? Colors.black : Colors.grey,
),
),
Tab(
text: 'Пицца',
icon: SvgPicture.asset(
'assets/icons/pizza_32.svg',
color:
_tabController.index == 3 ? Colors.black : Colors.grey,
),
),
],
),
),
backgroundColor: Color.fromRGBO(248, 219, 221, 1.0),
body: TabBarView(
// controller: _pageController,
controller: _tabController,
children: [
DonutsScreen(),
BurgersScreen(),
Icon(Icons.directions_bike),
PizzaScreen(),
],
),
),
),
);
}
}

How do you create a Flutter bottom navigation with a top line on active menu item?

I have the following Flutter bottom navigation bar
And I would like to add a top-line or boarder for active items like so
Is that even possible, my code is straight forward.
#override
Widget build(BuildContext context) {
return Scaffold(
body: _tabs[_tabIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
selectedLabelStyle: TextStyle(fontSize: 14),
selectedItemColor: Theme.of(context).accentColor,
unselectedLabelStyle: TextStyle(fontSize: 14.0),
unselectedItemColor: Color(0xff546481),
type: BottomNavigationBarType.fixed,
showSelectedLabels: true,
showUnselectedLabels: true,
currentIndex: _tabIndex,
onTap: (int index) {
setState(() {
_tabIndex = index;
});
},
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: 'HOME',
),
BottomNavigationBarItem(
icon: Icon(Icons.history_outlined),
label: 'HISTORY',
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: 'PROFILE',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings_outlined),
label: 'SETTINGS',
),
],
),
);
}
}
there are some packages can achieve this effect:
titled_navigation_bar
bottomNavigationBar: TitledBottomNavigationBar(
currentIndex: 2, // Use this to update the Bar giving a position
onTap: (index){
print("Selected Index: $index");
},
items: [
TitledNavigationBarItem(title: Text('Home'), icon: Icons.home),
TitledNavigationBarItem(title: Text('Search'), icon: Icons.search),
TitledNavigationBarItem(title: Text('Bag'), icon: Icons.card_travel),
TitledNavigationBarItem(title: Text('Orders'), icon: Icons.shopping_cart),
TitledNavigationBarItem(title: Text('Profile'), icon: Icons.person_outline),
]
)
bottom_indicator_bar
class _HomePageState extends State<HomePage> {
final List<BottomIndicatorNavigationBarItem> items = [
BottomIndicatorNavigationBarItem(icon: Icons.home),
BottomIndicatorNavigationBarItem(icon: Icons.search),
BottomIndicatorNavigationBarItem(icon: Icons.settings),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Indicator Bottom Bar"),
backgroundColor: Colors.teal,
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
],
),
),
bottomNavigationBar: BottomIndicatorBar(
onTap: (index) => {},
items: items,
activeColor: Colors.teal,
inactiveColor: Colors.grey,
indicatorColor: Colors.teal,
),
);
}
}
You can use a TabBar instead of a BottomNavigationBar using a custom decoration:
class TopIndicator extends Decoration {
#override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
return _TopIndicatorBox();
}
}
class _TopIndicatorBox extends BoxPainter {
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
Paint _paint = Paint()
..color = Colors.cyan
..strokeWidth = 5
..isAntiAlias = true;
canvas.drawLine(offset, Offset(cfg.size!.width + offset.dx, 0), _paint);
}
}
Then pass the decoration to the TapBar using TapBar(indicator: TopIndicator ...).
To use the TabBar as the Scaffold.bottomNavigationBar, you will most likely want to wrap it in a Material to apply a background color:
Scaffold(
bottomNavigationBar: Material(
color: Colors.white,
child: TabBar(
indicator: TopIndicator(),
tabs: const <Widget>[
Tab(icon: Icon(Icons.home_outlined), text: 'HOME'),
...
],
),
),
...
)
Thanks Ara Kurghinyan for the original idea.
Column(
children: [
pageIndex == 2
? Container(
height: 5.w,
width: 35.h,
alignment: Alignment.center,
// margin: const EdgeInsets.only(left: 5),
decoration: const BoxDecoration(
color: Colors.teal,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(
3,
),
bottomRight: Radius.circular(3)),
),
// height: 5,
// width: 35,
)
: SizedBox(
height: 5.w,
width: 35.h,
),
SizedBox(
height: 4.h,
),
Flexible(
child: Container(
height: 25.h,
width: 25.w,
// alignment: Alignment.lerp(
// Alignment.bottomLeft, Alignment.bottomRight, 1),
decoration: BoxDecoration(
// color: Colors.red,
border: Border.all(
width: 1,
color: Colors.black,
),
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
),
child: IconButton(
iconSize: 25,
alignment: Alignment.center,
padding: const EdgeInsets.only(top: 4, bottom: 4),
visualDensity:
const VisualDensity(horizontal: -4, vertical: -4),
enableFeedback: false,
onPressed: () {
setState(() {
pageIndex = 2;
});
},
icon: pageIndex == 2
? Image.asset(
'assets/icons/icons8-alarm-50.png',
height: 24.h,
color: Colors.black,
// size: 35.sm,
)
: Image.asset(
'assets/icons/icons8-alarm-50.png',
color: Colors.black,
// size: 35.sm,
)),
),
),
pageIndex == 2
? Container(
height: 15.h,
width: 60.w,
alignment: Alignment.lerp(
Alignment.centerLeft, Alignment.centerRight, 0.75),
child: Text(
'Reminder',
style: GoogleFonts.lato(
textStyle: const TextStyle(
fontSize: 12, fontWeight: FontWeight.bold),
),
),
)
: Container(),
],
),