I'm trying to learn proper state management with provider, I've got an issue that I'm having trouble with here. From what I can tell, I'm doing everything as per instructions in three separate provider tutorials. I'm also using it successfully for my dataTable with no issues. However I keep getting error messages and failures when trying to use provider for my BottomNavigationBar and my appBar up the widget tree.
I want to keep my bottom nav bar separate from my "zero screen" state. So I'm setting an int with bottom nav bar button presses according to index, this is supposed to update my GlobalData pageSelection Index. I then access this up the tree for the ternary operators. But I get a weird error saying that its out of the widget tree...
I tried doing what the error suggestion states with no luck.
Any help appreciated!
import 'package:flutter/material.dart';
class GlobalData extends ChangeNotifier {
int pageSelectionIndex = 0;
void updatePageSelection(int index) {
pageSelectionIndex = index;
notifyListeners();
}
}
BottomNavBar
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fp_provider_demo_one/models/global_data.dart';
import 'package:fp_provider_demo_one/screens/balance_screen.dart';
import 'package:fp_provider_demo_one/screens/subtract_screen.dart';
import 'package:fp_provider_demo_one/screens/add_screen.dart';
import 'package:fp_provider_demo_one/screens/inquiry_screen.dart';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class BottomNavBar extends StatefulWidget {
@override
_BottomNavBarState createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int _currentTabIndex = 0;
@override
Widget build(BuildContext context) {
final _tabPages = <Widget>[
AddScreen(),
SubtractScreen(),
BalanceScreen(),
InquiryScreen()
];
final _navBarItems = <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: Icon(Icons.add_circle), label: 'Add'),
const BottomNavigationBarItem(
icon: Icon(Icons.remove_circle), label: 'Subtract'),
const BottomNavigationBarItem(
icon: Icon(Icons.grid_on_rounded), label: 'Balance'),
const BottomNavigationBarItem(
icon: Icon(Icons.show_chart), label: 'Inquiry'),
];
assert(_tabPages.length == _navBarItems.length);
final navBar = BottomNavigationBar(
items: _navBarItems,
showUnselectedLabels: true,
showSelectedLabels: true,
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.orange,
currentIndex: _currentTabIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
Provider.of<GlobalData>(context).updatePageSelection(index);
setState(() {
_currentTabIndex = index;
});
},
);
return Scaffold(
body: _tabPages[_currentTabIndex],
bottomNavigationBar: navBar,
);
}
}
Zero Screen:
///
///
/// Holds bottom nav bar which in turn switches through screens
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fp_provider_demo_one/models/global_data.dart';
import 'package:fp_provider_demo_one/widgets/bottom_nav_bar.dart';
import 'package:provider/provider.dart';
class ZeroScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
int _selectedIndex = Provider.of<GlobalData>(context).pageSelectionIndex;
return Scaffold(
appBar: AppBar(
title: (_selectedIndex == 0)
? Text('Add')
: (_selectedIndex == 1)
? Text('Subtract')
: (_selectedIndex == 2)
? Text('Balance')
: Text('Inquiry'),
backgroundColor: (_selectedIndex == 0)
? Colors.green
: (_selectedIndex == 1)
? Colors.red
: (_selectedIndex == 2)
? Colors.blue
: Colors.orange,
),
bottomNavigationBar: BottomNavBar(),
);
}
}
Error:
======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.
To fix, write:
Provider.of<GlobalData>(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.
The context used was: BottomNavBar(state: _BottomNavBarState#38ad7)
'package:provider/src/provider.dart':
Failed assertion: line 262 pos 7: 'context.owner.debugBuilding ||
listen == false ||
debugIsInInheritedProviderUpdate'
When the exception was thrown, this was the stack:
#2 Provider.of (package:provider/src/provider.dart:262:7)
#3 _BottomNavBarState.build.<anonymous closure> (package:fp_provider_demo_one/widgets/bottom_nav_bar.dart:49:18)
#4 _BottomNavigationBarState._createTiles.<anonymous closure> (package:flutter/src/material/bottom_navigation_bar.dart:974:26)
#5 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:991:20)
#6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#035fc
debugOwner: GestureDetector
state: ready
won arena
finalPosition: Offset(144.0, 783.0)
finalLocalPosition: Offset(46.5, 21.5)
button: 1
sent tap down
====================================================================================================
You can copy paste run full code below
Step 1: use Provider.of<GlobalData>(context, listen: false).updatePageSelection
onTap: (int index) {
Provider.of<GlobalData>(context, listen: false)
.updatePageSelection(index);
Step 2 : put ChangeNotifierProvider
in main()
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => GlobalData(),
child: MyApp(),
),
);
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class GlobalData extends ChangeNotifier {
int pageSelectionIndex = 0;
void updatePageSelection(int index) {
pageSelectionIndex = index;
notifyListeners();
}
}
class BottomNavBar extends StatefulWidget {
@override
_BottomNavBarState createState() => _BottomNavBarState();
}
class _BottomNavBarState extends State<BottomNavBar> {
int _currentTabIndex = 0;
@override
Widget build(BuildContext context) {
final _tabPages = <Widget>[
AddScreen(),
SubtractScreen(),
BalanceScreen(),
InquiryScreen()
];
final _navBarItems = <BottomNavigationBarItem>[
const BottomNavigationBarItem(icon: Icon(Icons.add_circle), label: 'Add'),
const BottomNavigationBarItem(
icon: Icon(Icons.remove_circle), label: 'Subtract'),
const BottomNavigationBarItem(
icon: Icon(Icons.grid_on_rounded), label: 'Balance'),
const BottomNavigationBarItem(
icon: Icon(Icons.show_chart), label: 'Inquiry'),
];
assert(_tabPages.length == _navBarItems.length);
final navBar = BottomNavigationBar(
items: _navBarItems,
showUnselectedLabels: true,
showSelectedLabels: true,
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.orange,
currentIndex: _currentTabIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
Provider.of<GlobalData>(context, listen: false)
.updatePageSelection(index);
setState(() {
_currentTabIndex = index;
});
},
);
return Scaffold(
body: _tabPages[_currentTabIndex],
bottomNavigationBar: navBar,
);
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => GlobalData(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ZeroScreen(),
);
}
}
class ZeroScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
int _selectedIndex = Provider.of<GlobalData>(context).pageSelectionIndex;
return Scaffold(
appBar: AppBar(
title: (_selectedIndex == 0)
? Text('Add')
: (_selectedIndex == 1)
? Text('Subtract')
: (_selectedIndex == 2)
? Text('Balance')
: Text('Inquiry'),
backgroundColor: (_selectedIndex == 0)
? Colors.green
: (_selectedIndex == 1)
? Colors.red
: (_selectedIndex == 2)
? Colors.blue
: Colors.orange,
),
bottomNavigationBar: BottomNavBar(),
);
}
}
class AddScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text("AddScreen"));
}
}
class SubtractScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text("SubtractScreen"));
}
}
class BalanceScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text("BalanceScreen"));
}
}
class InquiryScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text("InquiryScreen"));
}
}