How to change the BottomNavigationBar after a Navigator.push? - flutter

I would like to set a new BottomNavigationBar after i've clicked on one of my ListTile. Right now, i am getting two BottomNavigationbar after I've clicked on one of them.
Below is my code where I setup the first bar:
class CoachNav extends StatefulWidget {
const CoachNav({Key? key}) : super(key: key);
#override
State<CoachNav> createState() => _CoachNavState();
}
class _CoachNavState extends State<CoachNav> {
int _selectedIndex = 0;
final List<Widget> _widgetOptions = <Widget>[
const TeamListView(),
const SettingsFormView(),
];
Widget? _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.groups),
label: "Mes équipes",
),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: "Paramètres"),
],
currentIndex: _selectedIndex,
onTap: _onItemTap,
),
);
}
}
Then, there is the code where I setup the second bar:
class TeamNav extends StatefulWidget {
const TeamNav({Key? key}) : super(key: key);
#override
State<TeamNav> createState() => _TeamNavState();
}
class _TeamNavState extends State<TeamNav> {
int _selectedIndex = 0;
final List<Widget> _widgetOptions = <Widget>[
const PlayersListView(),
const GamesListView(),
];
Widget? _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.group),
label: "Mes joueurs",
),
BottomNavigationBarItem(
icon: Icon(Icons.sports_basketball), label: "Mes matchs"),
],
currentIndex: _selectedIndex,
onTap: _onItemTap,
),
);
}
}
Here are two screenshots of what is happening
First Bar
Second Bar
---------------- EDIT ----------------------
This is what I get when I make the _widgetOptions texts
I got the first bar... whith the content from where the second bar should Appear
this is the snippet of the code I got as answer below:
lass App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(home: CoachNav());
}
}
class TeamNav extends StatefulWidget {
const TeamNav({Key? key}) : super(key: key);
#override
State<TeamNav> createState() => _TeamNavState();
}
class _TeamNavState extends State<TeamNav> {
int _selectedIndex = 0;
final List<Widget> _widgetOptions = <Widget>[
Text("PlayersListView"),
Text("PlayersListView"),
];
Widget? _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
/* floatingActionButton: FloatingActionButton(onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CoachNav(),
));
}),*/
body: Container(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.group),
label: "Mes joueurs",
),
BottomNavigationBarItem(
icon: Icon(Icons.sports_basketball), label: "Mes matchs"),
],
currentIndex: _selectedIndex,
onTap: _onItemTap,
),
);
}
}
class CoachNav extends StatefulWidget {
const CoachNav({Key? key}) : super(key: key);
#override
State<CoachNav> createState() => _CoachNavState();
}
class _CoachNavState extends State<CoachNav> {
int _selectedIndex = 0;
final List<Widget> _widgetOptions = <Widget>[
Text("PlayersListView"),
Text("PlayersListView"),
];
Widget? _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: _widgetOptions.elementAt(_selectedIndex),
),
//floatingActionButton: FloatingActionButton(onPressed: () {}),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.groups),
label: "Mes équipes",
),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: "Paramètres"),
],
currentIndex: _selectedIndex,
onTap: _onItemTap,
),
);
}
}
This is the code that have the text shown in the third screenshot.
class PlayersListView extends StatelessWidget {
const PlayersListView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: Text("Playerslist"),);
}
}

I think you have a design problem.
In your case, the best way is this one, i think.
When you tap on a tile you should fix a flag and rebuild your page instead of navigating to a new route.
Then, when building your BottomNavigationBarItemlist check the flag and add or remove BottomNavigationBarItem as you need.

Remove extra scaffold from _widgetOptions children that contains bottomNavBar. Follow the snippet pattern
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(home: TeamNav());
}
}
class TeamNav extends StatefulWidget {
const TeamNav({Key? key}) : super(key: key);
#override
State<TeamNav> createState() => _TeamNavState();
}
class _TeamNavState extends State<TeamNav> {
int _selectedIndex = 0;
final List<Widget> _widgetOptions = <Widget>[
Text("PlayersListView"),
Text(" GamesListView()"),
];
Widget? _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CoachNav(),
));
}),
body: Container(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.group),
label: "Mes joueurs",
),
BottomNavigationBarItem(
icon: Icon(Icons.sports_basketball), label: "Mes matchs"),
],
currentIndex: _selectedIndex,
onTap: _onItemTap,
),
);
}
}
class CoachNav extends StatefulWidget {
const CoachNav({Key? key}) : super(key: key);
#override
State<CoachNav> createState() => _CoachNavState();
}
class _CoachNavState extends State<CoachNav> {
int _selectedIndex = 0;
final List<Widget> _widgetOptions = <Widget>[
Text("TeamListView"),
Text("SettingsFormView"),
];
Widget? _onItemTap(int index) {
setState(() {
_selectedIndex = index;
});
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: _widgetOptions.elementAt(_selectedIndex),
),
floatingActionButton: FloatingActionButton(onPressed: () {}),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.groups),
label: "Mes équipes",
),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: "Paramètres"),
],
currentIndex: _selectedIndex,
onTap: _onItemTap,
),
);
}
}

