Troubles with BottomNavigationBar // Extra pages - flutter

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: ""),
]));
}
}

Related

Flutter : how to actualize the state of a global variable in every widget?

so i have 2 pages and 1 file where i have my variable . when i run the app my variable total is update every time i enter income page or add a new income . but when i navigate back to the home page the total shows only the first value ive entered .
Home.dart
import 'package:flutter/material.dart';
import 'componets.dart';
import 'income.dart';
import 'variables.dart';
....
bottomNavigationBar: Container(
color: Colors.white,
child: Row(
children: <Widget>[
Expanded(
child: ListTile(
title: new Text("Balance:"),
subtitle: new Text("$Total"),
),
),
the variable total is shown in the bottom of home page
Income.dart
import 'package:flutter/material.dart';
import 'componets.dart';
import 'home.dart';
import 'variables.dart';
class Income extends StatefulWidget {
const Income({Key? key}) : super(key: key);
#override
State<Income> createState() => _IncomeState();
}
class _IncomeState extends State<Income> {
TextEditingController _textFieldController = TextEditingController();
List<double> transactions = [];
Future<void> _displayTextInputDialog(BuildContext context) async {
double Amount = 0;
String valueText = '';
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add income'),
content: TextField(
keyboardType: TextInputType.number,
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: _textFieldController,
),
actions: <Widget>[
FlatButton(
color: Colors.red,
textColor: Colors.white,
child: Text('CANCEL'),
onPressed: () {
setState(() {
Navigator.pop(context);
});
},
),
FlatButton(
color: Colors.green,
textColor: Colors.white,
child: Text('OK'),
onPressed: () {
setState(() {
Amount = double.parse(valueText);
transactions.add(Amount);
Total = Total + Amount;
print(Amount);
Navigator.pop(context);
});
},
),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Income"),
backgroundColor: Colors.deepOrange,
centerTitle: false,
elevation: 1.0,
),
body: Center(
child: FlatButton(
color: Colors.teal,
textColor: Colors.white,
onPressed: () {
_displayTextInputDialog(context);
},
child: Text('Press For Alert'),
),
),
// NavigationBar
bottomNavigationBar: Container(
color: Colors.white,
child: Row(
children: <Widget>[
Expanded(
child: ListTile(
title: Text("Balance:"),
subtitle: Text('${Total}'),
),
),
the variable in the same place as the home page
variables.dart
double Total = 0;
I have created example of updating total value using provider by refering yours
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
//Provider for updating totalvalue
class TotalValue with ChangeNotifier {
double _total = 0;
double get value => _total;
void sum(double val) {
_total += val;
notifyListeners();
}
}
//Income Page
class Income extends StatefulWidget {
const Income({Key? key}) : super(key: key);
#override
State<Income> createState() => _IncomeState();
}
class _IncomeState extends State<Income> {
TextEditingController _textFieldController = TextEditingController();
List<double> transactions = [];
Future<void> _displayTextInputDialog(BuildContext context) async {
double Amount = 0;
String valueText = '';
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add income'),
content: TextField(
keyboardType: TextInputType.number,
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: _textFieldController,
),
actions: <Widget>[
FlatButton(
color: Colors.red,
textColor: Colors.white,
child: Text('CANCEL'),
onPressed: () {
Navigator.pop(context);
},
),
FlatButton(
color: Colors.green,
textColor: Colors.white,
child: Text('OK'),
onPressed: () {
Amount = double.parse(valueText);
transactions.add(Amount);
context.read<TotalValue>().sum(Amount);
Navigator.pop(context);
},
),
],
);
});
}
#override
Widget build(BuildContext context) {
return Center(
child: FlatButton(
color: Colors.teal,
textColor: Colors.white,
onPressed: () {
_displayTextInputDialog(context);
},
child: Text('Press For Alert'),
),
);
}
}
//Home Page
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _selectedIndex = 0;
static TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static List<Widget> _widgetOptions(BuildContext context) {
return <Widget>[
Income(),
Text(
'${context.watch<TotalValue>().value}',
style: optionStyle,
),
];
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Income'),
),
body: Center(
child: _widgetOptions(context).elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.money),
label: 'Income',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}

Call Function From Another Flutter Class

I would like to call function between another clas. So when the menu tapped from grabDrawer it will change the currentIndex at Main() class. Do you know how to do that? Here is so far I have tried.
main.dart
class _MainState extends State<Main> {
int currentIndex = 0;
Map<String,dynamic> searchParameter = {};
List screens = [
Home(),
Search({}),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
actions: [
Builder(builder: (context){
return IconButton(
onPressed: (){
Scaffold.of(context).openEndDrawer();
},
icon: const Icon(Icons.menu),
);
}),
],
),
endDrawer: const Drawer(
child:DrawerObject(),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () async{
await Future.delayed(Duration(milliseconds: 100),(){
globals.scrollController.animateTo(0, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
});
},
),
body: screens[currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
onTap: (index) => setState(() {
if (index == 1) {
getSearchForm(context);
} else {
currentIndex = index;
searchParameter = {};
}
}),
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey[100],
type: BottomNavigationBarType.shifting,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.blue[500],
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Pencarian',
backgroundColor: Colors.orange[500],
),
],
),
);
}
//main function ===> NEED TO CALL THIS FUNCTION INSIDE grabDrawer.dart
Future UpdateIndex({int Index = 0}) async{
setState(() {
currentIndex = Index;
});
}
Future getSearchForm(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SearchForm(parameter:searchParameter)),
);
setState(() {
if (result != null) {
currentIndex = 1;
if(result!=searchParameter){
searchParameter = result;
screens[1] = CallLoading(show: ''); //set default to load
//set to new parameter (rebuilding widget)
Future.delayed(Duration(milliseconds: 500),(){
setState(() {
screens[1] = Search(searchParameter);
});
});
}
}
else{
}
});
}
}
Under this file, I need to call function from Main.UpdateIndex.
grabDrawer.dart
class DrawerObject extends StatelessWidget {
const DrawerObject({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text('Cari Properti?'),
onTap: (){
===> CALL IT HERE
}
),
],
),
);
}
}
I really appreciate any answers. Thank you.
Change your grabDrawer.dart like this
class DrawerObject extends StatelessWidget {
void Function()? UpdateIndex;
DrawerObject({
this.UpdateIndex,
});
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: [
ListTile(
leading: Icon(Icons.home),
title: Text('Cari Properti?'),
onTap: (){
UpdateIndex!();
}
),
],
),
);
}
}
And in your main.dart, call Drawer class like this
endDrawer: const Drawer(
child:DrawerObject(
UpdateIndex: UpdateIndex,
);
),
Hope this works for you.
Here is the clear way to pass data between one class to another class
void main() {
runApp(MaterialApp(
home: Modalbtn(),
));
}
class Modalbtn extends StatefulWidget {
#override
_ModalbtnState createState() => _ModalbtnState();
}
class _ModalbtnState extends State<Modalbtn> {
String value = "0";
// Pass this method to the child page.
void _update(String newValue) {
setState(() => value = newValue);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
child: Column(
children: [StatefulModalbtn(update: _update)],
),
);
});
},
icon: Icon(Icons.add),
iconSize: 20,
),
Text(
value,
style: TextStyle(fontSize: 40),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
class StatefulModalbtn extends StatelessWidget {
final ValueChanged<String> update;
StatefulModalbtn({required this.update});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => update("100"), // Passing value to the parent widget.
child: Text('Update (in child)'),
);
}
}

