Sorry if this sounds a bit obvious but this framework is still fairly unknown to me but I have created a bunch of other pages in my app (saved as .dart files) and also have created paths to them through the use of a drawer. I now have have a bottomNavigationBar that has 5 tabs. I want to be able to route to my previously created page using the tabs. I am currently using the defaultTabController.
bottomNavigationBar: Container(
color: Colors.grey[200],
child: Container(
margin: EdgeInsets.symmetric(vertical: 16),
child: TabBar(
isScrollable: true
labelColor: Colors.black,
labelPadding: EdgeInsets.fromLTRB(21, 0, 21, 16),
unselectedLabelColor: Colors.black45,
indicatorColor: Colors.orange,
indicatorPadding: EdgeInsets.fromLTRB(21, 0, 21, 16),
labelStyle: TextStyle(
fontSize: 16, fontWeight: FontWeight.bold
),
tabs: <Tab>[
new Tab(
text: "Home",
),
new Tab(
text: "Page 1",
),
new Tab(
text: "Page 2",
),
new Tab(
text: "Page 3",
),
new Tab(
text: "Page 4",
),
],
),
),
),
For example, when someone clicks on the "Page 1" tab, I want to be able to route the user to my "page1.dart" file. Thanks in advance!
You do not need TabBar if you're using bottomNavigationBar. You can implement bottomNavigationBar by code below.
int currentIndex = 0;
List<Widget> children = [
Home();
Page1();
];
onTapped(int index){
setState(() {
currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
onTap: onTapped,
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(icon: Icon(Icons.add), title: Text('Page 1'))
]),
body: children[currentIndex]);
}
currentIndex will keep the record of the current opened tab. children is the List of pages you want to route in body. onTapped will change currentIndex to index of Navigation Bar.
Related
Following is my code to handle TabBar in Flutter
TabBar(controller: tabController, indicatorColor: white, tabs: [
Tab(
child: Text(
"Present",
style: TextStyle(fontFamily: "BarlowBold", color: black),
),
),
Tab(
child: Text(
"Upcoming",
style: TextStyle(fontFamily: "BarlowBold", color: black),
),
)
]),
I need to change the fontFamily of text depending on which tab is selected. I tried the properties of tabController but they do not help
Same can be done with - labelStyle: & unselectedLabelStyle: of TabBar
TabBar(
indicatorColor: Colors.white,
labelStyle: TextStyle(fontSize: 22.0,fontFamily: 'Family Name'), //For Selected tab
unselectedLabelStyle: TextStyle(fontSize: 10.0,fontFamily: 'Family Name'), //For Un-selected Tabs
tabs: [
I'm on the home tab and I need to shift to my second tab with the CupertinoTabBar. I do not have tabcontroller to shift with a global key like the material tab bar. Can you please suggest options ?
I have tried using a global key and changing the currentIndex of the tab. However, no luck. Have ran out of options.
HomeScreen.dart - Containing the tab
```Widget build(BuildContext context) {
return new CupertinoTabScaffold(
tabBar: CupertinoTabBar(
key: HomeScreen._myTabbedPageKey,
backgroundColor: Colors.black,
currentIndex: _currentIndex,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: new Icon(Icons.home, color: Colors.white),
activeIcon: new Icon(Icons.home,
color: Color.fromARGB(198, 39, 169, 227)),
title: new Text(
'Home',
style: TextStyle(color: Color.fromARGB(198, 39, 169, 227)),
)),
BottomNavigationBarItem(
icon: new Icon(Icons.track_changes, color: Colors.white),
activeIcon: new Icon(Icons.track_changes,
color: Color.fromARGB(198, 39, 169, 227)),
title: new Text(
'Trips',
style: TextStyle(color: Color.fromARGB(198, 39, 169, 227)),
)),
BottomNavigationBarItem(
icon: new Icon(Icons.traffic, color: Colors.white),
activeIcon: new Icon(Icons.traffic,
color: Color.fromARGB(198, 39, 169, 227)),
title: new Text('Track',
style: TextStyle(color: Color.fromARGB(198, 39, 169, 227)))),
BottomNavigationBarItem(
icon: new Icon(Icons.settings, color: Colors.white),
activeIcon: new Icon(Icons.settings,
color: Color.fromARGB(198, 39, 169, 227)),
title: new Text('Settings',
style: TextStyle(color: Color.fromARGB(198, 39, 169, 227))))
],
),
tabBuilder: (BuildContext context, int index) {
if (_currentIndex != -1) {
_currentIndex = index;
return _children[_currentIndex];
} else {
return _children[index];
}
},
);
}```
HomeTab.dart - Containing the 'SEE MORE TRIPS' button. Click on the button should take me to second tab
Widget build(BuildContext context) {
return new CupertinoPageScaffold(
backgroundColor: Colors.white,
navigationBar: new CupertinoNavigationBar(
middle: title,
backgroundColor: Colors.black,
automaticallyImplyLeading: false,
),
child: new RefreshIndicator(
key: _homeRefreshIndicatorKey,
onRefresh: _refresh,
child: new SingleChildScrollView(
child: new Container(
child: new Center(
child: new Column(
// center the children
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CupertinoButton(
color: Color.fromARGB(198, 39, 169, 227),
disabledColor: Colors.grey,
child: Text('SEE MORE TRIPS',
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontFamily: 'Lato')),
onPressed: () {
//to call second tab
}),
new SizedBox(
height: 16,
)
],
),
),
),
)));
}```
To be navigated to second tab
You should use CupertinoTabController to change the tabbar index as expected , passing the instance of CupertinoTabController to CupertinoTabScaffold property of controller.
Finally, change the index programatically.
setState(() {
_tabController.index = 0;
});
i am trying to use the whats new package in my flutter application but I change th version and nothing happens, i implemmented it in the initstate of the class like this .Is it right??:
void initState() {
super.initState();
WhatsNewPage(
title: Text(
"What's New",
textScaleFactor: 1.0,
textAlign: TextAlign.center,
style: TextStyle(
// Text Style Needed to Look like iOS 11
fontSize: 22.0,
fontWeight: FontWeight.bold,
),
),
buttonText: Text(
'Continue',
textScaleFactor: 1.0,
style: TextStyle(
color: Colors.white,
),
),
items: <ListTile>[
ListTile(
leading: Icon(Icons.color_lens),
title: Text(
'Dark Theme',
textScaleFactor: 1.0,
), //Title is the only Required Item
subtitle: Text(
'Black and grey theme (Tap to Change)',
textScaleFactor: 1.0,
),
onTap: () {
// You Can Navigate to Locations in the App
Navigator.of(context).pop();
},
),
], //Required
home: HomePage(),
showNow: false,
showOnVersionChange: true,
);}
Ok, it seems like the documentation is not clear, you can use that widget inside your build method, like my example below, replace NewPage() widget by the widget you want use after user press 'continue'
class TestingPackage extends StatefulWidget {
#override
TestingPackageState createState() => TestingPackageState();
}
class TestingPackageState extends State<TestingPackage> {
double textScaleFactor = 1.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: WhatsNewPage(
title: Text(
"What's New",
textScaleFactor: textScaleFactor,
textAlign: TextAlign.center,
style: TextStyle(
// Text Style Needed to Look like iOS 11
fontSize: 22.0,
fontWeight: FontWeight.bold,
),
),
buttonText: Text(
'Continue',
textScaleFactor: textScaleFactor,
style: TextStyle(
color: Colors.white,
),
),
// Create a List of WhatsNewItem for use in the Whats New Page
// Create as many as you need, it will be scrollable
items: <ListTile>[
ListTile(
leading: Icon(Icons.color_lens),
title: Text(
'Dark Theme',
textScaleFactor: textScaleFactor,
), //Title is the only Required Item
subtitle: Text(
'Black and grey theme (Tap to Change)',
textScaleFactor: textScaleFactor,
),
onTap: () {
// You Can Navigate to Locations in the App
Navigator.of(context).pushNamed("/settings");
},
),
ListTile(
leading: Icon(Icons.map),
title: Text(
'Google Maps',
textScaleFactor: textScaleFactor,
),
subtitle: Text(
'Open Address Links in Google Maps instead of Apple Maps (Tap to Change)',
textScaleFactor: textScaleFactor,
),
onTap: () {
// You Can Navigate to Locations in the App
Navigator.of(context).pushNamed("/settings");
},
),
ListTile(
leading: Icon(Icons.person_outline),
title: Text(
'Loan Contacts Enhancements',
textScaleFactor: textScaleFactor,
),
subtitle: Text(
'Updated look for faster navigation',
textScaleFactor: textScaleFactor,
),
onTap: () {
WhatsNewPage.showDetailPopUp(
context,
'Info',
"Navigate to any loan then select the bottom right icon to go to the contacts. You can press the dropdown arrow for contact information.",
);
},
),
], //Required
home:
NewPage(), // Where the Button will Navigate (Usually the Login or Home Screen)
showNow:
false, // Show now regarless of version change (Useful for showing from the main menu)
showOnVersionChange:
true, //Show only if the version changes or the user reinstalls the app
),
);
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Text("new page"),
),
);
}
}
so i have this flutter app, and i'm trying to hide or remove the title. I tried leaving the title as an empty string i.e. new Text("") but that messed with the alignment of the navbar.
Desired Outcome:
What i'm getting (if i leave the title as empty string):
:
There are two workarounds for this problem, as this feature is not yet implemented.
Pass Container(height: 0.0) instead of Text("")
Create widget and use it instead of Flutter's bottom navigation. Source.
Update:
Just add this to your BottomNavigationBar
showSelectedLabels: false,
showUnselectedLabels: false,
Now you can simply disable labels for selected or unselected items:
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: false, // <-- HERE
showUnselectedLabels: false, // <-- AND HERE
items: [
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Personal')
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications),
title: Text('Notifications'),
),
]
...
)
...resulting in:
Hopefully, this snippet helps someone. It worked well for me.
bottomNavigationBar : new BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icons.search,
title: Padding(padding: EdgeInsets.all(0))
),
BottomNavigationBarItem(
icon: Icons.share,
title: Padding(padding: EdgeInsets.all(0))
),
BottomNavigationBarItem(
icon: Icons.call,
title: Padding(padding: EdgeInsets.all(0))
)],
type: BottomNavigationBarType.fixed
)
//bottomNavBar
In the new version of flutter, the title is depreciated, and we must provide the label.
So, make the label an empty string
BottomNavigationBarItem(
label: '',
icon: Icon(
Icons.home_rounded,
color: kHintColor,
size: 35,
),
activeIcon: Icon(
Icons.home_rounded,
color: kMainColor,
size: 35,
),
),
and add the following to the BottomNavigationBar:
selectedLabelStyle: TextStyle(fontSize: 0),
unselectedLabelStyle: TextStyle(fontSize: 0),
As of now, this feature is not implement. For a BottomNavigationBarItem, title is a required field
But you can build a new widget for this.
Try this :
Column buildButtonColumn(IconData icon) {
Color color = Theme.of(context).primaryColor;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color),
],
);
}
Widget buttonSection = Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
buildButtonColumn(Icons.call),
buildButtonColumn(Icons.near_me),
buildButtonColumn(Icons.share),
],
),
);
I have tried this approach and it works like charm:
BottomNavigationBarItem(
icon: Icon(
Icons.home,
size: 40,
),
title: Text(
"",
style: TextStyle(fontSize: 0),
),
)
Use BottomAppBar to achieve this BottomNavigationBarItem without label. You could further customize it.
#override
Widget build(BuildContext context) {
return Scaffold(
body: body,
bottomNavigationBar: new BottomAppBar(
child: new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(
icon: Icon(Icons.home),
disabledColor: Colors.green,
onPressed: _currentIndex == 0
? null
: () => setState(() => _currentIndex = 0)),
IconButton(
icon: Icon(Icons.notifications),
disabledColor: Colors.green,
onPressed: _currentIndex == 1
? null
: () => setState(() => _currentIndex = 1)),
IconButton(
icon: Icon(Icons.settings),
disabledColor: Colors.green,
onPressed: _currentIndex == 2
? null
: () => setState(() => _currentIndex = 2)),
],
)
),
);
}
Hope it really helps.
It worked well for me.
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
title: SizedBox.shrink(),
)
title: Container(height: 0.0)
will give you some extra padding below.
You can use
title: Text(
'',
style: TextStyle(fontWeight: FontWeight.bold, height: 0.0),
)
One can use bottom navigation bar type to shifting.
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
items: [
BottomNavigationBarItem(icon: Icon(Icons.star, color: Colors.red,), title: Text("")),
BottomNavigationBarItem(icon: Icon(Icons.star, color: Colors.red,), title: Text(""))
]
),
To display icons without any labels, below step worked for me:
Set selectedFontSize: 0 and set label to empty string. For example,
BottomNavigationBar(
selectedFontSize: 0,
items: BottomNavigationBarItem(
icon: Icons.search
label: '',
),
)
I want 2 tab pages with a ListView each to share a single RefreshIndicator. However, a RefreshIndicator must have Scrollable as a child (which a TabBarView isn't) so instead I tried making 2 RefreshIndicators per tab as shown in the code below.
But this brings a different problem, I also wanted a floating AppBar which meant I had to use a NestedScrollView. So as a result I end up triggering both RefreshIndicators' onRefresh method whenever I scroll down. Whereas I only need one to refresh.
import 'package:flutter/material.dart';
main() {
runApp(
MaterialApp(
home: DefaultTabController(
length: 2,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
floating: true,
snap: true,
bottom: TabBar(
tabs: [
Tab(text: 'Page1'),
Tab(text: 'Page2'),
],
),
),
];
},
body: TabBarView(
children: [
Page(1),
Page(2),
],
),
),
),
),
),
);
}
class Page extends StatefulWidget {
final pageNumber;
Page(this.pageNumber);
createState() => PageState();
}
class PageState extends State<Page> with AutomaticKeepAliveClientMixin {
get wantKeepAlive => true;
build(context){
super.build(context);
return RefreshIndicator(
onRefresh: () => Future(() async {
print('Refreshing page no. ${widget.pageNumber}'); // This prints twice once both tabs have been opened
await Future.delayed(Duration(seconds: 5));
}),
child: ListView.builder(
itemBuilder: ((context, index){
return ListTile(
title: Text('Item $index')
);
}),
)
);
}
}
The AutomaticKeepAliveClientMixin is there to prevent the pages rebuilding every time I switch tabs as this would be an expensive process in my actual app.
A solution that uses a single RefreshIndicator for both tabs would be most ideal, but any help is appreciated.
DefaultTabController(
length: tabs.length,
child: RefreshIndicator(
notificationPredicate: (notification) {
// with NestedScrollView local(depth == 2) OverscrollNotification are not sent
return notification.depth == 2;
},
onRefresh: () => Future.value(null),
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(...)
];
},
body: TabBarView(
children: tabs,
),
),
),
)
Could wrap whole NestedScrollView with RefreshIndicator and update notificationPredicate:
DefaultTabController(
length: tabs.length,
child: RefreshIndicator(
notificationPredicate: (notification) {
// with NestedScrollView local(depth == 2) OverscrollNotification are not sent
if (notification is OverscrollNotification || Platform.isIOS) {
return notification.depth == 2;
}
return notification.depth == 0;
},
onRefresh: () => Future.value(null),
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(...)
];
},
body: TabBarView(
children: tabs,
),
),
),
)
If you want floating app bar then you have to use nested scroll view and sliver app bar . When you try to use refresh indicator in a list which a child of tab bar view , refresh indicator doesn't work. This is just because of the nested scroll view .
If you have suppose two lists as child of tab bar view, you want to refresh only one or both at a time then follow the below code.
Wrap the nested scroll view with refresh indicator then on refresh part ,
RefreshIndicator(
color: Colors.red,
displacement: 70,
onRefresh: _refreshGeneralList,
key: _refreshIndicatorKey,
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
centerTitle: true,
title: Text(
"App Bar",
style: TextStyle(
color: Colors.black,
fontSize: 14,
),
leading: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: IconButton(
icon: Icon(Icons.profile),
onPressed: () {
Navigator.of(context).pop();
},
),
),
actions: [
InkWell(
onTap: () {
setState(() {
isPremium = !isPremium;
});
},
child: Icon(
Icons.monetization_on,
color: isPremium ? Colors.green : Colors.blueGrey,
size: 33,
)),
SizedBox(
width: 25,
)
],
elevation: 0,
backgroundColor: Colors.white,
pinned: true,
floating: true,
forceElevated: innerBoxIsScrolled,
bottom: isPremium
? TabBar(
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600),
labelColor: Colors.blueGrey,
indicatorColor:Colors.red,
unselectedLabelColor:
Colors.green,
labelPadding: EdgeInsets.symmetric(vertical: 13.5),
controller: _tabController,
tabs: [
Text(
"General",
),
Text(
"Visitors",
),
])
: null,
)
];
},
body: isPremium
? TabBarView(
controller: _tabController,
children: [
generalNotificationsList(context),
visitorsNotificationsList(context),
])
: generalNotificationsList(context),
),
),
add a function which calls a future. In the future part we will write the code if one child or two child of tab bar view will be scrolled.
Future _refreshGeneralList() async{
print('refreshing ');
GeneralNotificationBloc().add(LoadGeneralNotificationEvent(context));
PremiumNotificationBloc().add(LoadPremiumNotificationEvent(context));
return Future.delayed(Duration(seconds: 1));
}