Flutter: Getx navigation return null - flutter

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.

Related

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

Is it possible to implement Navigation bar using GoRouter Package?

I am trying to implement a NavigationBar using the new Material You API.
https://api.flutter.dev/flutter/material/NavigationBar-class.html
I was just curious to know if we can implement the same using the Go_Router package .
Updated Answer (v6.0.0)
My original answer was created using GoRouter v3 and it was not possible at the time to keep the NavigationBar in sub screens.
Currently, in version 6, GoRouter allows that using the ShellRoute, where you can use the builder attribute to build a Scaffold with the navigation bar.
You can see the oficial live example here.
I am rewriting the outdated answer below using the GoRouter v6.0.0, and I am leaving the original answer in case someone finds it useful.
Updated Code
We need to create some basic models to store data:
/// Just a generic model that will be used to present some data on the screen.
class Person {
final String id;
final String name;
Person({required this.id, required this.name});
}
/// Family will be the model that represents our tabs. We use the properties `icon` and `name` in the `NavigationBar`.
class Family {
final String id;
final String name;
final List<Person> people;
final Icon icon;
Family({
required this.id,
required this.name,
required this.people,
required this.icon,
});
}
/// Families will be used to store the tabs to be easily accessed anywhere. In a real application you would use something fancier.
class Families {
static const List<Icon> icons = [
Icon(Icons.looks_one),
Icon(Icons.looks_two),
Icon(Icons.looks_3)
];
static final List<Family> data = List.generate(
3,
(fid) => Family(
id: '$fid',
name: 'Family $fid',
people: List.generate(
10,
(id) => Person(id: '$id', name: 'Family $fid Person $id'),
),
icon: icons[fid],
),
);
}
Now we'll create the basic views that will render the model's data:
/// Used to present Person's data.
class PersonView extends StatelessWidget {
const PersonView({required this.person, Key? key}) : super(key: key);
final Person person;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text(person.name),
),
);
}
}
/// This is the view that will be used by each application's tab.
class FamilyView extends StatelessWidget {
const FamilyView({super.key, required this.family});
final Family family;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(family.name),
),
body: ListView(
children: [
for (final p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go('/family/${family.id}/person/${p.id}'),
),
],
),
);
}
}
Now, let's finally create the widget that will show the NavigationBar:
/// Widget responsible to render the actual page and the navigation bar.
class ShellScreen extends StatelessWidget {
final Widget child;
final int index;
const ShellScreen({super.key, required this.child, required this.index});
#override
Widget build(BuildContext context) {
if (index < 0 || index >= Families.data.length) {
// Just in case someone tries to pass an invalid index in the url.
GoRouter.of(context).go('/');
return const SizedBox.shrink();
}
return Scaffold(
body: child,
bottomNavigationBar: NavigationBar(
destinations: [
for (final f in Families.data)
NavigationDestination(
icon: f.icon,
label: f.name,
)
],
onDestinationSelected: (index) => context.go(
'/family/${Families.data[index].id}',
),
selectedIndex: index,
),
);
}
}
Finally, this will only work if we define the app's routes using the StackRouter and set the GoRouter as the app's navigator:
void main() {
usePathUrlStrategy();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Flutter Demo',
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
final router = GoRouter(
routes: [
GoRoute(
path: '/',
redirect: (_, __) => '/family/${Families.data[0].id}',
),
ShellRoute(
// The ShellRoute is what make it possible to wrap the subroutes in a common widget using the `builder`
builder: (BuildContext context, GoRouterState state, Widget child) {
int index = int.tryParse(state.params['fid'] ?? '0') ?? 0;
return ShellScreen(index: index, child: child);
},
routes: [
GoRoute(
path: '/family/:fid',
builder: (context, state) {
final fid = state.params['fid']!;
final family = Families.data.firstWhere((f) => f.id == fid,
orElse: () => throw Exception('family not found: $fid'));
return FamilyView(
key: state.pageKey,
family: family,
);
},
routes: [
GoRoute(
path: 'person/:id',
builder: (context, state) {
final fid = state.params['fid']!;
final id = state.params['id'];
final person = Families.data
.firstWhere((f) => f.id == fid,
orElse: () => throw Exception('family not found: $fid'))
.people
.firstWhere(
((p) => p.id == id),
orElse: () => throw Exception('person not found: $id'),
);
return PersonView(key: state.pageKey, person: person);
},
),
],
),
],
),
],
);
The important part that solves our need is the ShellRouter. It is a route used to display any matching sub-routes, instead of placing them on the root Navigator.
The widget built by the matching sub-route becomes the child parameter of the builder. So, the ShellScreen can render the sub-route widget presenting the navigation bar.
With all these steps you will have this:
Outdated Answer (v3.0.0)
Yes, it is possible [as a matter of fact, it was not possible, but I didn't understand the question at the time].
Let's use the example in GoRouter documentation as a starting point.
We need to create some basic models to store data:
/// Just a generic model that will be used to present some data on the screen.
class Person {
final String id;
final String name;
Person({required this.id, required this.name});
}
/// Family will be the model that represents our tabs. We use the properties `icon` and `name` in the `NavigationBar`.
class Family {
final String id;
final String name;
final List<Person> people;
final Icon icon;
Family({
required this.id,
required this.name,
required this.people,
required this.icon,
});
}
/// Families will be used to store the tabs to be easily accessed anywhere. In a real application you would use something fancier.
class Families {
static const List<Icon> icons = [
Icon(Icons.looks_one),
Icon(Icons.looks_two),
Icon(Icons.looks_3)
];
static final List<Family> data = List.generate(
3,
(fid) => Family(
id: '$fid',
name: 'Family $fid',
people: List.generate(
10,
(id) => Person(id: '$id', name: 'Family $fid Person $id'),
),
icon: icons[fid],
),
);
}
Now we'll create the basic views that will render the model's data:
/// Used to present Person's data.
class PersonView extends StatelessWidget {
const PersonView({required this.person, Key? key}) : super(key: key);
final Person person;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text(person.name),
),
);
}
}
/// This is the view that will be used by each application's tab.
class FamilyView extends StatefulWidget {
const FamilyView({required this.family, Key? key}) : super(key: key);
final Family family;
#override
State<FamilyView> createState() => _FamilyViewState();
}
class _FamilyViewState extends State<FamilyView>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return ListView(
children: [
for (final p in widget.family.people)
ListTile(
title: Text(p.name),
onTap: () =>
context.go('/family/${widget.family.id}/person/${p.id}'),
),
],
);
}
}
Until now we did nothing different compared to the GoRouter documentation, so let's finally create the widget that will show the NavigationBar:
class FamilyTabsScreen extends StatefulWidget {
final int index;
FamilyTabsScreen({required Family currentFamily, Key? key})
: index = Families.data.indexWhere((f) => f.id == currentFamily.id),
super(key: key) {
assert(index != -1);
}
#override
_FamilyTabsScreenState createState() => _FamilyTabsScreenState();
}
class _FamilyTabsScreenState extends State<FamilyTabsScreen>
with TickerProviderStateMixin {
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(_title(context)),
),
body: FamilyView(family: Families.data[widget.index]),
bottomNavigationBar: NavigationBar(
destinations: [
for (final f in Families.data)
NavigationDestination(
icon: f.icon,
label: f.name,
)
],
onDestinationSelected: (index) => _tap(context, index),
selectedIndex: widget.index,
),
);
void _tap(BuildContext context, int index) =>
context.go('/family/${Families.data[index].id}');
String _title(BuildContext context) =>
(context as Element).findAncestorWidgetOfExactType<MaterialApp>()!.title;
}
This is the important part of the code above:
/// [...] suppressed code
bottomNavigationBar: NavigationBar(
destinations: [
for (final f in Families.data)
NavigationDestination(
icon: f.icon,
label: f.name,
)
],
onDestinationSelected: (index) => _tap(context, index),
selectedIndex: widget.index,
),
/// [...] suppressed code
So, basically we are using the NavigationBar almost exactly as we would use the TabBarView.
Finally, this will only work if we define the app's routes and set the GoRouter as the app's navigator:
void main() {
GoRouter.setUrlPathStrategy(UrlPathStrategy.path);
runApp(const MyApp());
}
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
redirect: (_) => '/family/${Families.data[0].id}',
),
GoRoute(
path: '/family/:fid',
builder: (context, state) {
final fid = state.params['fid']!;
final family = Families.data.firstWhere((f) => f.id == fid,
orElse: () => throw Exception('family not found: $fid'));
return FamilyTabsScreen(key: state.pageKey, currentFamily: family);
},
routes: [
GoRoute(
path: 'person/:id',
builder: (context, state) {
final fid = state.params['fid']!;
final id = state.params['id'];
final person = Families.data
.firstWhere((f) => f.id == fid,
orElse: () => throw Exception('family not found: $fid'))
.people
.firstWhere(
((p) => p.id == id),
orElse: () => throw Exception('person not found: $id'),
);
return PersonView(key: state.pageKey, person: person);
},
),
]),
],
);
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Flutter Demo',
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
With all these steps you will have this:
For anyone searching on a persistent BottomNavBar across all pages, this is actively being discussed on Github,
https://github.com/flutter/packages/pull/2453
Now you can use ShellRouter with GoRouter to create Navigation Bar
Explaination:
Things to keep in mind while using context.go() from ShellRoute to GoRoute
Specify parentNavigatorKey prop in each GoRoute
Use context.go() to replace page , context.push() to push page to stack
Code Structure to follow:
final _parentKey = GlobalKey<NavigatorState>();
final _shellKey = GlobalKey<NavigatorState>();
|_ GoRoute
|_ parentNavigatorKey = _parentKey 👈 Specify key here
|_ ShellRoute
|_ GoRoute // Needs Bottom Navigation
|_ parentNavigatorKey = _shellKey
|_ GoRoute // Needs Bottom Navigation
|_ parentNavigatorKey = _shellKey
|_ GoRoute // Full Screen which doesn't need Bottom Navigation
|_parentNavigatorKey = _parentKey
Code has following features:
Active icon navbar
Persists navBar item's focus when transisted to new page
back button in the transisted page
Code:
Router
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorKey = GlobalKey<NavigatorState>();
final router = GoRouter(
initialLocation: '/',
navigatorKey: _rootNavigatorKey,
routes: [
ShellRoute(
navigatorKey: _shellNavigatorKey,
pageBuilder: (context, state, child) {
print(state.location);
return NoTransitionPage(
child: ScaffoldWithNavBar(
location: state.location,
child: child,
));
},
routes: [
GoRoute(
path: '/',
parentNavigatorKey: _shellNavigatorKey,
pageBuilder: (context, state) {
return const NoTransitionPage(
child: Scaffold(
body: Center(child: Text("Home")),
),
);
},
),
GoRoute(
path: '/discover',
parentNavigatorKey: _shellNavigatorKey,
pageBuilder: (context, state) {
return const NoTransitionPage(
child: Scaffold(
body: Center(child: Text("Discover")),
),
);
},
),
GoRoute(
parentNavigatorKey: _shellNavigatorKey,
path: '/shop',
pageBuilder: (context, state) {
return const NoTransitionPage(
child: Scaffold(
body: Center(child: Text("Shop")),
),
);
}),
],
),
GoRoute(
parentNavigatorKey: _rootNavigatorKey,
path: '/login',
pageBuilder: (context, state) {
return NoTransitionPage(
key: UniqueKey(),
child: Scaffold(
appBar: AppBar(),
body: const Center(
child: Text("Login"),
),
),
);
},
),
],
);
BottomNavigationBar
class ScaffoldWithNavBar extends StatefulWidget {
String location;
ScaffoldWithNavBar({super.key, required this.child, required this.location});
final Widget child;
#override
State<ScaffoldWithNavBar> createState() => _ScaffoldWithNavBarState();
}
class _ScaffoldWithNavBarState extends State<ScaffoldWithNavBar> {
int _currentIndex = 0;
static const List<MyCustomBottomNavBarItem> tabs = [
MyCustomBottomNavBarItem(
icon: Icon(Icons.home),
activeIcon: Icon(Icons.home),
label: 'HOME',
initialLocation: '/',
),
MyCustomBottomNavBarItem(
icon: Icon(Icons.explore_outlined),
activeIcon: Icon(Icons.explore),
label: 'DISCOVER',
initialLocation: '/discover',
),
MyCustomBottomNavBarItem(
icon: Icon(Icons.storefront_outlined),
activeIcon: Icon(Icons.storefront),
label: 'SHOP',
initialLocation: '/shop',
),
MyCustomBottomNavBarItem(
icon: Icon(Icons.account_circle_outlined),
activeIcon: Icon(Icons.account_circle),
label: 'MY',
initialLocation: '/login',
),
];
#override
Widget build(BuildContext context) {
const labelStyle = TextStyle(fontFamily: 'Roboto');
return Scaffold(
body: SafeArea(child: widget.child),
bottomNavigationBar: BottomNavigationBar(
selectedLabelStyle: labelStyle,
unselectedLabelStyle: labelStyle,
selectedItemColor: const Color(0xFF434343),
selectedFontSize: 12,
unselectedItemColor: const Color(0xFF838383),
showUnselectedLabels: true,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
_goOtherTab(context, index);
},
currentIndex: widget.location == '/'
? 0
: widget.location == '/discover'
? 1
: widget.location == '/shop'
? 2
: 3,
items: tabs,
),
);
}
void _goOtherTab(BuildContext context, int index) {
if (index == _currentIndex) return;
GoRouter router = GoRouter.of(context);
String location = tabs[index].initialLocation;
setState(() {
_currentIndex = index;
});
if (index == 3) {
context.push('/login');
} else {
router.go(location);
}
}
}
class MyCustomBottomNavBarItem extends BottomNavigationBarItem {
final String initialLocation;
const MyCustomBottomNavBarItem(
{required this.initialLocation,
required Widget icon,
String? label,
Widget? activeIcon})
: super(icon: icon, label: label, activeIcon: activeIcon ?? icon);
}
Output:

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 update a text in an item in listview after updating the content from it's detail view?

