Flutter change AppBottomNavigation body by tapping on button on another state - flutter

I have an AppBottomNavigation for five different states. In the first state, HomeScreen, I have a button. By tapping on this button, the state is to be changed to the third element of the AppBottomNavigation.How exactly can I implement this? I have already tried several times to call the method "bottomTapped" with index 2 via ScannderDummy. Unfortunately without success.
app_bottom_navigation.dart
class AppBottomNavigation extends StatefulWidget {
#override
_AppBottomNavigationState createState() => _AppBottomNavigationState();
}
class _AppBottomNavigationState extends State<AppBottomNavigation> {
int _selectedIndex = 0;
List<dynamic> menuItems = [
...
];
PageController pageController = PageController(
initialPage: 0,
keepPage: true,
);
Widget buildPageView() {
return PageView(
controller: pageController,
onPageChanged: (index) {
pageChanged(index);
},
children: <Widget>[
HomeScreen(),
CategoryScreen(),
ScannerScreen(),
CategoryScreen(),
ProfileSettingsScreen(),
],
);
}
pageChanged(int index) {
setState(() {
_selectedIndex = index;
});
}
bottomTapped(int index) {
setState(() {
_selectedIndex = index;
pageController.jumpToPage(index);
});
}
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark));
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
showUnselectedLabels: true,
unselectedItemColor: Colors.grey,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: GoogleFonts.outfit(
textStyle:
TextStyle(height: 1.5, fontSize: 10, fontWeight: FontWeight.bold),
),
unselectedLabelStyle: GoogleFonts.outfit(
textStyle: TextStyle(
height: 1.5,
fontSize: 10,
),
),
items: menuItems.map((i) {
return BottomNavigationBarItem(
icon: (i['icon']),
activeIcon: (i['icon']),
label: i['label'],
);
}).toList(),
currentIndex: _selectedIndex,
selectedItemColor: primaryColor,
onTap: (index) {
bottomTapped(index);
},
),
body: buildPageView(),
);
}
}
scanner_dummy.dart (Element from home_screen.dart)
class ScannerDummy extends StatelessWidget {
final AppBottomNavigation bn = new AppBottomNavigation();
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
},
child: Container(
margin:
EdgeInsets.only(top: 6, bottom: 28.0, left: 28.0, right: 28.0),
padding: EdgeInsets.symmetric(
horizontal: 16.0,
),
height: 125,
width: double.infinity,
decoration: BoxDecoration(
color: darkGrey,
borderRadius: BorderRadius.all(Radius.circular(15)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
LineIcons.retroCamera,
color: Colors.white,
size: 35.0,
),
Text(
'Artikel scannen & verkaufen',
textAlign: TextAlign.right,
style: GoogleFonts.outfit(
textStyle: TextStyle(
color: Colors.white,
fontSize: 16,
height: 2,
)),
),
],
)),
);
}
}
Thank you very much for your help.

Play with this widget. I am using callback method.
class ItemB extends StatefulWidget {
final VoidCallback callback;
ItemB({Key? key, required this.callback}) : super(key: key);
#override
State<ItemB> createState() => _ItemBState();
}
class _ItemBState extends State<ItemB> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Text("B"),
ElevatedButton(
onPressed: widget.callback,
child: Text("switch Tab"),
),
],
);
}
}
class HomeWidgetX extends StatefulWidget {
const HomeWidgetX({Key? key}) : super(key: key);
#override
State<HomeWidgetX> createState() => _HomeWidgetXState();
}
class _HomeWidgetXState extends State<HomeWidgetX> {
int currentTab = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: [
Text("A"),
ItemB(
callback: () {
setState(() {
currentTab = 0;
});
},
)
][currentTab]),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentTab,
onTap: (value) {
currentTab = value;
setState(() {});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.abc),
label: "",
),
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: "",
),
],
),
);
}
}

Related

Boolean always turn into 'False', spontaneously

