Flutter - Send params in Bottom Navigation Bar - flutter

I have two different pages. One of these is a form, the other is a list of elements. If you swipe one of the elements to left can edit. I need to send the element data (from the list) to first page (the form)
When the application starts the data is null, if it come from the element list isnĀ“t null.
The navigation bar code is:
import 'package:flutter/material.dart';
import 'package:datameter/screens/configuration/form/partials/home_page.dart';
import 'package:datameter/screens/configuration/list/datameters.dart';
import 'package:datameter/locations/locations.dart';
class Navigation extends StatefulWidget {
final item;
Navigation(
{Key key,
this.item})
: super(key: key);
#override
State<StatefulWidget> createState() {
return _NavigationState();
}
}
class _NavigationState extends State<Navigation> {
int currentIndex = 0;
List<Widget> children = [
HomePageScreen(datameter: widget.item), //ERRROR HERE
DatametersPageScreen(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: children[currentIndex],
bottomNavigationBar: new Theme(
data: Theme.of(context).copyWith(
primaryColor: Colors.blue[700],
textTheme: Theme.of(context)
.textTheme
.copyWith(caption: new TextStyle(color: Colors.black)),
),
child: BottomNavigationBar(
onTap: onTabTapped,
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.add),
title: Text(DemoLocalizations.of(context).text('new-device')),
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text(DemoLocalizations.of(context).text('show-all')),
),
],
)),
);
}
void onTabTapped(int index) {
setState(() {
currentIndex = index;
});
}
}
When I try to send params from elements list (using navigation bottom bar) to the form, returns
Static only members can be accessed in initializers.
Does anybody know how to fix this?

_NavigationState Object is not constructed so you cannot access "widget" getter yet because its not initialized and its not static property either.
change
List<Widget> children = [
HomePageScreen(datameter: widget.item), //ERRROR HERE
DatametersPageScreen(),
];
to
List<Widget> _children() =>
[
HomePageScreen(datameter: widget.item),
DatametersPageScreen(),
];
and then in your build change
#override
Widget build(BuildContext context)
{
final List<Widget> children = _children();
return Scaffold
(
//code
body: children[currentIndex],
//code
);
}

Related

Flutter: Getx navigation return null

