How to make BottonNavigationBar route to different body Containers in Flutter - flutter

I'm creating a simple app consisting of three pages, the only thing to be changed when using the navigation bar is the body. I need every body to be in it's own dart file. whenever I try achieving this and replace the text with another widget, I break the app.
Here's my code so far:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Sample App';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 2: Account',
style: optionStyle,
),
Text(
'Index 1: Locate a Store',
style: optionStyle,
),
Text(
'Index 0: Home',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
backgroundColor: Colors.amber[400],
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: false, // <-- HERE
showUnselectedLabels: false, // <-- AND HERE
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.manage_accounts),
label: 'Account',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Store Locator',
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home Page',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amberAccent[400],
onTap: _onItemTapped,
iconSize: 35,
),
);
}
}
To sum it up, The Text widget need to be replaced with new body/container that's saved in its own dart file, and work with the navigation bar.

Related

Bottom Navigation Bar doesn't work correctly Flutter

I'm trying to make a Bottom Navigation Bar in Flutter but it keeps showing a shadow in front of the Navigation Bar. The code below is the code that taken from the flutter websites which has guarantee 100% work. But the fact is I keep failing because it doesn't work well on my Flutter. Is the version/SDK the problem?
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
Set elevation: 0.0, Inside your BottomNavigationBar widget
child: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
//backgroundColor: Theme.of(context).primaryColor,
elevation: 0.0,

How to add functionality to an instantiated widget in flutter?

I am trying to create a package where I add some functionality to a BottomNavigationBar. I want a generic helper that when used, wraps a bottom nav widget with another widget and changes its onTap method. Unfortunately, onTap is final and cannot be changed. This forces me to create the BottomNavigationBar widget in the package code. This results in me having to delegate all properties of the BottomNavigationBar from the user.
Ideally, I want the user to pass me a navigation bar instance, and I add the functionality to it as long as the passed in widget has currentIndex and onTap properties settable.
How would you solve this?
Edit
Code snippet for what I am trying to achieve:
class ExtendedBottomNav extends StatefulWidget {
const ExtendedBottomNav({required this.bottomNavBar});
final BottomNavigationBar bottomNavBar;
#override
State<StatefulWidget> createState() => ExtendedBottomNavState();
}
class ExtendedBottomNavState extends State<ExtendedBottomNav> {
int _currentTabIndex = 0;
#override
Widget build(BuildContext context) {
widget.bottomNavBar.currentIndex = _currentTabIndex;
widget.bottomNavBar.onTap = (int index) => {
// Some code, using _currentTabIndex
};
return Scaffold(
body: Text("Text"), // Some body
bottomNavigationBar: widget.bottomNavBar,
);
}
}
This is not possible because 'onTap' can't be used as a setter because it's final.
You can use this approach (uses null safety). I've not used all the constructor parameters, you can modify as per your need.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: CustomBottomNavigationBar(bar:BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
));
}
}
class CustomBottomNavigationBar extends StatelessWidget {
final BottomNavigationBar bar;
CustomBottomNavigationBar({ required this.bar});
BottomNavigationBar? customBar;
#override
Widget build(BuildContext context) {
customBar = BottomNavigationBar(
items: bar.items,
currentIndex: bar.currentIndex,
selectedItemColor: bar.selectedItemColor,
onTap: (int index){
// here you can add your additional code
print("hi");
if(bar.onTap != null)
bar.onTap!(index);
},
);
return Container(
child:customBar);
}
}
Without null safety
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: CustomBottomNavigationBar(bar:BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
));
}
}
class CustomBottomNavigationBar extends StatelessWidget {
final BottomNavigationBar bar;
CustomBottomNavigationBar({ #required this.bar});
BottomNavigationBar customBar;
#override
Widget build(BuildContext context) {
customBar = BottomNavigationBar(
items: bar.items,
currentIndex: bar.currentIndex,
selectedItemColor: bar.selectedItemColor,
onTap: (int index){
print("hi");
if(bar.onTap != null)
bar.onTap(index);
},
);
return Container(
child:customBar);
}
}
i think you can do it by extending the BottomNavigationBar widget and override
the onTap funtion and tell user to pass the new subclass of BottomNavigationBar
class NewBottomNavigationBar extends BottomNavigationBar {
NewBottomNavigationBar(List<BottomNavigationBarItem> items)
: super(items: []);
#override
ValueChanged<int>? onTap;
}

How to refactor / extract bottom nav bar from Scaffold?

How do I extract BottomNavigationBar widget from the Flutter's example, please? The problematic part for me is the _onItemTapped function that needs to be used in the BottomNavigationBar as well as in the Scaffold
This is Flutter's BottomNavigationBar example:
/// Flutter code sample for BottomNavigationBar
// This example shows a [BottomNavigationBar] as it is used within a [Scaffold]
// widget. The [BottomNavigationBar] has three [BottomNavigationBarItem]
// widgets and the [currentIndex] is set to index 0. The selected item is
// amber. The `_onItemTapped` function changes the selected item's index
// and displays a corresponding message in the center of the [Scaffold].
//
// ![A scaffold with a bottom navigation bar containing three bottom navigation
// bar items. The first one is selected.](https://flutter.github.io/assets-for-api-docs/assets/material/bottom_navigation_bar.png)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This 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(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
You can extract the BottomNavigationBar as a widget & handle the _onItemTapped as a callback as shown below:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This 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(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: CustomBottomNavigation(
selectedIndex: _selectedIndex,
onTap: _onItemTapped,
),
);
}
}
Create another file called custom_bottom_navigation.dart with the following code:
class CustomBottomNavigation extends StatelessWidget {
const CustomBottomNavigation({this.selectedIndex = 0, this.onTap});
final int selectedIndex;
final void Function(int) onTap;
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: onTap,
);
}
}
This way your code will be refactored into two simple widgets.