Ok so I found a solution, I simply needed to add "rootNavigator: true" to my Navigator.push. It works as intended now.

Related

Why doesn't BottomNavigatorBarItem currentIndex get updated?

I am new to flutter and I did find similar questions on SO but am too novice to understand the nuance so apologies if this question is too similar to an already asked one.
I have a BottomNavigationBar which has 3 icons (Home, Play and Create) which should navigate between these 3 routes/pages.
main.dart
routes: {
"/home": (context) => MyHomePage(title: "STFU"),
"/play": (context) => Play(),
"/create": (context) => Create(),
"/settings": (context) => Settings(),
},
I extracted my navbar into a custom class so my 3 separate pages could use it:
bottom-nav.dart
class MyBottomNavBar extends StatefulWidget {
MyBottomNavBar({
Key? key,
}) : super(key: key);
#override
State<MyBottomNavBar> createState() => _MyBottomNavBarState();
}
class _MyBottomNavBarState extends State<MyBottomNavBar> {
int _selectedIndex = 0;
void _onTapped(int index) => {
print("_onTapped called with index = $index"),
setState(
() => _selectedIndex = index,
)
};
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
backgroundColor: Colors.orangeAccent[100],
currentIndex: _selectedIndex,
onTap: (value) => {
print("value is $value"),
// find index and push that
_onTapped(value),
if (value == 0)
{Navigator.pushNamed(context, "/home")}
else if (value == 1)
{Navigator.pushNamed(context, "/play")}
else if (value == 2)
{Navigator.pushNamed(context, "/create")}
},
items: [
BottomNavigationBarItem(label: "Home", icon: Icon(Icons.home_filled)),
BottomNavigationBarItem(
label: "Play", icon: Icon(Icons.play_arrow_rounded)),
BottomNavigationBarItem(label: "Create", icon: Icon(Icons.create)),
],
);
}
}
so now i just set this MyBottomNavBar class to the bottomNavigationBar property of the Scaffold widget my inside Home page, Play page and Create page for eg.
home.dart
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: Column(
children: [
Container(
padding: EdgeInsets.all(20.00),
child: Text("inside home"),
),
],
),
bottomNavigationBar: MyBottomNavBar(),
);
}
}
play.dart
class Play extends StatefulWidget {
const Play({super.key});
#override
State<Play> createState() => _PlayState();
}
class _PlayState extends State<Play> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text("inside play page"),
SizedBox(
height: 30.00,
),
Text("some text"),
],
),
),
bottomNavigationBar: MyBottomNavBar(),
);
}
}
The nav bar buttons work to switch between pages but for some reason the currentIndex value isn't getting updated and stays at 0 (i.e on the "Home" icon). When I debug it I can see _selectedIndex getting updated inside inside the _onTapped function which should update the currentIndex value but it doesn't appear to do so. Any help would be appreciated
What you have here is three different pages with their separate BottomNavBar class instance. Instead you should have a shared Scaffold and one bottomNavBar so that when you navigate bottomNavbar state does not reset.
You can use PageView to do this.
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
List<Widget> pages = const [Home(), Play(), Create()];
final _pageController = PageController();
int _selectedIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index) {
_pageController.jumpToPage(index);
_selectedIndex = index;
setState(() {});
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.play_arrow), label: 'Play'),
BottomNavigationBarItem(icon: Icon(Icons.create), label: 'Create'),
],
backgroundColor: Colors.greenAccent,
),
body: PageView(
controller: _pageController,
children: pages,
),
);
}
}
class Home extends StatelessWidget {
const Home({super.key});
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}
class Play extends StatelessWidget {
const Play({super.key});
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}
class Create extends StatelessWidget {
const Create({super.key});
#override
Widget build(BuildContext context) {
return const Placeholder();
}
}