I'm building 'What to Do list' app,
I made my own class Bucket(String, String, bool).
3rd property bool plays a role in checking if this task has done.
I designed if bool is false, unfulfilled task get fontweight.bold and icon is shown red, and if bool is true, fulfilled task get fontweight.normal and icons is shown green.
But, as soon as bool get 'true', it turns in to 'false', spontaneously.
No other codes are designed to get bool changed.
I don't understand why :(
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Homepage(),
);
}
}
class Homepage extends StatefulWidget {
Homepage({Key? key}) : super(key: key);
#override
State<Homepage> createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('WhattoDo List')),
floatingActionButton: IconButton(
onPressed: () async {
Bucket newbucket = await Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => PopUp()));
setState(() {
bucketlist.add(newbucket);
});
},
icon: Icon(Icons.add)),
body: bucketlist.isEmpty
? Center(child: Text('List is empty'))
: ListView.builder(
itemCount: bucketlist.length,
itemBuilder: (BuildContext context, int index) {
bool donecheck = bucketlist[index].isDone;
print(donecheck);
return Container(
margin: EdgeInsets.only(top: 5),
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.teal[200],
borderRadius: BorderRadius.circular(8)),
child: ListTile(
title: Text(
bucketlist[index].whattodo,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
decoration: donecheck
? TextDecoration.lineThrough
: TextDecoration.none),
),
subtitle: Text(
bucketlist[index].deadline,
style: TextStyle(fontSize: 15),
),
trailing: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Wanna remove?"),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
setState(() {
bucketlist.removeAt(index);
});
},
child: Text(
"OK",
style: TextStyle(color: Colors.pink),
),
),
],
);
},
);
},
icon: Icon(Icons.delete)),
leading: IconButton(
onPressed: () {
setState(() {
donecheck = !donecheck;
print(donecheck);
});
},
icon: Icon(
Icons.check,
color: donecheck ? Colors.green : Colors.red,
)),
));
}));
}
}
class Bucket {
String whattodo;
String deadline;
bool isDone;
Bucket(this.whattodo, this.deadline, this.isDone);
}
List<Bucket> bucketlist = [];
class PopUp extends StatefulWidget {
const PopUp({Key? key}) : super(key: key);
#override
State<PopUp> createState() => _PopUpState();
}
class _PopUpState extends State<PopUp> {
TextEditingController textController = TextEditingController();
TextEditingController textController2 = TextEditingController();
String? errorshowingtext;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), color: Colors.blue[200]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 150,
),
Text(
'Write down your task',
style: TextStyle(
fontSize: 25,
color: Colors.black,
fontWeight: FontWeight.normal),
),
SizedBox(
height: 25,
),
TextField(
autofocus: true,
decoration: InputDecoration(
hintText: 'What to Do', errorText: errorshowingtext),
style: TextStyle(fontSize: 22),
controller: textController,
),
TextField(
autofocus: true,
decoration: InputDecoration(
hintText: 'Deadline', errorText: errorshowingtext),
style: TextStyle(fontSize: 22),
controller: textController2,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('OK'),
onPressed: () {
String? job = textController.text;
String? line = textController2.text;
Bucket newbucket = Bucket(job, line, false);
if (job.isNotEmpty && line.isNotEmpty) {
setState(() {
Navigator.pop(context, newbucket);
});
} else {
setState(() {
errorshowingtext = 'contents missing';
});
}
},
),
SizedBox(
width: 150,
),
ElevatedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
],
)
],
),
),
);
}
}
You already have isDone to check the state. Your current method doesn't update the item check status, I prefer this way
class Bucket {
final String whattodo;
final String deadline;
final bool isDone;
Bucket({
required this.whattodo,
required this.deadline,
required this.isDone,
});
Bucket copyWith({
String? whattodo,
String? deadline,
bool? isDone,
}) {
return Bucket(
whattodo: whattodo ?? this.whattodo,
deadline: deadline ?? this.deadline,
isDone: isDone ?? this.isDone,
);
}
}
And the widget
class Homepage extends StatefulWidget {
const Homepage({Key? key}) : super(key: key);
#override
State<Homepage> createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('WhattoDo List')),
floatingActionButton: IconButton(
onPressed: () async {
Bucket newbucket = await Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => PopUp()));
setState(() {
bucketlist.add(newbucket);
});
},
icon: Icon(Icons.add)),
body: bucketlist.isEmpty
? Center(child: Text('List is empty'))
: ListView.builder(
itemCount: bucketlist.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(top: 5),
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.teal[200],
borderRadius: BorderRadius.circular(8)),
child: ListTile(
title: Text(
bucketlist[index].whattodo,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
decoration: bucketlist[index].isDone
? TextDecoration.lineThrough
: TextDecoration.none),
),
subtitle: Text(
bucketlist[index].deadline,
style: TextStyle(fontSize: 15),
),
trailing: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Wanna remove?"),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
setState(() {
bucketlist.removeAt(index);
});
},
child: Text(
"OK",
style: TextStyle(color: Colors.pink),
),
),
],
);
},
);
},
icon: Icon(Icons.delete)),
leading: IconButton(
onPressed: () {
setState(() {
bucketlist[index] = bucketlist[index]
.copyWith(isDone: !bucketlist[index].isDone);
});
},
icon: Icon(
Icons.check,
color: bucketlist[index].isDone
? Colors.green
: Colors.red,
)),
));
},
),
);
}
}
you have to update the list todo value. not the Widget of Listile value.
change this
leading: IconButton(
onPressed: () {
setState(() {
// your code: donecheck = !donecheck;
bucketlist[index].isDone = !donecheck; // change to this
print(donecheck);
});
},

How to switch class when a tab is clicked using google_nav_bar Flutter

I'm using google_nav_bar and line_icons from pub.
I have 3 classes named Home(), Likes(), Profile(). I want to switch classes when a bottom navigation bar tab is clicked, I have made a list of the classes, but I'm not sure how to change the classes when a tab is clicked.
Here is the code I have so far:
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _page = 0;
final screens = [
Home(),
Likes(),
Profile()
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
bottomNavigationBar: GNav(
rippleColor: Colors.grey[300],
hoverColor: Colors.grey[100],
gap: 8,
activeColor: Colors.black,
iconSize: 24,
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
duration: Duration(milliseconds: 400),
tabBackgroundColor: Colors.grey[100],
color: Colors.black,
tabs: [
GButton(
icon: LineIcons.home,
text: 'Home',
),
GButton(
icon: LineIcons.heart,
text: 'Likes',
),
GButton(
icon: LineIcons.user,
text: 'Profile',
),
],
selectedIndex: _page,
onTabChange: (index) {
setState(() {
_page = index;
});
},
),
body: /*new Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: _page == 0
? Home()
: _page == 1
? Likes()
: Profile(),
),*/
Center(
child: screens.elementAt(_page),
),
);
}
}
I would like to navigate the bottom navigation bar to the Likes() class when the second tab is clicked and navigate to the Profile() class when the third navigation bar is clicked..
Everything is working fine. Replace Tab widget with different widgets like Home(), Likes(), Profile()..
Now it will be like
class Tab extends StatelessWidget {
final int tab;
const Tab({
Key? key,
required this.tab,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Text("$tab"),
);
}
}
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _page = 0;
final screens = const [Tab(tab: 1), Tab(tab: 2), Tab(tab: 3)];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
bottomNavigationBar: GNav(
rippleColor: Colors.grey[300]!,
hoverColor: Colors.grey[100]!,
gap: 8,
activeColor: Colors.black,
iconSize: 24,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
duration: const Duration(milliseconds: 400),
tabBackgroundColor: Colors.grey[100]!,
color: Colors.black,
tabs: const [
GButton(
icon: LineIcons.home,
text: 'Home',
),
GButton(
icon: LineIcons.heart,
text: 'Likes',
),
GButton(
icon: LineIcons.user,
text: 'Profile',
),
],
selectedIndex: _page,
onTabChange: (index) {
setState(() {
_page = index;
});
},
),
body: Center(
child: screens[_page],
),
);
}
}
Does it solve in your case?
Try below code hope its helpful to you. Just add your Widgets in your classes
Your BottomBar Widget.
class Example extends StatefulWidget {
#override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
int _selectedIndex = 0;
static const List<Widget> _widgetOptions = <Widget>[
Home(),
Likes(),
Profile(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
elevation: 20,
title: const Text('GoogleNavBar'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 20,
color: Colors.black.withOpacity(.1),
)
],
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8),
child: GNav(
rippleColor: Colors.grey[300]!,
hoverColor: Colors.grey[100]!,
gap: 8,
activeColor: Colors.black,
iconSize: 24,
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
duration: Duration(milliseconds: 400),
tabBackgroundColor: Colors.grey[100]!,
color: Colors.black,
tabs: [
GButton(
icon: LineIcons.home,
text: 'Home',
),
GButton(
icon: LineIcons.heart,
text: 'Likes',
),
GButton(
icon: LineIcons.user,
text: 'Profile',
),
],
selectedIndex: _selectedIndex,
onTabChange: (index) {
setState(() {
_selectedIndex = index;
});
},
),
),
),
),
);
}
}
Your Home Class
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(
'Home',
);
}
}
Your Likes Class
class Likes extends StatelessWidget {
const Likes({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(
'Likes',
);
}
}
Your Profile Class
class Profile extends StatelessWidget {
const Profile({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(
'Profile',
);
}
}
For a better switch use IndexedStack:
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _page = 0;
final screens = [
Home(),
Likes(),
Profile()
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
bottomNavigationBar: GNav(
rippleColor: Colors.grey[300],
hoverColor: Colors.grey[100],
gap: 8,
activeColor: Colors.black,
iconSize: 24,
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
duration: Duration(milliseconds: 400),
tabBackgroundColor: Colors.grey[100],
color: Colors.black,
tabs: [
GButton(
icon: LineIcons.home,
text: 'Home',
),
GButton(
icon: LineIcons.heart,
text: 'Likes',
),
GButton(
icon: LineIcons.user,
text: 'Profile',
),
],
selectedIndex: _page,
onTabChange: (index) {
setState(() {
_page = index;
});
},
),
body: IndexedStack(
index: _page,
children: screens,
),
);
}
}