How to style BottomNavigationBarItem label

In my application I have a BottomNavigationBar.
I am implementing multi navigation with bottom navigation bar as shown here;
https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf
I have put the title text like this.
BottomNavigationBarItem _buildItem(
{TabItem tabItem, String tabText, IconData iconData}) {
return BottomNavigationBarItem(
icon: Icon(
iconData,
color: widget.currentTab == tabItem
? active_button_color
: Colors.grey,
),
//label: tabText,
title: Text(
tabText,
style: widget.currentTab == tabItem
? Archivo_12_0xff002245
: Archivo_12_grey,
),
);
I get message title is deprecated.
When I use the label parameter how do I style it?
You style it using properties on the BottomNavigationBar.
Example:
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'First',
),
BottomNavigationBarItem(
icon: Icon(Icons.exit_to_app),
label: 'Second',
),
],
selectedLabelStyle: TextStyle(fontSize: 22),
selectedItemColor: Colors.red,
),
There are of course more properties. Check the documentation on: https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html
As the message already indicates that the 'title' property is deprecated, it has been replace with 'label'.
Concerning the selected color not changing, you need to add a function call 'onItemTapped' to change the color by setting the state.
The following is the working code:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
static const String _title = 'TEST';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: First',
style: optionStyle,
),
Text(
'Index 1: Second',
style: optionStyle,
),
];
//METHOD TO SET STATE
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'First',
),
BottomNavigationBarItem(
icon: Icon(Icons.exit_to_app),
label: 'Second',
),
],
selectedLabelStyle: TextStyle(fontSize: 22),
selectedItemColor: Colors.red,
//THIS METHOD NEEDS TO BE CALLED TO CHANGE THE STATE
onTap: _onItemTapped,
currentIndex: _selectedIndex),
);
}
}
There is a bit differences and changes in new flutter for BottomNavigationBar for example title has been changed to label , style in that title moved from BottomNavigationBarItem to BottomNavigationBar as selectedLabelStyle and unselectedLabelStyle etc. So the new solution will something like below:
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentTab,
onTap: (index) => setState(() => currentTab = index),
type: BottomNavigationBarType.fixed,
selectedLabelStyle: Archivo_12_0xff002245,
unselectedLabelStyle: Archivo_12_grey,
items: [
_buildItem(),
],
),
);
}
BottomNavigationBarItem _buildItem(
{TabItem tabItem, String tabText, IconData iconData}) {
return BottomNavigationBarItem(
icon: Icon(
iconData,
color: widget.currentTab == tabItem ? active_button_color : Colors.grey,
),
label: tabText,
);
}

Flutter: Strange animation combining bottomSheet and bottomNavigationBar and calling setState

Im having a Scaffold with a bottomSheet and a bottomNavigationBar in a stateful widget.
Within the bottomSheet, i plan to add buttons, calling setState and triggering a rebuild of the local widget tree.
The problem is, that there is a strange animation happening when calling setState or when rebuilding the widgets. The bottom sheet appears to come from the bottom on top of the bottomNavigationbar.
Is there a way to fix that, such that this animation does not happen, when rebuilding or calling setState?
Please see the code below. To observe the behaviour, run the code and click on the green bottomSheet:
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 _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomSheet: MaterialButton(onPressed: () {
setState(() {});
}, child: Container(height: 50, color: Colors.green)),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('School'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
You should be able to reproduce the behavior i'm talking about when copy/pasting and running the code above.
I'm not sure what i'm doing wrong here, but this appears quite fishy to me.