Flutter: Looking up a deactivated widget's ancestor is unsafe with BottomNavigationBar and SearchPage

The app has a BottomNavigationBar (always visible) and when I added the search tab this error apears (Search page was writed following a tutorial from youtube). I'm not sure about how can manage the state when I leave the search page or how to restart it.
I realice error apears when no search has been performed
Can someone help to solve this error?
Here is my code
home
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static final List<Widget> _widgetOptions = <Widget>[
const Text(
'Index 1: Home',
style: optionStyle,
),
const Text(
'Index 1: Events',
style: optionStyle,
),
SearchScreen(),
const Text(
'Index 3: Messages',
style: optionStyle,
),
ProfilePage(uid: FirebaseAuth.instance.currentUser!.uid)
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: null,
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today),
label: 'Events',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.chat_bubble_2),
label: 'Messages',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: mobileBackgroundColor,
),
],
unselectedItemColor: Colors.black12,
selectedItemColor: Colors.deepOrangeAccent,
currentIndex: _selectedIndex,
onTap: _onItemTapped,
));
}
}
search
class Search extends StatefulWidget {
#override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
static const historyLenght = 5;
final List<String> _searchHistory = ['pink', 'blue'];
late List<String> filteredSearchHistory;
String selectedTerm = "Search";
List<String> filterSearchTerms({
#required String? filter,
}) {
if (filter != null && filter.isNotEmpty) {
return _searchHistory.reversed
.where((term) => term.startsWith(filter))
.toList();
} else {
return _searchHistory.reversed.toList();
}
}
void addSearchTerm(String term) {
if (_searchHistory.contains(term)) {
putSearchTermFirst(term);
return;
}
_searchHistory.add(term);
if (_searchHistory.length > historyLenght) {
_searchHistory.removeRange(0, _searchHistory.length - historyLenght);
}
filteredSearchHistory = filterSearchTerms(filter: null);
}
void deleteSearchTerm(String term) {
_searchHistory.removeWhere((element) => element == term);
filteredSearchHistory = filterSearchTerms(filter: null);
}
void putSearchTermFirst(String term) {
deleteSearchTerm(term);
addSearchTerm(term);
}
late FloatingSearchBarController controller;
#override
void initState() {
super.initState();
controller = FloatingSearchBarController();
filteredSearchHistory = filterSearchTerms(filter: null);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingSearchBar(
controller: controller,
body: FloatingSearchBarScrollNotifier(
child: SearchResultListView(
searchTerm: selectedTerm,
),
),
transition: CircularFloatingSearchBarTransition(),
physics: const BouncingScrollPhysics(),
title: Text(selectedTerm),
hint: 'Search ... ',
actions: [
FloatingSearchBarAction.searchToClear(),
],
onQueryChanged: (query) {
setState(() {
filteredSearchHistory = filterSearchTerms(filter: query);
});
},
onSubmitted: (query) {
setState(() {
addSearchTerm(query);
selectedTerm = query;
});
controller.close();
},
builder: (context, transition) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Material(
color: Colors.white,
elevation: 4,
child: Builder(builder: ((context) {
if (filteredSearchHistory.isEmpty &&
controller.query.isEmpty) {
return Container(
height: 56,
width: double.infinity,
alignment: Alignment.center,
child: const Text(
'Start searching',
maxLines: 1,
overflow: TextOverflow.ellipsis,
));
} else if (filteredSearchHistory.isEmpty) {
return ListTile(
title: Text(controller.query),
leading: const Icon(Icons.search),
onTap: () {
setState(() {
addSearchTerm(controller.query);
selectedTerm = controller.query;
});
controller.close();
},
);
} else {
return Column(
mainAxisSize: MainAxisSize.min,
children: filteredSearchHistory
.map(
(e) => ListTile(
title: Text(e,
maxLines: 1,
overflow: TextOverflow.ellipsis),
leading: const Icon(Icons.history),
trailing: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
setState(() {
deleteSearchTerm(e);
});
},
),
onTap: () {
setState(() {
putSearchTermFirst(e);
selectedTerm = e;
});
controller.close();
},
),
)
.toList());
}
}))),
);
},
),
);
}
}
class SearchResultListView extends StatelessWidget {
final String searchTerm;
const SearchResultListView({
Key? key,
required this.searchTerm,
}) : super(key: key);
#override
Widget build(BuildContext context) {
if (searchTerm == null) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [Icon(Icons.search), Text('data')],
),
);
}
final sBar = FloatingSearchBar.of(context);
return ListView(
padding: EdgeInsets.only(
top: sBar!.style.height + sBar.style.margins.vertical),
children: List.generate(
50,
(index) => ListTile(
title: Text('Hola $searchTerm'),
subtitle: Text(index.toString()),
)));
}
}