Flutter : How do I make BottomAppBar of buttomNavigationBar appear in every screens I have?

I'm using BottomAppBar instead of BottomNavigationBar widget. And I want to make this bar appear in every screen I have. Again, I don't want to use BottomNavigationBar.
bottomNavigationBar: BottomAppBar(
child: Container(
height: 60,
width: width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// Home
MaterialButton(
onPressed: () {
setState(() {
currentScreen = HomeScreenWrapper();
currentTab = 0;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.home,
color: currentTab == 0 ? primaryColor : Colors.grey,
),
Text(
AppLocalizations.of(context).translate('HOME'),
style: TextStyle(
color: currentTab == 0 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
// Dashboard
MaterialButton(
onPressed: () {
setState(() {
currentScreen = PointScreenWrapper();
currentTab = 2;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset(
'assets/images/dashboard.svg',
color: currentTab == 2 ? primaryColor : null,
),
// Icon(
// // Icons.show_chart,
// Icons.dashboard,
// //Icons.crop_square,
// color: currentTab == 2 ? primaryColor : Colors.grey,
// ),
Text(
AppLocalizations.of(context).translate('DASHBOARD'),
style: TextStyle(
color: currentTab == 2 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
// //Make a dummy space between
SizedBox(
width: width / 5,
),
// Score
MaterialButton(
onPressed: () {
setState(() {
currentScreen = ChallengeScreen();
currentTab = 1;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset(
'assets/images/ranking.svg',
color: currentTab == 1 ? primaryColor : Colors.grey,
),
// Icon(
// Icons.star,
// color: currentTab == 1 ? primaryColor : Colors.grey,
// size: 30,
// ),
Text(
AppLocalizations.of(context).translate('RATING'),
style: TextStyle(
color: currentTab == 1 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
//
MaterialButton(
onPressed: () {
setState(() {
currentScreen = ProfileWrapper();
currentTab = 3;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SvgPicture.asset(
'assets/images/profile.svg',
color: currentTab == 3 ? primaryColor : Colors.grey,
),
// Icon(
// Icons.settings,
// color: currentTab == 3 ? primaryColor : Colors.grey,
// ),
Text(
AppLocalizations.of(context).translate('PROFILE'),
style: TextStyle(
color: currentTab == 3 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
],
),
),
shape: CircularNotchedRectangle(),
),
How do I do that? Please give me some pointer regarding this issue. I am looking forward to hearing from anyone of you. Thank you in advance...
You can copy paste run full code below
You can use PageView when click on MaterialButton call bottomTapped with index
code snippet
Widget buildPageView() {
return PageView(
controller: pageController,
onPageChanged: (index) {
pageChanged(index);
},
children: <Widget>[
Red(),
Blue(),
Yellow(),
Green(),
],
);
}
#override
void initState() {
super.initState();
}
void pageChanged(int index) {
setState(() {
currentTab = index;
});
}
void bottomTapped(int index) {
setState(() {
currentTab = index;
pageController.animateToPage(index,
duration: Duration(milliseconds: 500), curve: Curves.ease);
});
}
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double width;
Color primaryColor = Colors.blue;
int currentTab = 0;
PageController pageController = PageController(
initialPage: 0,
keepPage: true,
);
Widget buildPageView() {
return PageView(
controller: pageController,
onPageChanged: (index) {
pageChanged(index);
},
children: <Widget>[
Red(),
Blue(),
Yellow(),
Green(),
],
);
}
#override
void initState() {
super.initState();
}
void pageChanged(int index) {
setState(() {
currentTab = index;
});
}
void bottomTapped(int index) {
setState(() {
currentTab = index;
pageController.animateToPage(index,
duration: Duration(milliseconds: 500), curve: Curves.ease);
});
}
#override
Widget build(BuildContext context) {
width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: buildPageView(),
bottomNavigationBar: BottomAppBar(
child: Container(
height: 60,
width: width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// Home
MaterialButton(
onPressed: () {
bottomTapped(0);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.home,
color: currentTab == 0 ? primaryColor : Colors.grey,
),
Text(
'HOME',
style: TextStyle(
color: currentTab == 0 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
// Dashboard
MaterialButton(
onPressed: () {
bottomTapped(1);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
'https://picsum.photos/20/20?image=9',
color: currentTab == 1 ? primaryColor : null,
),
// Icon(
// // Icons.show_chart,
// Icons.dashboard,
// //Icons.crop_square,
// color: currentTab == 2 ? primaryColor : Colors.grey,
// ),
Text(
'DASHBOARD',
style: TextStyle(
color: currentTab == 1 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
// //Make a dummy space between
SizedBox(
width: width / 10,
),
// Score
MaterialButton(
onPressed: () {
bottomTapped(2);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
'https://picsum.photos/20/20?image=9',
color: currentTab == 2 ? primaryColor : Colors.grey,
),
// Icon(
// Icons.star,
// color: currentTab == 1 ? primaryColor : Colors.grey,
// size: 30,
// ),
Text(
'RATING',
style: TextStyle(
color: currentTab == 2 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
//
MaterialButton(
onPressed: () {
bottomTapped(3);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
'https://picsum.photos/20/20?image=9',
color: currentTab == 3 ? primaryColor : Colors.grey,
),
// Icon(
// Icons.settings,
// color: currentTab == 3 ? primaryColor : Colors.grey,
// ),
Text(
'PROFILE',
style: TextStyle(
color: currentTab == 3 ? primaryColor : Colors.grey,
fontSize: 10,
),
),
],
),
minWidth: width / 5,
),
],
),
),
shape: CircularNotchedRectangle(),
),
);
}
}
class Red extends StatefulWidget {
#override
_RedState createState() => _RedState();
}
class _RedState extends State<Red> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
);
}
}
class Blue extends StatefulWidget {
#override
_BlueState createState() => _BlueState();
}
class _BlueState extends State<Blue> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blueAccent,
);
}
}
class Yellow extends StatefulWidget {
#override
_YellowState createState() => _YellowState();
}
class _YellowState extends State<Yellow> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellowAccent,
);
}
}
class Green extends StatefulWidget {
#override
_GreenState createState() => _GreenState();
}
class _GreenState extends State<Green> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.greenAccent,
);
}
}
#Eren is right you cannot use same widget on every screen its against the flutter physics because its the scope of Scaffold field but you can use Common bottom-sheet class for every Page and pass that page context.
In flutter a good practice would be to create a widget and use that in every scaffold. Here is an example code I am using in my application
import 'package:flutter/material.dart';
class AppBottomNavigationBar extends StatelessWidget {
final int selectedIndex;
const AppBottomNavigationBar({
Key key,
#required this.selectedIndex,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail_outline),
title: Text('Messages'),
),
BottomNavigationBarItem(
icon: Icon(Icons.explore),
title: Text('Explore'),
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
title: Text('Profile'),
),
],
currentIndex: selectedIndex,
onTap: (int index) {
if (selectedIndex == index) {
return;
}
if (index == 0) {
Navigator.of(context).pushReplacementNamed('/home');
} else if (index == 1) {
Navigator.of(context).pushReplacementNamed('/messages');
} else if (index == 2) {
Navigator.of(context).pushReplacementNamed('/explore');
} else if (index == 3) {
Navigator.of(context).pushReplacementNamed('/profile');
}
},
);
}
}

How to handle a one widget state from another widget state?

I am making a flutter application that have shopping cart. Right now, i am just working on a single functionality of it when we select a particular product, after increase or decrease its quantity then our cart doesnot show the quantity of that item on the cart immediately. we have to go to another widget then it update the count on cart.
Note: HomeScreen comprise of two stateful widget, one is bottom navigation bar which have cart and other icons along with other icons and their respective UI's and other one is Product screen which is showing all our products, and in my product screen i used listview and in its UI i used - and + icons to increase or decrease its quantity. I am sharing the code of the widget - and +(a small portion of product screen) on which i want to implement this functionality.
This is the video link to show
https://youtu.be/3qqVpmWguys
HomeScreen:
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
//static _HomeScreenState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<_HomeScreenState>());
int _currentindex = 0;
var cart;
final List<Widget> children = [
ProductScreen(),
OrderScreen(),
CartScreen(),
AccountScreen(),
];
List<BottomNavigationBarItem> _buildNavigationItems() {
var bloc = Provider.of<CartManager>(context);
int totalCount = bloc.getCart().length;
setState(() {
totalCount;
});
return <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
Icons.reorder,
size: 30,
color: Colors.white,
),
title: Text(
'Product',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.add_alert,
size: 30,
color: Colors.white,
),
title: Text(
'Order',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
BottomNavigationBarItem(
icon: Stack(
children: <Widget>[
Icon(
Icons.shopping_cart,
size: 30,
color: Colors.white,
),
Positioned(
bottom: 12.0,
right: 0.0,
child: Container(
constraints: BoxConstraints(
minWidth: 20.0,
minHeight: 20.0,
),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10.0),
),
child: Center(
child: Text(
'$totalCount',
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
)
],
),
title: Text(
'Cart',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.lock,
size: 30,
color: Colors.white,
),
title: Text(
'Account',
style: TextStyle(fontSize: 15, color: Colors.white),
),
),
];
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomPadding: true,
body: children[_currentindex],
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.transparent,
backgroundColor: Colors.orange,
onTap: onNavigationTapbar,
currentIndex: _currentindex,
items: _buildNavigationItems(),
type: BottomNavigationBarType.fixed,
),
),
);
}
void onNavigationTapbar(int index) {
setState(() {
_currentindex = index;
});
}
}
ProductScreen incrementor or decrementor:
class TEProductIncrementor extends StatefulWidget {
var product;
TEProductIncrementor({
this.product,
});
#override
_TEProductIncrementorState createState() => new _TEProductIncrementorState();
}
class _TEProductIncrementorState extends State<TEProductIncrementor> {
int totalCount = 0;
#override
Widget build(BuildContext context) {
var cartmanager = CartManager();
void decrementsavecallback() {
// bloc.decreaseToCart(widget.ctlist);
//CartManager().updateToCart(totalCount.toString(), widget.ctlist);
setState(() {
if (totalCount > 0) {
totalCount--;
cartmanager.updateToCart(totalCount.toString(),widget.product);
}
});
}
void increasesavecallback() {
setState(() {
totalCount++;
cartmanager.updateToCart(totalCount.toString(),widget.product);
});
}
return Container(
margin: EdgeInsets.only(top: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), color: Colors.orange),
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
iconSize: 30,
icon: new Icon(
Icons.remove,
),
onPressed: () {
decrementsavecallback();
},
),
Text(
totalCount.toString(),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
IconButton(
iconSize: 30,
icon: new Icon(Icons.add),
onPressed: () {
increasesavecallback();
},
)
],
),
),
);
}
}
You can pass the entire function into a widget just implemented like below
class Parent extends StatefulWidget {
#override
_ParentState createState() => _ParentState();
}
class _ParentState extends State<Parent> {
#override
Widget build(BuildContext context) {
return Button(
(){
setState(() {
///define your logic here
});
}
);
}
}
class Button extends StatelessWidget {
final Function onTap;
Button(this.onTap);
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: onTap,
);
}
}
But if your project is pretty big then I will recommend you to use any state management libraries just like redux or mobx.
https://pub.dev/packages/mobx
You can use BLoC pattern for this,
Here is full answer and demo code that you can check.
Why not use keys?
Assign at both widget a GlobalKey and then, when you need to update that state you just need to call
yourGlobalKeyName.currentState.setState((){
//your code
});

