Just like here when I click on the timer, the Bottom navigation bar was disappeared. I want to implement the same thing on flutter. Whenever I click on Bottom Navigation Bar Item, for the new screen the Bottom Navigation Bar should not appear.
Here is my code. My Bottom Navigation Bar has four items and I want to hide the bottom navigation bar when I route to a new screen.
class MyFeedScreen extends StatefulWidget {
#override
_MyFeedScreenState createState() => _MyFeedScreenState();
}
class _MyFeedScreenState extends State<MyFeedScreen> {
int _bottomNavIndex = 0;
Widget pageCaller(int index) {
switch (index) {
case 0:
{
return Category();
}
case 1:
{
return Feed();
}
case 3:
{
return Settings();
}
}
}
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
body: pageCaller(_bottomNavIndex),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: klogoBlue,
selectedItemColor: Color(0xfff5f5f5),
unselectedItemColor: Color(0xfff5f5f5),
selectedFontSize: 12.0,
type: BottomNavigationBarType.fixed,
currentIndex: _bottomNavIndex,
onTap: (index) {
setState(() {
_bottomNavIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Padding(
padding:
EdgeInsets.only(top: SizeConfig.blockSizeVertical * 0.60),
child: Icon(Icons.category),
),
title: Padding(
padding: EdgeInsets.symmetric(
vertical: SizeConfig.blockSizeVertical * 0.60),
child: Text('Category'),
),
),
BottomNavigationBarItem(
icon: Padding(
padding:
EdgeInsets.only(top: SizeConfig.blockSizeVertical * 0.60),
child: Icon(FontAwesomeIcons.newspaper),
),
title: Padding(
padding: EdgeInsets.symmetric(
vertical: SizeConfig.blockSizeVertical * 0.60),
child: Text('My Feed'),
),
),
BottomNavigationBarItem(
icon: Padding(
padding:
EdgeInsets.only(top: SizeConfig.blockSizeVertical * 0.60),
child: Icon(Icons.refresh),
),
title: Padding(
padding: EdgeInsets.symmetric(
vertical: SizeConfig.blockSizeVertical * 0.60),
child: Text('Refresh'),
),
),
BottomNavigationBarItem(
icon: Padding(
padding:
EdgeInsets.only(top: SizeConfig.blockSizeVertical * 0.60),
child: Icon(Icons.settings),
),
title: Padding(
padding: EdgeInsets.symmetric(
vertical: SizeConfig.blockSizeVertical * 0.60),
child: Text('Settings'),
),
),
],
),
);
}
}
You can try this method
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(
builder: (_) => NewScreen(),
),
);
You can copy paste run full code below
You can check onTap index and do Navigator.push for specific button
code snippet
void _onItemTapped(int index) {
if (index != 2) {
setState(() {
_bottomNavIndex = index;
});
print(_bottomNavIndex);
} else {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Settings()),
);
}
}
Widget pageCaller(int index) {
switch (index) {
case 0:
{
return Category();
}
case 1:
{
return Feed();
}
}
}
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _bottomNavIndex = 0;
void _onItemTapped(int index) {
if (index != 2) {
setState(() {
_bottomNavIndex = index;
});
print(_bottomNavIndex);
} else {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Settings()),
);
}
}
Widget pageCaller(int index) {
switch (index) {
case 0:
{
return Category();
}
case 1:
{
return Feed();
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: pageCaller(_bottomNavIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Category'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Feed'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('Settings'),
),
],
currentIndex: _bottomNavIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
class Category extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Category"),
),
);
}
}
class Feed extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Feed"),
),
);
}
}
class Settings extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
),
body: Center(
child: Text("Settings"),
),
);
}
}
Related
This is the main page where I have written the bottom bar navigation code. I have run the code, but the bar does not appear on my home page. It does not give me any errors. which means the code is fine and i just need to call it properly. How do I call the function so it displays across all other pages?
import 'package:thehunt/views/hunt/profile_view.dart';
import 'package:thehunt/views/hunt/settings_view.dart';
import 'package:thehunt/views/login_view.dart';
class HuntView extends StatelessWidget {
const HuntView({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomeView();
} else {
return AuthView();
}
},
),
);
}
}
//bottom navigation bar
class BottomNavView extends StatefulWidget {
const BottomNavView({super.key});
#override
State<BottomNavView> createState() => _BottomNavViewState();
}
class _BottomNavViewState extends State<BottomNavView> {
List views = [
const HomeView(),
const CurrentLocationView(),
const ProfileView(),
const SettingsView()
];
int currentIndex = 0;
void onTap(int index) {
currentIndex = index;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: views[currentIndex],
bottomNavigationBar: BottomNavigationBar(
unselectedFontSize: 0,
selectedFontSize: 0,
type: BottomNavigationBarType.fixed,
backgroundColor: Color.fromARGB(255, 198, 176, 235),
onTap: onTap,
currentIndex: currentIndex,
selectedItemColor: Colors.black54,
unselectedItemColor: Colors.grey.withOpacity(0.5),
showUnselectedLabels: false,
showSelectedLabels: false,
elevation: 0,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
label: 'Home',
icon: Icon(Icons.home_outlined),
),
BottomNavigationBarItem(
label: 'Map',
icon: Icon(Icons.map),
),
BottomNavigationBarItem(
label: 'Settings',
icon: Icon(Icons.settings),
),
BottomNavigationBarItem(
label: 'Profile',
icon: Icon(Icons.person),
),
],
),
);
}
}
Below is my home page view where I want the bottom navigation bar to be displayed
class HomeView extends StatefulWidget {
const HomeView({super.key});
#override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final user = FirebaseAuth.instance.currentUser!;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(' Home'),
backgroundColor: Colors.deepPurple[200],
elevation: 0,
),
drawer: const NavigationDrawer(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Logged In as: ' + user.email!),
MaterialButton(
onPressed: () {
FirebaseAuth.instance.signOut();
},
color: Colors.deepPurple[200],
child: Text('sign out'),
),
MaterialButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return const CurrentLocationView();
},
),
);
},
color: Colors.deepPurple[200],
child: const Text('User location')),
],
),
),
);
}
}
Call BottomNavView in HuntView instead of HomeView as below code
class HuntView extends StatelessWidget {
const HuntView({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return BottomNavView(); <------- call here BottomNavView
} else {
return AuthView();
}
},
),
);
}
}
I'm using the BottomNavigationBar + the BottomNavigationBarItem widget to switch through pages within one scaffold. My problem:
I'm also using a Drawer to give navigation options. I was able to use the same list of pages I use with the bottom navigation bar in the drawer. That works. But: I now want to use the drawer to offer more pages than in the bottom bar and as soon as I want to set the index to those pages, the BottomNavigationBar throws an error:
'package:flutter/src/material/bottom_navigation_bar.dart': Failed
assertion: line 192 pos 15: '0 <= currentIndex && currentIndex <
items.length': is not true.
What seems to happen is that the BottomNavigationBar is keeping me from using more pages than connected to the Bottom Bar. Is there anay way around this? I don't want more than 4 symbols in the bottom bar, but I want 5 pages; and if possible all of them in the same Scaffold. Thanks!
For making this you need to manage variables logically. Here is a complete example.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: (settings) {
switch (settings.name) {
case HomePage.route:
return MaterialPageRoute(
builder: (context) => Scaffold(
body: Container(
color: Colors.amber,
child: const Text("home"),
),
),
settings: const RouteSettings(name: HomePage.route));
}
},
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
static const String route = "/home";
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _pageIndex = 0;
int _drawerIndex = 0;
List<int> screenStack = [0];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("page $_pageIndex"),
),
body: SafeArea(
child: WillPopScope(
onWillPop: () async {
if (screenStack.length > 1) {
setState(() {
screenStack.removeLast();
_pageIndex = screenStack[screenStack.length - 1];
});
return false;
}
return true;
},
child: IndexedStack(
index: (_drawerIndex < _pageIndex) ? _pageIndex : _drawerIndex,
children: <Widget>[
Container(
color: Colors.amber,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Scaffold(
body: Center(
child: Text("DetailsPage"),
),
)));
},
child: const Text("navigate"),
),
const Text('Home'),
],
),
),
),
Container(
color: Colors.green,
child: const Center(child: Text('Business')),
),
Container(
color: Colors.amber,
child: const Center(child: Text('Technology')),
),
Container(
color: Colors.blueAccent,
child: const Center(child: Text('Education')),
),
Container(
color: Colors.deepOrange,
child: const Center(child: Text('Others')),
),
],
),
),
),
drawer: Drawer(
child: Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
children: [
ListTile(
onTap: () {
Navigator.of(context).pop();
setState(
() {
_pageIndex = 0;
_drawerIndex = _pageIndex;
if (_pageIndex == 0) {
screenStack = [0];
} else if (!screenStack.contains(_pageIndex)) {
screenStack.add(_pageIndex);
}
},
);
},
title: const Text("Home"),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
setState(
() {
_pageIndex = 1;
_drawerIndex = _pageIndex;
if (_pageIndex == 0) {
screenStack = [0];
} else if (!screenStack.contains(_pageIndex)) {
screenStack.add(_pageIndex);
}
},
);
},
title: const Text("Business"),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
setState(
() {
_pageIndex = 2;
_drawerIndex = _pageIndex;
if (_pageIndex == 0) {
screenStack = [0];
} else if (!screenStack.contains(_pageIndex)) {
screenStack.add(_pageIndex);
}
},
);
},
title: const Text("Technology"),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
setState(
() {
_pageIndex = 3;
_drawerIndex = _pageIndex;
if (_pageIndex == 0) {
screenStack = [0];
} else if (!screenStack.contains(_pageIndex)) {
screenStack.add(_pageIndex);
}
},
);
},
title: const Text("Education"),
),
ListTile(
onTap: () {
Navigator.of(context).pop();
setState(
() {
_drawerIndex = 4;
},
);
},
title: const Text("Others"),
)
],
),
),
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.computer),
label: 'Technology',
),
BottomNavigationBarItem(
icon: Icon(Icons.book),
label: 'Education',
),
],
currentIndex: _pageIndex,
onTap: (int index) {
setState(
() {
_pageIndex = index;
_drawerIndex = _pageIndex;
if (_pageIndex == 0) {
screenStack = [0];
} else if (!screenStack.contains(_pageIndex)) {
screenStack.add(_pageIndex);
}
},
);
},
),
);
}
}
// ignore: must_be_immutable
class DetailRoute extends StatelessWidget {
late TextEditingController? textEditingController;
int? index;
DetailRoute({Key? key, this.textEditingController, this.index})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container();
}
}
Try this approach-
class BottomNavBar extends StatefulWidget {
const BottomNavBar({Key? key}) : super(key: key);
#override
_BottomNavBarState createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int pageIndex = 0;
List<Widget> pageList = <Widget>[Home(), Profile(), Setting()];
#override
Widget build(BuildContext context) {
return Scaffold(
body: pageList[pageIndex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.redAccent[400],
currentIndex: pageIndex,
onTap: (value) {
setState(() {
pageIndex = value;
});
},
// type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
activeIcon: Icon(
Icons.home,
color: AppColors.black,
),
icon: Icon(
Icons.home,
color: AppColors.grey,
),
label: ""),
BottomNavigationBarItem(
activeIcon: Icon(
Icons.person,
color: AppColors.black,
),
icon: Icon(
Icons.person,
color: AppColors.grey,
),
label: ""),
BottomNavigationBarItem(
activeIcon: Icon(
Icons.settings,
color: AppColors.black,
),
icon: Icon(
Icons.settings,
color: AppColors.grey,
),
label: ""),
]));
}
}
I am trying to see how I can change the items in BottomNavigationBar within a DefaultTabController on real time but came to no avail.
Basically these are the scenario:
Non-admin Tab Bar: [Screen A and B]
Admin Tab Bar: [Screen C, D and B]
Within screen B, there is a button to change from Admin to non-admin view or vice versa.
Thank you in advance.
Try this, it works. But, I would advise using a Provider rather than passing a WidgetState as an argument to any function or to any Widget.
List<Widget> nonAdminWidgets(_BottomNavScaffoldState parent) {
return <Widget>[
ProfilePage(parent),
ClothesPage(),
ColorsPage(),
];
}
List<Widget> adminWidgets(_BottomNavScaffoldState parent) {
return <Widget>[
ProfilePage(parent),
IdeasPage(),
];
}
int _currentIndex = 0;
bool isAdmin = true;
List<dynamic> nonAdminNavBars = [
BottomNavigationBarItem(
title: Text('Profile'),
icon: Icon(Icons.face_rounded),
),
BottomNavigationBarItem(
title: Text('Clothes'),
icon: Icon(Icons.design_services_rounded),
),
BottomNavigationBarItem(
title: Text('Colors'),
icon: Icon(Icons.colorize_rounded),
),
];
List<dynamic> adminNavBars = [
BottomNavigationBarItem(
title: Text('Profile'),
icon: Icon(Icons.face_rounded),
),
BottomNavigationBarItem(
title: Text('Ideas'),
icon: Icon(Icons.lightbulb_outline_rounded),
),
];
class BottomNavScaffold extends StatefulWidget {
#override
_BottomNavScaffoldState createState() => _BottomNavScaffoldState();
}
class _BottomNavScaffoldState extends State<BottomNavScaffold> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: isAdmin ? adminWidgets(this)[_currentIndex] : nonAdminWidgets(this)[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
backgroundColor: Colors.orangeAccent,
selectedItemColor: Colors.white,
onTap: (value) {
_currentIndex = value;
setState(() {});
},
items: isAdmin ? adminNavBars : nonAdminNavBars,
),
);
}
}
class ClothesPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
"Clothes",
),
),
);
}
}
class ColorsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
"Colors",
),
),
);
}
}
class IdeasPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Text(
"Ideas",
),
),
);
}
}
class ProfilePage extends StatelessWidget {
_BottomNavScaffoldState parent;
ProfilePage(this.parent);
#override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
isAdmin = !isAdmin;
_currentIndex = 0;
parent.setState(() {});
},
child: Text("Change"),
);
}
}
I am stuck here for the past 20 days in returning data in my app from the other screen. I'm new to programming and need help. I've been searching through all the internet to find an answer related to my query but nothing is helping though. I ask my fellow SO guys to please help.
You can look at the entire code which I've made open here.
My code:
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () async {
final newList = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
setState(() {
return ListView.builder(
itemCount: newList.length,
itemBuilder: (context, index){
return Container(
child: Text('item: $newList'),
);
},
);
});
},
)
],
),
);
}
}
The screen where Navigator.pop() is used:
final Set saved = Set();
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
value: saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// __saved.add(context); // changed
// } else{ // changed
// __saved.remove(context); // changed
// } // changed
if (val == true) {
saved.add(index);
} else {
saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text(nameOfSite[index]),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
),
);
}
}
Here is the SecondPage and FavoriteList that I made
import 'package:flutter/material.dart';
import 'package:aioapp2/lists.dart';
Set<int> favorites = {};
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
_getFavoriteList(),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FloatingActionButton(
child: Icon(
Icons.edit,
color: Colors.blue,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditFavorites(),
),
).then((updatedFavorites) {
if (updatedFavorites != null)
setState(() {
favorites = updatedFavorites;
});
});
},
),
),
)
],
);
}
Widget _getFavoriteList() {
if (favorites?.isNotEmpty == true)
return _FavoriteList();
else
return _EmptyFavoriteList();
}
}
class _EmptyFavoriteList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Add Your Favorite Sites Here!❤',
style: TextStyle(color: Colors.white),
),
Icon(
Icons.favorite,
size: 150,
color: Colors.blue[100],
),
],
),
),
),
),
],
);
}
}
class _FavoriteList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: favorites.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('lib/images/${images[index]}'),
),
title: Text(nameOfSite[favorites.elementAt(index)]),
);
},
);
}
}
//Its FavoriteList Page. I changed the name
class EditFavorites extends StatefulWidget {
#override
_EditFavoritesState createState() => _EditFavoritesState();
}
class _EditFavoritesState extends State<EditFavorites> {
final _editableFavorites = <int>{};
#override
void initState() {
_editableFavorites.addAll(favorites);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red,
actions: <Widget>[
IconButton(
icon: Icon(Icons.done),
onPressed: () {
Navigator.pop<Set>(context, _editableFavorites);
},
)
],
),
//backgroundColor: Colors.indigo,
body: SafeArea(
child: ListView.builder(
itemCount: nameOfSite.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('lib/images/${images[index]}'),
),
title: Text(nameOfSite[index]),
trailing: IconButton(
icon: _editableFavorites.contains(index)
? Icon(
Icons.favorite,
color: Colors.red,
)
: Icon(
Icons.favorite_border,
color: Colors.grey,
),
onPressed: () {
setState(() {
if (_editableFavorites.contains(index))
_editableFavorites.remove(index);
else
_editableFavorites.add(index);
});
},
),
);
},
),
),
);
}
}
Just replace secondtab.dart with this code.
You can copy paste run full code below
You have to move out return ListView to the same layer with FloatingActionButton
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SecondPage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
Set newList = {};
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(30),
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
ListView.builder(
itemCount: newList.length,
itemBuilder: (context, index) {
return Container(
child: Text('item: ${newList.elementAt(index)}'),
);
},
),
FloatingActionButton(
child: Icon(
Icons.add,
color: Colors.blue,
),
onPressed: () async {
newList = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteList(),
),
);
setState(() {});
},
)
],
),
);
}
}
final Set saved = Set();
class FavoriteList extends StatefulWidget {
#override
_FavoriteListState createState() => _FavoriteListState();
}
class _FavoriteListState extends State<FavoriteList> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add to Favorites!'),
centerTitle: true,
backgroundColor: Colors.red),
body: SafeArea(
child: ListView.builder(
itemCount: 53,
itemBuilder: (context, index) {
return CheckboxListTile(
activeColor: Colors.red,
checkColor: Colors.white,
value: saved.contains(index),
onChanged: (val) {
setState(() {
// isChecked = val; // changed
// if(val == true){ // changed
// __saved.add(context); // changed
// } else{ // changed
// __saved.remove(context); // changed
// } // changed
if (val == true) {
saved.add(index);
} else {
saved.remove(index);
}
});
},
title: Row(
children: <Widget>[
//Image.asset('lib/images/${images[index]}'),
SizedBox(
width: 10,
),
Text('nameOfSite[index]'),
],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
foregroundColor: Colors.red,
child: Icon(Icons.check),
onPressed: () {
Navigator.pop<Set>(context, saved);
},
),
);
}
}
I have a list in the body and bottom navigation bar. I want to hide bottom navigation bar with a slide down animation when the posts list is scrolled down and visible with a slide up animation when scrolled up. How to do it?
While Naveen's solution works perfectly, I didn't like the idea of using setState to handle the visibility of the navbar. Every time the user would change scroll direction, the entire homepage including the appbar and body would rebuild which can be an expensive operation. I created a separate class to handle the visibility that uses a ValueNotifier to track the current hidden status.
class HideNavbar {
final ScrollController controller = ScrollController();
final ValueNotifier<bool> visible = ValueNotifier<bool>(true);
HideNavbar() {
visible.value = true;
controller.addListener(
() {
if (controller.position.userScrollDirection ==
ScrollDirection.reverse) {
if (visible.value) {
visible.value = false;
}
}
if (controller.position.userScrollDirection ==
ScrollDirection.forward) {
if (!visible.value) {
visible.value = true;
}
}
},
);
}
void dispose() {
controller.dispose();
visible.dispose();
}
}
Now all you do is create a final instance of HideNavbar in your HomePage widget.
final HideNavbar hiding = HideNavbar();
Now pass the instance's ScrollController to the ListView or CustomScrollView body of your Scaffold.
body: CustomScrollView(
controller: hiding.controller,
...
Then surround your bottomNavigationBar with a ValueListenableBuilder that takes the ValueNotifier from the HideNavbar instance and then set the height property of the bottomNavigationBar to be either 0 or any other value depending on the status of the ValueNotifier.
bottomNavigationBar: ValueListenableBuilder(
valueListenable: hiding.visible,
builder: (context, bool value, child) => AnimatedContainer(
duration: Duration(milliseconds: 500),
height: value ? kBottomNavigationBarHeight : 0.0,
child: Wrap(
children: <Widget>[
BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.blue,
fixedColor: Colors.white,
unselectedItemColor: Colors.white,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.card_giftcard),
title: Text('Offers'),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
title: Text('Account'),
),
],
),
],
),
),
),
This approaches keeps avoids countless rebuilds and doesn't require any external libraries. You can also implement this as a stream-based approach but that would require another library such as dart:async and would not be changing anything. Make sure to call the dispose function of HideNavbar inside HomePage's dispose function to clear all resources used.
Working code with BottomNavigationBar.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController _hideBottomNavController;
bool _isVisible;
#override
initState() {
super.initState();
_isVisible = true;
_hideBottomNavController = ScrollController();
_hideBottomNavController.addListener(
() {
if (_hideBottomNavController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (_isVisible)
setState(() {
_isVisible = false;
});
}
if (_hideBottomNavController.position.userScrollDirection ==
ScrollDirection.forward) {
if (!_isVisible)
setState(() {
_isVisible = true;
});
}
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: CustomScrollView(
controller: _hideBottomNavController,
shrinkWrap: true,
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(10.0),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => _getItem(context),
childCount: 20,
),
),
),
],
),
),
bottomNavigationBar: AnimatedContainer(
duration: Duration(milliseconds: 500),
height: _isVisible ? 56.0 : 0.0,
child: Wrap(
children: <Widget>[
BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.blue,
fixedColor: Colors.white,
unselectedItemColor: Colors.white,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.card_giftcard),
title: Text('Offers'),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box),
title: Text('Account'),
),
],
),
],
),
),
);
}
_getItem(BuildContext context) {
return Card(
elevation: 3,
margin: EdgeInsets.all(8),
child: Row(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Item',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
)
],
),
),
),
],
),
);
}
}
Working Model
Screenshot (Null safe + Optimized)
Code:
class MyPage extends StatefulWidget {
#override
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
late final ScrollListener _model;
late final ScrollController _controller;
final double _bottomNavBarHeight = 56;
#override
void initState() {
super.initState();
_controller = ScrollController();
_model = ScrollListener.initialise(_controller);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: _model,
builder: (context, child) {
return Stack(
children: [
ListView.builder(
controller: _controller,
itemCount: 20,
itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
),
Positioned(
left: 0,
right: 0,
bottom: _model.bottom,
child: _bottomNavBar,
),
],
);
},
),
);
}
Widget get _bottomNavBar {
return SizedBox(
height: _bottomNavBarHeight,
child: BottomNavigationBar(
backgroundColor: Colors.amber,
items: [
BottomNavigationBarItem(icon: Icon(Icons.call), label: 'Call'),
BottomNavigationBarItem(icon: Icon(Icons.message), label: 'Message'),
],
),
);
}
}
class ScrollListener extends ChangeNotifier {
double bottom = 0;
double _last = 0;
ScrollListener.initialise(ScrollController controller, [double height = 56]) {
controller.addListener(() {
final current = controller.offset;
bottom += _last - current;
if (bottom <= -height) bottom = -height;
if (bottom >= 0) bottom = 0;
_last = current;
if (bottom <= 0 && bottom >= -height) notifyListeners();
});
}
}