Keep the bottomnavigationbar when Navigating to a new screen

I want to keep the bottomnavigationbar AppBar when navigating to a new screen. with save state when i back to page.
The trick is to use "IndexedStack" in the body, IndexedStack workers like Stack Widget :
IndexedStack(
index: _selectedIndex,
children: _widgetOptions,
)
pass list of widgets in 'children' & index of the widget in 'index'.
Here is the example code:
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: BottomNavPage(),
);
}
}
class BottomNavPage extends StatefulWidget {
const BottomNavPage({Key? key}) : super(key: key);
#override
_BottomNavPageState createState() => _BottomNavPageState();
}
class _BottomNavPageState extends State<BottomNavPage> {
int _selectedIndex = 0;
static const List<Widget> _widgetOptions = <Widget>[
NavHome(),
Text('Index 1: Business'),
Text('Index 2: School'),
Text('Index 3: Settings'),
];
Widget navHome() {
return Text(
'Index 0: Home',
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('BottomNavigationBar Sample')),
body: IndexedStack(
index: _selectedIndex,
children: _widgetOptions,
),
bottomNavigationBar: Material(
child: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Home',
backgroundColor: Colors.blue,
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.orange[400],
onTap: _onItemTapped,
),
),
);
}
}
class NavHome extends StatefulWidget {
const NavHome({
Key? key,
}) : super(key: key);
#override
_NavHomeState createState() => _NavHomeState();
}
class _NavHomeState extends State<NavHome> {
double _counter = 0;
void incrementCounter() {
setState(() {
_counter++;
});
}
void decrementCounter() {
setState(() {
_counter--;
});
}
#override
Widget build(BuildContext context) {
// Return a widget number in the center of the screen and button to increment the number
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Counter',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Increment'),
onPressed: incrementCounter,
),
SizedBox(width: 10),
ElevatedButton(
child: Text('Decrement'),
onPressed: decrementCounter,
),
],
),
],
),
);
}
}