Flutter add a card widget on another screen on button tap

I am working on an app in which I want to add some card widgets on the home screen when a user selects the cards on the explore screen. Since I am new to flutter I need some assistance with code.
It is like it would be adding a favourite or bookmark feature.
If you want to check the full code here is the link to the github : https://github.com/Ankitkj1999/study_lpu/tree/mark_feature
explore.dart
import 'package:flutter/material.dart';
class ExploreScreen extends StatelessWidget {
const ExploreScreen({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ListViewHome();
}
}
class ListViewHome extends StatelessWidget {
final titles = ["List 1", "List 2", "List 3", "List 4"];
final subtitles = [
"Here is list 1 subtitle",
"Here is list 2 subtitle",
"Here is list 3 subtitle",
"Here is list 4 subtitle"
];
// final icons = [
// // IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
// // Icons.access_alarm,
// // Icons.access_time,
// Icons.add
// ];
ListViewHome({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: titles.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(titles[index]),
subtitle: Text(subtitles[index]),
leading: const CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50")),
trailing: IconButton(
onPressed: () {},
icon: const Icon(Icons.add),
),
),
);
});
}
}
class pushAdd extends StatelessWidget {
const pushAdd({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
showWidget() {
return const Scaffold(
body: Center(
child: Text('Home'),
),
);
}
return showWidget();
}
}
home.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:study_lpu/explore.dart';
import 'package:study_lpu/fav.dart';
import 'login.dart';
import 'explore.dart';
// bottom navigation tutorial https://blog.logrocket.com/how-to-build-a-bottom-navigation-bar-in-flutter/
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
late String uid;
int _selectedIndex = 0;
static const List<Widget> _pages = <Widget>[
// Scaffold(
// body: Center(
// child: Text('Hot'),
// ),
// ),
pushAdd(),
ExploreScreen(),
RandWords(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: () async {
await FirebaseAuth.instance.signOut();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const LoginScreen()),
(route) => false);
},
)
],
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.book_outlined),
label: 'Explore',
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outlined),
label: 'Profile',
)
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
),
body:
// Center(
// // child: Text(uid),
// child: _pages.elementAt(_selectedIndex),
// ),
IndexedStack(
index: _selectedIndex,
children: _pages,
));
}
#override
void initState() {
// TODO: implement initState
super.initState();
uid = FirebaseAuth.instance.currentUser!.uid;
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
}
Here is how you can do that in a basic way without using any provider. Firstly you need to put your data in a model to access the variable properly. second, in home page I defined a list of model that is empty and also defined a function that adds card to the list executed in explore page. Also you need to make a view in home page to show added card to the list
class MyCardModel {
String? title;
String? subTitle;
String? leadingImage;
MyCardModel(String title, String subTitle, String leadingImage) {
this.title = title;
this.subTitle = subTitle;
this.leadingImage = leadingImage;
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
late String uid;
int _selectedIndex = 0;
List<MyCardModel> listOfCard = [];
void addCardToListMethod(MyCardModel card) {
setState(() {
listOfCard.add(card);
});
}
#override
Widget build(BuildContext context) {
List<Widget> _pages = <Widget>[
Scaffold(
body: Center(
child: ListView.builder(
itemCount: listOfCard.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(listOfCard[index].title!),
subtitle: Text(listOfCard[index].subTitle!),
leading: CircleAvatar(
backgroundImage:
NetworkImage(listOfCard[index].leadingImage!)),
),
);
})),
),
pushAdd(),
ExploreScreen(addCardToListMethod: addCardToListMethod),
RandWords(),
];
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: () async {
await FirebaseAuth.instance.signOut();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const LoginScreen()),
(route) => false);
},
)
],
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.book_outlined),
label: 'Explore',
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outlined),
label: 'Profile',
)
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
),
body:
// Center(
// // child: Text(uid),
// child: _pages.elementAt(_selectedIndex),
// ),
IndexedStack(
index: _selectedIndex,
children: _pages,
));
}
#override
void initState() {
// TODO: implement initState
super.initState();
uid = FirebaseAuth.instance.currentUser!.uid;
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
}
class ExploreScreen extends StatelessWidget {
Function? addCardToListMethod;
ExploreScreen({Key? key, this.addCardToListMethod}) : super(key: key);
List<MyCardModel> myCardList = [
MyCardModel("List1", "here is list 1 subtitle",
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"),
MyCardModel("List1", "here is list 1 subtitle",
"https://images.unsplash.com/photo-1547721064-da6cfb341d50")
];
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: myCardList.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(myCardList[index].title!),
subtitle: Text(myCardList[index].subTitle!),
leading: CircleAvatar(
backgroundImage:
NetworkImage(myCardList[index].leadingImage!)),
trailing: IconButton(
onPressed: () {
addCardToListMethod!(myCardList[index]);
},
icon: const Icon(Icons.add),
),
),
);
});
}
}