I am following this link,
https://medium.com/…/developing-for-multiple-screen-sizes-a…
to create a master detail ipad application.
I have a scenario, there is a text field and button in detail page. When i change the text field value and press the button, the listview item (in left side) at that specific index also should be updated. can somebody suggest a work around?
You can return the edited object using Navigator.pop(context,object) to the Navigator.push() caller. I wrote an example app for you.
the data class:
class Item {
final String name;
Item(this.name);
}
the home page, where I display the item:
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Item item = Item('ali2236');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: Center(
child: Column(
children: <Widget>[
Text(item.name),
FlatButton(
child: Text('edit'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return ItemEditingPage(
item: item,
callbackFunction: (editedItem){
setState(() {
item = editedItem;
});
},
);
}));
},
),
],
),
),
),
);
}
}
and the editing page:
class ItemEditingPage extends StatefulWidget {
final Item item;
final void Function(Item item) callbackFunction;
const ItemEditingPage({Key key, this.item, this.callbackFunction}) : super(key: key);
#override
_ItemEditingPageState createState() => _ItemEditingPageState();
}
class _ItemEditingPageState extends State<ItemEditingPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: FlatButton(
child: Text('change name to aligator'),
onPressed: () {
///
/// if the name is [final], you create a new Item and pass it back
///
Item item = Item('aligator');
widget.callbackFunction(item);
///
/// if the name is not final you can just change it on the current object
///
//widget.item.name = 'aligator';
//widget.callbackFunction(widget.item);
},
),
),
),
);
}
}
edit: used a callback function instead of Navigator.pop() to notify the showcase page.

Flutter - Send params in Bottom Navigation Bar

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
);
}