How to use Flutter GetX Sidebar

How can I implement the design in the image (sidebar and navigation menu) in Flutter using GetX? similarly to Tabs on the web.
This is a example, maybe it can help you:
import 'package:flutter/material.dart';
import '../../routes/app_pages.dart';
import 'package:get/get.dart';
class SideBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
DrawerHeader(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Icon(
Icons.person,
color: Colors.white,
size: 50.0,
),
),
Center(
child: Text(
"Vakup",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
],
),
decoration: BoxDecoration(
color: Colors.blueAccent,
),
),
ListTile(
leading: Icon(Icons.read_more),
title: Text('Leer datos'),
onTap: () {
if (Get.currentRoute == Routes.HOME) {
Get.back();
} else {
Get.toNamed(Routes.HOME);
}
},
),
ListTile(
leading: Icon(Icons.pets),
title: Text('Registrar animal'),
onTap: () {
if (Get.currentRoute == Routes.NEWANIMAL) {
Get.back();
} else {
Get.toNamed(Routes.NEWANIMAL);
}
},
),
ListTile(
leading: Icon(Icons.list_alt),
title: Text('Lista movimientos'),
onTap: () {
if (Get.currentRoute == Routes.MOVEMENTS) {
Get.back();
} else {
//Get.to
Get.toNamed(Routes.MOVEMENTS);
}
},
),
ListTile(
leading: Icon(Icons.list),
title: Text('Lista animales'),
onTap: () {
if (Get.currentRoute == Routes.LISTOFANIMALS) {
Get.back();
} else {
Get.toNamed(Routes.LISTOFANIMALS);
}
},
),
ListTile(
leading: Icon(Icons.edit),
title: Text('Grabar datos'),
onTap: () {
if (Get.currentRoute == Routes.GRABADO) {
Get.back();
} else {
Get.toNamed(Routes.GRABADO);
}
},
),
ListTile(
leading: Icon(Icons.bluetooth),
title: Text('Conexion BT'),
onTap: () {
if (Get.currentRoute == Routes.CONEXIONBT) {
Get.back();
} else {
Get.toNamed(Routes.CONEXIONBT);
}
},
),
ListTile(
leading: Icon(Icons.picture_as_pdf),
title: Text('Exportar Datos'),
onTap: () {
if (Get.currentRoute == Routes.EXPORT) {
Get.back();
} else {
Get.toNamed(Routes.EXPORT);
}
},
),
ListTile(
leading: Icon(Icons.recent_actors_rounded),
title: Text('Acerca de'),
onTap: () {
if (Get.currentRoute == Routes.ACERCA) {
Get.back();
} else {
Get.toNamed(Routes.ACERCA);
}
},
),
],
),
);
}
}
And the home part is:
import 'package:vakuprfid/app/modules/widgets/side_bar.dart';//import widget
class HomeView extends GetView<HomeController> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: SideBar(),
body: ...
);
}
}
This is the result:
For the main content put all the different view into a list and put it into PageView. And create a custom navigator and put these two widget into a Row:
Controller:
class SettingsController extends GetxController {
final PageController pageController =
PageController(initialPage: 1, keepPage: true);
}
Sidebar:
class MySideNavigation extends StatefulWidget {
MySideNavigation({Key? key}) : super(key: key);
#override
State<MySideNavigation> createState() => _MySideNavigationState();
}
class _MySideNavigationState extends State<MySideNavigation> {
#override
Widget build(BuildContext context) {
final SettingsController c = Get.find();
return NavigationRail(
selectedIndex: c.selectedViewIndex.value,
onDestinationSelected: (value) async {
setState(() {
c.selectedViewIndex(value);
c.pageController.jumpToPage(
value,
// duration: Duration(milliseconds: 500), curve: Curves.decelerate
);
});
},
labelType: NavigationRailLabelType.selected,
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.map_outlined),
selectedIcon: Icon(Icons.map_rounded),
label: Text(
'نقشه ها',
style: TextStyle(fontSize: 14, fontFamily: 'Vazir'),
),
),
NavigationRailDestination(
icon: Icon(Icons.map_outlined),
selectedIcon: Icon(Icons.map_rounded),
label: Text(
'نقشه ها',
style: TextStyle(fontSize: 14, fontFamily: 'Vazir'),
),
),
NavigationRailDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: Text(
'پروفایل',
style: TextStyle(fontSize: 14, fontFamily: 'Vazir'),
),
),
],
);
}
}
GotFatherView:
class GodFatherView extends StatelessWidget {
GodFatherView({Key? key}) : super(key: key);
final PageStorageBucket bucket = PageStorageBucket();
final SettingsController c = Get.find();
List<Widget> pages = [
const KeepAlivePage(Page1()),
KeepAlivePage(Page2()),
const KeepAlivePage(Page3()),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
MySideNavigation(),
Expanded(
child: PageView(
controller: c.pageController,
children: pages,
),
)
],
));
}
}
tap on below link to open screenshot: I don't have enough reputation to post image :))))))
Screeshot
Give a special attention to the sidebar navigator in MySideNavigation class:
NavigationRail(
selectedIndex: c.selectedViewIndex.value,
onDestinationSelected: (value) async {
setState(() {
c.pageController.jumpToPage(value);
});
},
When user tap on each NavigationRailDestination ,onDestinationSelected function will be called with an index. The index are representing the index of the destination view. Example: When user on [Page1() -> index:0] tab on the second NavigationRailDestination the index inside of function is 1, so you can use the PageController to navigate into [Page2() -> index:1].
Attention, Attention, More Attention:
If you don't like to lose the state(I mean when u navigate to another view and back to previous view don't rebuild it again). Sometimes we need to keep the state of widget, we change something, write something into a text field and etc. If you don't wrap it with this widget all the data will be loosed(or you can save it through another way).
Wrap your widget with this Widget see the GodFather View I wrap all pages with KeepAlivePage, In this widget I extend State of the widget with 'AutomaticKeepAliveClientMixin' and override its value bool get wantKeepAlive => true; .
import 'package:flutter/material.dart';
class KeepAlivePage extends StatefulWidget {
const KeepAlivePage(this.child, {Key? key}) : super(key: key);
final child;
#override
State<KeepAlivePage> createState() => _KeepAlivePageState();
}
class _KeepAlivePageState extends State<KeepAlivePage>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
it's easy,just let your right conent use GetMaterialApp and the route change is render right concent, then left sidler is a component warp your menuslider,
last control you left slider menuchange index.
show my code
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(dessignWidth, dessignHeight),
builder: () => BarcodeKeyboardListener(
onBarcodeScanned: (String codeValue) {},
child: Material(
child: MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const [
Locale('zh', 'CH'),
Locale('en', 'US'),
],
home: Row(
children: [
Material(
child: SliderMenu(),
),
Expanded(
child: GetMaterialApp(
debugShowCheckedModeBanner: false,
enableLog: true,
navigatorKey: Get.key,
routingCallback: RouteChangeMiddleWare.observer,
logWriterCallback: Logger.write,
initialRoute: AppPages.INITIAL,
getPages: AppPages.routes,
unknownRoute: AppPages.unknownRoute,
builder: EasyLoading.init(),
onInit: () =>
{logger.v('Global.CONFIG', AppConfig)}))
],
)),
)));
}```
hope to help you;