Flutter: navigating only part of screen

So I have a Row and two containers; first for selection of page and second for navigating to that selected page. I pass a string value to the 2nd container for identification which page should I navigate towards.
use NavigationRail
example:
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(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: <Widget>[
NavigationRail(
selectedIndex: _selectedIndex,
onDestinationSelected: (int index) {
setState(() {
_selectedIndex = index;
});
},
labelType: NavigationRailLabelType.selected,
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.favorite_border),
selectedIcon: Icon(Icons.favorite),
label: Text('First'),
),
NavigationRailDestination(
icon: Icon(Icons.bookmark_border),
selectedIcon: Icon(Icons.book),
label: Text('Second'),
),
NavigationRailDestination(
icon: Icon(Icons.star_border),
selectedIcon: Icon(Icons.star),
label: Text('Third'),
),
],
),
const VerticalDivider(thickness: 1, width: 1),
// This is the main content.
Expanded(
child: Center(
child: Text('selectedIndex: $_selectedIndex'),
),
)
],
),
);
}
}
here is the simple solution, you can add page or widget to list then add setstate and index of widget to button onPressed function. when you pressed to button its gonna show the page
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
final screen = [
const Center(child: Text('Home')),
const Center(child: Text('Search')),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Container(
width: 200,
color: Colors.red,
child: Column(
children: [
IconButton(
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
icon: Icon(Icons.home)),
IconButton(
onPressed: () {
setState(() {
_selectedIndex = 1;
});
},
icon: Icon(Icons.search)),
],
),
),
screen[_selectedIndex]
],
),
);
}
}

Unable to call a method in another dart file

I have a dart file with IndexedStack and the following function in the same file to change the stacks.
The file with method as follows-
class RootApp extends StatefulWidget with selectedTab {
#override
_RootAppState createState() => _RootAppState();
}
class _RootAppState extends State<RootApp> {
int pageIndex = 0;
List<Widget> pages = [
......
];
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
....
return AnimatedBottomNavigationBar(
.......
onTap: (index) {
selectedTab(index);
},
);
}
selectedTab(index) {
setState(() {
pageIndex = index;
});
}
}
There is this other dart file from which i would like to call selectedTab method with value of 0. The other file is as follows---
class CreatBudgetPage extends StatefulWidget {
#override
_CreatBudgetPageState createState() => _CreatBudgetPageState();
}
class _CreatBudgetPageState extends State<CreatBudgetPage> {
.......
FirebaseFirestore.instance
.collection('expenses/' + userId + '/' + todayDate)
.add({
....
}).then((_) {
print("collection created");
void rootApp() => selectedTab(0);
}).catchError((error) {
print(error);
});
}
How can i call this method from the other dart file?
P.S: I am a Newbie
You can pass the function callback as parameters from the source class to the intended class and invoke the function as you want.
here is a simple example.
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
int pageIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: IndexedStack(
index: pageIndex,
children: [
Tab1Page(),
Tab2Page(
/// solution 1
onPressed1: () {
selectedTab(0);
},
/// solution 2
// onPressed2: selectedTab,
),
],
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'TAB 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'TAB 2',
),
],
currentIndex: pageIndex,
selectedItemColor: Colors.amber[800],
onTap: selectedTab,
),
);
}
selectedTab(index) {
setState(() {
pageIndex = index;
});
}
}
class Tab1Page extends StatelessWidget {
const Tab1Page({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
child: Center(
child: Text(
"Tab1",
style: TextStyle(fontSize: 20),
),
),
);
}
}
class Tab2Page extends StatelessWidget {
final VoidCallback? onPressed1;
// final Function(int)? onPressed2;
const Tab2Page({
Key? key
/// solution 1
,
this.onPressed1,
/// solution 2
// this.onPressed2
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Tab2",
style: TextStyle(fontSize: 20),
),
ElevatedButton(
onPressed: () {
/// solution 1
if (onPressed1 != null) {
onPressed1!();
}
/// solution 2
// if(onPressed2!=null){
// onPressed2!(0);
//
// }
},
child: Text("click to navigate to another tab",
style: TextStyle(fontSize: 20)),
),
],
),
),
);
}
}