I use GetX package since a while, but sometimes I suffer from some mistakes..
Now I have a bottomNavigationBar which has 5 pages to navigate between (Offers - Categories - Cart - Favorite - Account).
My problem is:
When I go from index 0 to index 2 (for an example), it goes normally, but when I want to get back to index 0, here the app crashes and give me this error:
Null check operator used on a null value
The same way I was using with another project, but there I was using TabBar, I used it normally without this mistake, but here in the bottom navigation bar it happens.
Actually I don't believe that the error because of the widget kind, but really want to solve it.
Note :
I created a HomePageController which I defined all of the bottomNavigationBar operations, like changing the index, and the list of pages, ..etc
And for each page it has its controller, even when I get back to the page which uses HomePageController it crashes!!!
This is a simple of my code:
class HomePageController extends GetxController {
static HomePageController instance = HomePageController();
late TextEditingController categoriesSearchController;
#override
void onInit() {
super.onInit();
categoriesSearchController = TextEditingController();
}
int bottomNavIndex = 0;
changeBottomIndex(int index) {
bottomNavIndex = index;
update();
}
List<Widget> bottomScreens = const [
Offers(),
Categories(),
Cart(),
Favorite(),
Account(),
];
List<ItemModel> meatsList = [
ItemModel(
title: 'Thigh',
image: 'assets/images/home_page/pin_thigh.png',
description: '1 Kg',
price: 1.72,
),
ItemModel(
title: 'Breast',
image: 'assets/images/home_page/breasts2.jpg',
description: '1 Kg',
price: 1.65,
),
ItemModel(
title: 'lamb',
image: 'assets/images/home_page/lamb.jpeg',
description: '1 Kg',
price: 6.55,
),
];
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetBuilder<HomePageController>(
builder: (controller) => controller != null
? SafeArea(
child: Scaffold(
backgroundColor: AppColors.whiteColor,
bottomNavigationBar: BottomNavigationBar(
items: controller.changingBottom(),
currentIndex: controller.bottomNavIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: AppColors.onBoardingButton,
onTap: (index) {
controller.changeBottomIndex(index);
},
),
body: controller.bottomScreens[controller.bottomNavIndex],
),
)
: const Center(
child: CircularProgressIndicator(),
),
);
}
}
Update:
I forgot to mention that I use GetX Binding class to initialize all controllers when it's needed, like this way:
class Binding extends Bindings {
#override
void dependencies() {
Get.put(() => DatabaseController());
Get.lazyPut(() => AuthController());
Get.lazyPut(() => HomePageController());
Get.lazyPut(() => ProductsController());
Get.lazyPut(() => CartController());
}
}
So there is no need to initialize each controller in each page.
In your Homepage, add init method and autoRemove in GetBuilder like this:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetBuilder<HomePageController>(
init:HomePageController(),
autoRemove:false,
builder: (controller) => controller != null
? SafeArea(
child: Scaffold(
backgroundColor: AppColors.whiteColor,
bottomNavigationBar: BottomNavigationBar(
items: controller.changingBottom(),
currentIndex: controller.bottomNavIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: AppColors.onBoardingButton,
onTap: (index) {
controller.changeBottomIndex(index);
},
),
body: controller.bottomScreens[controller.bottomNavIndex],
),
)
: const Center(
child: CircularProgressIndicator(),
),
);
}
}
By using init, it will rebuild your controller if it is disposed. And, by using autoRemove to false, it won't dispose the controller every single time.
Finally I got solution..
I had to use Get.find<xController>() in each UI page I use any controller class in it.
Widget build(BuildContext context) {
return GetBuilder<CartController>(
init: Get.find<CartController>(),
builder: (controller) => Scaffold(),
And after testing, it works good.

How to call init method or specific function again when we click on already activated bottom menu

I have implemented following BottomNavigation
class AppMenu extends StatefulWidget {
const AppMenu({Key? key}) : super(key: key);
#override
State<AppMenu> createState() => _AppMenuState();
}
class _AppMenuState extends State<AppMenu> {
int current = 0;
final List<String> titles = [
"Home 1",
"Home 2"
];
final List<Widget> views = [
const HomeView1(),
const HomeView2(),
];
final List<String> icons = [
"icon_1",
"icon_2",
];
final List<String> barTitles = ["Home1", "Home2"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HomeAppBar(
title: titles[current],
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (index) {
setState(() {
current = index;
});
},
selectedItemColor: const Color(0xff6B6B6B),
showUnselectedLabels: true,
showSelectedLabels: true,
unselectedItemColor: const Color(0xff6B6B6B),
selectedLabelStyle: const TextStyle(fontSize: 12),
unselectedLabelStyle: const TextStyle(fontSize: 12),
items: views.map((e) {
final itemIndex = views.indexOf(e);
return BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Image.asset(
"assets/images/${icons[itemIndex]}${itemIndex == current ? "" : "_disabled"}.png",
width: 25,
),
),
label: barTitles[itemIndex],
);
}).toList()),
body: Column(
children: [
Expanded(child: views[current]),
],
),
);
}
}
Now it works perfect when I click on home1 and home2 bottom menu and it shows respected widget and load all the content which I have wrote on initState of home1 and home2 but now assume that I am on home1 and if I click again home1 then it is not calling initState again.
I want to call initState or specific function if user click on that menu even if it is selected.
Is there any way to do it?
You can create a initialize or initXXX function to initialize something in initState or somewhere. If parent widget call setState(), then child widget will call didUpdateWidget().
void initialize() {
// do something
}
Call initialize() in initState().
void initState() {
super.initState();
initialize();
}
Call initialize() in didUpdateWidget() of page(child widget).
#override
void didUpdateWidget(covariant PageTest oldWidget) {
super.didUpdateWidget(oldWidget);
initialize();
}
To handle the case in a simple way. You can add your method in onTap of BottomNavigationBar and then pass your data down to the widget tree.
It's only a demonstration to handle your case, you can adjust it with your own liking
For example
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (index) {
if(current == index){
foo = yourMethodHere();
}
setState(() {
current = index;
});
},
Pass the variable in the tree
List<Widget> get views => [
HomeView1(foo),
HomeView2(foo),
];

Flutter: Calling Function from another Class State