Flutter: BottomNavigationBar rebuilds Page on change of tab

I have a problem with my BottomNavigationBar in Flutter. I want to keep my page alive if I change the tabs.
here my implementation
BottomNavigation
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomeState();
}
}
class _HomeState extends State<Home> {
int _currentIndex = 0;
List<Widget> _children;
final Key keyOne = PageStorageKey("IndexTabWidget");
#override
void initState() {
_children = [
IndexTabWidget(key: keyOne),
PlaceholderWidget(Colors.green),
NewsListWidget(),
ShopsTabWidget(),
PlaceholderWidget(Colors.blue),
];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(MyApp.appName),
textTheme: Theme.of(context).textTheme.apply(
bodyColor: Colors.black,
displayColor: Colors.blue,
),
),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped,
key: IHGApp.globalKey,
fixedColor: Colors.green,
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(Icons.message),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(Icons.perm_contact_calendar),
title: Container(height: 0.0),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Container(height: 0.0),
),
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
Column buildButtonColumn(IconData icon) {
Color color = Theme.of(context).primaryColor;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color),
],
);
}
}
This is my index page (first tab):
class IndexTabWidget extends StatefulWidget {
IndexTabWidget({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return new IndexTabState();
}
}
class IndexTabState extends State<IndexTabWidget>
with AutomaticKeepAliveClientMixin {
List<News> news = List();
FirestoreNewsRepo newsFirestore = FirestoreNewsRepo();
#override
Widget build(BuildContext context) {
return Material(
color: Colors.white,
child: new Container(
child: new SingleChildScrollView(
child: new ConstrainedBox(
constraints: new BoxConstraints(),
child: new Column(
children: <Widget>[
HeaderWidget(
CachedNetworkImageProvider(
'https://static1.fashionbeans.com/wp-content/uploads/2018/04/50-barbershop-top-savill.jpg',
),
"",
),
AboutUsWidget(),
Padding(
padding: const EdgeInsets.all(16.0),
child: SectionTitleWidget(title: StringStorage.salonsTitle),
),
StreamBuilder(
stream: newsFirestore.observeNews(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
} else {
news = snapshot.data;
return Column(
children: <Widget>[
ShopItemWidget(
AssetImage('assets/images/picture.png'),
news[0].title,
news[0],
),
ShopItemWidget(
AssetImage('assets/images/picture1.png'),
news[1].title,
news[1],
)
],
);
}
},
),
Padding(
padding: const EdgeInsets.only(
left: 16.0, right: 16.0, bottom: 16.0),
child: SectionTitleWidget(title: StringStorage.galleryTitle),
),
GalleryCategoryCarouselWidget(),
],
),
),
),
),
);
}
#override
bool get wantKeepAlive => true;
}
So if I switch from my index tab to any other tab and back to the index tab, the index tab will always rebuild. I debugged it and saw that the build function is always being called on the tab switch.
Could you guys help me out with this issue?
Thank you a lot
Albo
None of the previous answers worked out for me.
The solution to keep the pages alive when switching the tabs is wrapping your Pages in an IndexedStack.
class Tabbar extends StatefulWidget {
Tabbar({this.screens});
static const Tag = "Tabbar";
final List<Widget> screens;
#override
State<StatefulWidget> createState() {
return _TabbarState();
}
}
class _TabbarState extends State<Tabbar> {
int _currentIndex = 0;
Widget currentScreen;
#override
Widget build(BuildContext context) {
var _l10n = PackedLocalizations.of(context);
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: widget.screens,
),
bottomNavigationBar: BottomNavigationBar(
fixedColor: Colors.black,
type: BottomNavigationBarType.fixed,
onTap: onTabTapped,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.format_list_bulleted),
title: new Text(_l10n.tripsTitle),
),
BottomNavigationBarItem(
icon: new Icon(Icons.settings),
title: new Text(_l10n.settingsTitle),
)
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
You need to wrap every root page (the first page you see when you press a bottom navigation item) with a navigator and put them in a Stack.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final int _pageCount = 2;
int _pageIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: _body(),
bottomNavigationBar: _bottomNavigationBar(),
);
}
Widget _body() {
return Stack(
children: List<Widget>.generate(_pageCount, (int index) {
return IgnorePointer(
ignoring: index != _pageIndex,
child: Opacity(
opacity: _pageIndex == index ? 1.0 : 0.0,
child: Navigator(
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute(
builder: (_) => _page(index),
settings: settings,
);
},
),
),
);
}),
);
}
Widget _page(int index) {
switch (index) {
case 0:
return Page1();
case 1:
return Page2();
}
throw "Invalid index $index";
}
BottomNavigationBar _bottomNavigationBar() {
final theme = Theme.of(context);
return new BottomNavigationBar(
fixedColor: theme.accentColor,
currentIndex: _pageIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text("Page 1"),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
title: Text("Page 2"),
),
],
onTap: (int index) {
setState(() {
_pageIndex = index;
});
},
);
}
}
The pages will be rebuild but you should separate your business logic from you UI anyway. I prefer to use the BLoC pattern but you can also use Redux, ScopedModel or InhertedWidget.
Just use an IndexedStack
IndexedStack(
index: selectedIndex,
children: <Widget> [
ProfileScreen(),
MapScreen(),
FriendsScreen()
],
)
I'm not sure but CupertinoTabBar would help.
If you don't want it, this video would be great url.
import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => new _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final List<dynamic> pages = [
new Page1(),
new Page2(),
new Page3(),
new Page4(),
];
int currentIndex = 0;
#override
Widget build(BuildContext context) {
return new WillPopScope(
onWillPop: () async {
await Future<bool>.value(true);
},
child: new CupertinoTabScaffold(
tabBar: new CupertinoTabBar(
iconSize: 35.0,
onTap: (index) {
setState(() => currentIndex = index);
},
activeColor: currentIndex == 0 ? Colors.white : Colors.black,
inactiveColor: currentIndex == 0 ? Colors.green : Colors.grey,
backgroundColor: currentIndex == 0 ? Colors.black : Colors.white,
currentIndex: currentIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.looks_one),
title: Text(''),
),
BottomNavigationBarItem(
icon: Icon(Icons.looks_two),
title: Text(''),
),
BottomNavigationBarItem(
icon: Icon(Icons.looks_3),
title: Text(''),
),
BottomNavigationBarItem(
icon: Icon(Icons.looks_4),
title: Text(''),
),
],
),
tabBuilder: (BuildContext context, int index) {
return new DefaultTextStyle(
style: const TextStyle(
fontFamily: '.SF UI Text',
fontSize: 17.0,
color: CupertinoColors.black,
),
child: new CupertinoTabView(
routes: <String, WidgetBuilder>{
'/Page1': (BuildContext context) => new Page1(),
'/Page2': (BuildContext context) => new Page2(),
'/Page3': (BuildContext context) => new Page3(),
'/Page4': (BuildContext context) => new Page4(),
},
builder: (BuildContext context) {
return pages[currentIndex];
},
),
);
},
),
);
}
}
class Page1 extends StatefulWidget {
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
String title;
#override
void initState() {
title = 'Page1';
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
leading: new IconButton(
icon: new Icon(Icons.text_fields),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => Page13()));
},
)),
body: new Center(
child: new Text(title),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Page2'),
leading: new IconButton(
icon: new Icon(Icons.airline_seat_flat_angled),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => Page12()));
},
)),
body: new Center(
child: Column(
children: <Widget>[
CupertinoSlider(
value: 25.0,
min: 0.0,
max: 100.0,
onChanged: (double value) {
print(value);
}
),
],
),
),
);
}
}
class Page3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Page3'),
),
body: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
child: new Text('Cupertino'),
textColor: Colors.white,
color: Colors.red,
onPressed: () {
List<int> list = List.generate(10, (int i) => i + 1);
list.shuffle();
var subList = (list.sublist(0, 5));
print(subList);
subList.forEach((li) => list.remove(li));
print(list);
}
),
new SizedBox(height: 30.0),
new RaisedButton(
child: new Text('Android'),
textColor: Colors.white,
color: Colors.lightBlue,
onPressed: () {
var mes = 'message';
var messa = 'メッセージ';
var input = 'You have a new message';
if (input.contains(messa) || input.contains(mes)) {
print('object');
} else {
print('none');
}
}
),
],
),
),
);
}
}
class Page4 extends StatelessWidget {
static List<int> ints = [1, 2, 3, 4, 5];
static _abc() {
print(ints.last);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Page4'),
),
body: new Center(
child: new RaisedButton(
child: new Text('Static', style: new TextStyle(color: Colors.white)),
color: Colors.lightBlue,
onPressed: _abc,
)),
);
}
}
class Page12 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Page12'),
actions: <Widget>[
new FlatButton(
child: new Text('GO'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => Page13()));
},
)
],
),
body: new Center(
child: new RaisedButton(
child: new Text('Swiper', style: new TextStyle(color: Colors.white)),
color: Colors.redAccent,
onPressed: () {},
)),
);
}
}
class Page13 extends StatefulWidget {
#override
_Page13State createState() => _Page13State();
}
class _Page13State extends State<Page13> with SingleTickerProviderStateMixin {
final List<String> _productLists = Platform.isAndroid
? [
'android.test.purchased',
'point_1000',
'5000_point',
'android.test.canceled',
]
: ['com.cooni.point1000', 'com.cooni.point5000'];
String _platformVersion = 'Unknown';
List<IAPItem> _items = [];
List<PurchasedItem> _purchases = [];
#override
void initState() {
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
String platformVersion;
try {
platformVersion = await FlutterInappPurchase.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
var result = await FlutterInappPurchase.initConnection;
print('result: $result');
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
// refresh items for android
String msg = await FlutterInappPurchase.consumeAllItems;
print('consumeAllItems: $msg');
}
Future<Null> _buyProduct(IAPItem item) async {
try {
PurchasedItem purchased = await FlutterInappPurchase.buyProduct(item.productId);
print('purchased: ${purchased.toString()}');
} catch (error) {
print('$error');
}
}
Future<Null> _getProduct() async {
List<IAPItem> items = await FlutterInappPurchase.getProducts(_productLists);
print(items);
for (var item in items) {
print('${item.toString()}');
this._items.add(item);
}
setState(() {
this._items = items;
this._purchases = [];
});
}
Future<Null> _getPurchases() async {
List<PurchasedItem> items = await FlutterInappPurchase.getAvailablePurchases();
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}
Future<Null> _getPurchaseHistory() async {
List<PurchasedItem> items = await FlutterInappPurchase.getPurchaseHistory();
for (var item in items) {
print('${item.toString()}');
this._purchases.add(item);
}
setState(() {
this._items = [];
this._purchases = items;
});
}
List<Widget> _renderInApps() {
List<Widget> widgets = this
._items
.map((item) => Container(
margin: EdgeInsets.symmetric(vertical: 10.0),
child: Container(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 5.0),
child: Text(
item.toString(),
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
),
),
),
FlatButton(
color: Colors.orange,
onPressed: () {
print("---------- Buy Item Button Pressed");
this._buyProduct(item);
},
child: Row(
children: <Widget>[
Expanded(
child: Container(
height: 48.0,
alignment: Alignment(-1.0, 0.0),
child: Text('Buy Item'),
),
),
],
),
),
],
),
),
))
.toList();
return widgets;
}
List<Widget> _renderPurchases() {
List<Widget> widgets = this
._purchases
.map((item) => Container(
margin: EdgeInsets.symmetric(vertical: 10.0),
child: Container(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 5.0),
child: Text(
item.toString(),
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
),
),
)
],
),
),
))
.toList();
return widgets;
}
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width-20;
double buttonWidth=(screenWidth/3)-20;
return new Scaffold(
appBar: new AppBar(),
body: Container(
padding: EdgeInsets.all(10.0),
child: ListView(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
child: Text(
'Running on: $_platformVersion\n',
style: TextStyle(fontSize: 18.0),
),
),
Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
width: buttonWidth,
height: 60.0,
margin: EdgeInsets.all(7.0),
child: FlatButton(
color: Colors.amber,
padding: EdgeInsets.all(0.0),
onPressed: () async {
print("---------- Connect Billing Button Pressed");
await FlutterInappPurchase.initConnection;
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
alignment: Alignment(0.0, 0.0),
child: Text(
'Connect Billing',
style: TextStyle(
fontSize: 16.0,
),
),
),
),
),
Container(
width: buttonWidth,
height: 60.0,
margin: EdgeInsets.all(7.0),
child: FlatButton(
color: Colors.amber,
padding: EdgeInsets.all(0.0),
onPressed: () async {
print("---------- End Connection Button Pressed");
await FlutterInappPurchase.endConnection;
setState(() {
this._items = [];
this._purchases = [];
});
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
alignment: Alignment(0.0, 0.0),
child: Text(
'End Connection',
style: TextStyle(
fontSize: 16.0,
),
),
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
width: buttonWidth,
height: 60.0,
margin: EdgeInsets.all(7.0),
child: FlatButton(
color: Colors.green,
padding: EdgeInsets.all(0.0),
onPressed: () {
print("---------- Get Items Button Pressed");
this._getProduct();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
alignment: Alignment(0.0, 0.0),
child: Text(
'Get Items',
style: TextStyle(
fontSize: 16.0,
),
),
),
)),
Container(
width: buttonWidth,
height: 60.0,
margin: EdgeInsets.all(7.0),
child: FlatButton(
color: Colors.green,
padding: EdgeInsets.all(0.0),
onPressed: () {
print(
"---------- Get Purchases Button Pressed");
this._getPurchases();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
alignment: Alignment(0.0, 0.0),
child: Text(
'Get Purchases',
style: TextStyle(
fontSize: 16.0,
),
),
),
)),
Container(
width: buttonWidth,
height: 60.0,
margin: EdgeInsets.all(7.0),
child: FlatButton(
color: Colors.green,
padding: EdgeInsets.all(0.0),
onPressed: () {
print(
"---------- Get Purchase History Button Pressed");
this._getPurchaseHistory();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
alignment: Alignment(0.0, 0.0),
child: Text(
'Get Purchase History',
style: TextStyle(
fontSize: 16.0,
),
),
),
)),
]),
],
),
Column(
children: this._renderInApps(),
),
Column(
children: this._renderPurchases(),
),
],
),
],
),
),
);
}
}
Use IndexedStack widget:
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: IndexedStack(
index: _currentIndex,
children: const [
HomePage(),
SettingsPage(),
],
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (int index) => setState(() => _currentIndex = index),
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
print('build home');
return Center(child: Text('Home'));
}
}
class SettingsPage extends StatelessWidget {
const SettingsPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
print('build settings');
return Center(child: Text('Settings'));
}
}
Make sure the IndexedStack children widget list is constant. This will prevent the widgets from rebuilding when setState() is called.
IndexedStack(
index: _currentIndex,
children: const [
HomeWidget(),
SettingsWidget(),
],
),
The problem with IndexedStack is that all the widgets will be built at the same time when IndexedStack is initialized. For small widgets (like the example above), it won't be a problem. But for big widgets, you may see some performance issues.
Consider using the lazy_load_indexed_stack package. According to the package:
[LazyLoadIndexedStack widget] builds the required widget only when it is needed, and returns the pre-built widget when it is needed again
Again, make sure the LazyLoadIndexedStack children widgets are constant, otherwise they will keep rebuilding when setState is called.
If, you just need to remember the scroll position inside a list, the best option is to simply use a PageStoreKey object for the key property:
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
key: PageStorageKey<String>('some-list-key'),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () => _onElementTapped(index),
child: makeCard(items[index])
);
},
),
);
}
According to https://docs.flutter.io/flutter/widgets/PageStorageKey-class.html, this should work on ANY scrollable widget.
if i use the IndexedStack in the body it is loading only the main screen content not every other screen which is present in the bottom nativation bar.
Using IndexedStack with bloc pattern solved everthing.