Flutter - How to change the body widget of parent, while passing a value, from child?

So, I have a "home" page, that has a scaffold, appbar, body, and bottomNavigationBar.
Body is loaded as widget, from a list, depending on what's clicked on bottomNavigationBar.
I'd like to be able to press something in the child widget currently loaded in parent body, and change the parent's body widget to another child widget, while passing a value to the child widget as well.
Think of it like pressing a link in an iframe in a browser, just the iframe changes and you can pass a value like ?id=12.
This is the "home" page:
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
final List<Widget> _children = [
Page1(),
Page2(),
Page3(),
Page4()
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
TextButton(
onPressed: () {},
child: Text("Log Out", style: TextStyle(color: Colors.white)),
),
],
backgroundColor: Colors.blue.shade900,
),
body: _children[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.work),
label: 'Page 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.inventory),
label: 'Page 2',
),
BottomNavigationBarItem(
icon: Icon(Icons.alt_route),
label: 'Page 3',
),
BottomNavigationBarItem(
icon: Icon(Icons.article),
label: 'Page 4',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
type: BottomNavigationBarType.fixed,
),
);
}
}
And this is the child widget that would be defined by _children[_selectedIndex] by pressing one of the buttons in the bottomNavigationBar
class Page1 extends StatefulWidget {
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
#override
Widget build(BuildContext context) {
return Text("Click Here",onPressed:(){
//objective here is to open Page5 in the body of HomePage(not in _children list of homepage, and pass it some value (ie. id 12))
},
),
);
}
}
To exemplify expectation, this is what I'd like in Page5 (which should be loaded in the body of HomePage
class Page5 extends StatefulWidget {
#override
_Page5State createState() => _Page5State();
}
class _Page5State extends State<Page5> {
int _variableFromPage1;
#override
Widget build(BuildContext context) {
return Text(_variableFromPage1);
}
}
Can anyone advise the best way of doing that?
You can define custom callback function like the following code sample.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
int _selectedID = 0;
List<Widget> _children;
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
void initState() {
super.initState();
_children = [
Page1(
callback: (value) {
setState(() {
_selectedID = value;
});
},
),
Page2(),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: (_selectedIndex == 0 && _selectedID > 0)
? Page5(id: _selectedID)
: _children[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.work),
label: 'Page 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.inventory),
label: 'Page 2',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
type: BottomNavigationBarType.fixed,
),
);
}
}
typedef IntCallback(int value);
class Page1 extends StatelessWidget {
Page1({Key key, #required this.callback}) : super(key: key);
final IntCallback callback;
#override
Widget build(BuildContext context) {
return Center(
child: InkWell(
onTap: () {
callback(12);
},
child: Text("Page1 ::: id >>> 12"),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Text("Page2"),
);
}
}
class Page5 extends StatelessWidget {
Page5({Key key, #required this.id}) : super(key: key);
final int id;
#override
Widget build(BuildContext context) {
return Center(child: Text("Page5 ::: id $id"));
}
}
You try this way
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _selectedIndex = 0;
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case "Home":
return MaterialPageRoute(builder: (context) => Home());
case "Settings":
return MaterialPageRoute(builder: (context) => Settings());
default:
return MaterialPageRoute(builder: (context) => Default());
}
}
void _onItemTapped(int index) {
switch (index) {
case 1:
_navigatorKey.currentState!
.pushNamedAndRemoveUntil("Home", (route) => false);
break;
case 2:
_navigatorKey.currentState!
.pushNamedAndRemoveUntil("Settings", (route) => false);
break;
default:
_navigatorKey.currentState!
.pushNamedAndRemoveUntil("Default", (route) => false);
}
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Navigator(key: _navigatorKey, onGenerateRoute: generateRoute),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Setting',
),
],
showSelectedLabels: true,
showUnselectedLabels: false,
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Color(0xFF9e9e9e),
iconSize: 16,
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
onTap: _onItemTapped,
),
),
);
}
}