My app allows people to post text and switch between different pages on a navbar. On the users page, there is a button, when clicked, will show an overlay so the user can create a post. The overlay includes a back button that calls a function to close the overlay. I want to keep the navbar available at the bottom so user can back out of the post that way if they want to.
The problem is, when the user uses the navbar, the overlay does not close because the close overlay function is on the user page and the navbar page does not have access to it.
How do I give another class on another dart file access to a method or function? If you are able to answer, can you please use my code instead of another example to help me follow better? Thank you.
User Page File #1
class UserPage extends StatefulWidget {
#override
_UserPageState createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
OverlayEntry? entry;
#override
Widget build(BuildContext context) {
return Scaffold(
ElevatedButton(
child: const Text('New Post'),
onPressed: showOverlay,
),
),
}
void showOverlay() {
(...)
}
void closeOverlay() {
entry?.remove();
entry = null;
}
}
Nav Bar File #2 (Need help with "OnTap")
class Nav extends StatefulWidget {
const Nav({Key? key}) : super(key: key);
#override
_NavState createState() => _NavState();
}
class _NavState extends State<Nav> {
int currentTab = 1; // makes the home page the default when loading up the app
#override
void initState() {
super.initState();
}
List<Widget> tabs = <Widget>[
const Other(),
const Home(),
const UserPage(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: tabs.elementAt(currentTab),
),
// BOTTOM NAVIGATION BAR
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentTab,
onTap: (value) {
setState(() => currentTab = value);
const _UserPageState().closeOverlay(); //HERE IS WHERE I NEED HELP WITH THE CODE
},
items: const [
BottomNavigationBarItem(
label: 'Other',
),
BottomNavigationBarItem(
label: 'Home',
),
BottomNavigationBarItem(
label: 'User Page',
),
],
),
);
}
}
You can try to make your _UserPageState public by removing - from it, and then call it UserPageState().closeOverlay();

Flutter - how to hide global bottom navigation bar?

On one of the pages, nested inside one of bottom navigation bar pages I want to hide the bottom navigation bar, which is set as global. To be clear, I'm talking about this bar:
I can't just use Navigator.pushNamed because I'm creating viewModel and passing arguments in this way:
openConversation(BuildContext context) async {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ChangeNotifierProvider(
create: (context) => ConversationVM(...),
child: ConversationScreen(
(...)
),
),
));
}
I've tried to set the Scaffold parameter bottomNavigationBar to null, but without effect, I need to resolve that problem somewhere higher.
Nav bar snippet:
class NavigationBar extends StatefulWidget {
static String id = 'navigation_screen';
#override
State<StatefulWidget> createState() {
return _NavigationBarState();
}
}
class _NavigationBarState extends State<NavigationBar> {
//track the index of our currently selected tab
int _currentIndex = 0;
//st of widgets that we want to render
final List<Object> _children = [
PostedQueries(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
//body of our scaffold which is the widget that gets displayed between our app bar and bottom navigation bar.
body: _children[_currentIndex], // new
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
// new
currentIndex: _currentIndex,
// new
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.deepOrange,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.home),
label: ('Home'),
),
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}

flutter_pagewise library does not preserve page (scroll) state on tabbing away

I am using the flutter_pagewise library and have implemented a paginated grid as per the library's documentation (following their example at https://pub.dev/packages/flutter_pagewise/example), which grabs placeholder images and text via the network.
In my app, I have 2 pages (one is called PaginatedGrid and the other is called SearchPage) that I can tab to via a BottomNavigationBar. However, when I tab to the SearchPage, then tab back to PaginatedGrid, the paginated grid scroll state isn't preserved. The pagination starts from the very beginning and the screen is scrolled back to the top.
import 'package:myproject/my_events/my_events_page.dart';
import 'package:myproject/search/search_page.dart';
import 'package:myproject/widget/paginated_grid.dart';
import 'package:flutter/material.dart';
class PageWrapper extends StatefulWidget {
#override
_PageWrapperState createState() => _PageWrapperState();
}
class _PageWrapperState extends State<PageWrapper> {
ScrollController _scrollController = ScrollController();
int _curIndex = 0;
List<Widget> _pages;
final bucket = PageStorageBucket();
final Key searchPageKey = PageStorageKey('searchKey');
final Key paginatedGridKey = PageStorageKey('paginatedGrid');
#override
void initState() {
_pages = [
PaginatedGrid(key: paginatedGridKey),
SearchPage(key: searchPageKey)
];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageStorage(
bucket: bucket,
child: CustomScrollView(
key: PageStorageKey(_pages[_curIndex].runtimeType.toString()),
controller: _scrollController,
slivers: <Widget>[SliverAppBar(), _pages[_curIndex]],
),
),
bottomNavigationBar: BottomNavigationBar(
onTap: (int i) {
setState(() {
_curIndex = i;
});
},
currentIndex: _curIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Browse',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
)
],
),
);
}
}
Any help would be appreciated.
Using an indexedStack is a solution that worked! The paginated state is preserved on navigation to another tab from the bottom navigation bar.
Instead of using a PageStorage widget, use an IndexedStack widget.
#override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: currentTab,
children: pages,
),
bottomNavigationBar: BottomNavigationBar(
The solution is described here: https://medium.com/#codinghive.dev/keep-state-of-widgets-with-bottom-navigation-bar-in-flutter-bb732214bd11