How to change a bottom navigation bar icon in the setState() function in flutter? - flutter

I'm building a food ordering app in flutter. What I want is, when the user adds items to the cart, I want the cart icon in the bottom navigation bar to obtain a red dot on top to notify the user of the addition of items to the cart.
To achieve this, I have created a global variable called no_of_cart_items and when the user adds an item to the cart, I increment this variable in the setState() function as follows:
setState(() {
GlobalVariables.no_of_cart_items+=1;
// change icon here
});
In this setState() function, I wish to change the icon in the bottom navigation bar. How should I do this?
Thank you.
FULL CODE
This is main.dart
//import lines
void main() => runApp(CanteenApp());
class CanteenApp extends StatefulWidget {
#override
_CanteenAppState createState() => _CanteenAppState();
}
class _CanteenAppState extends State<CanteenApp> {
int _currentindex=0; // index of bottom tab
int admin=GlobalVariables.admin;
BottomNavigationBadge badger = new BottomNavigationBadge(
backgroundColor: Colors.red,
badgeShape: BottomNavigationBadgeShape.circle,
textColor: Colors.white,
position: BottomNavigationBadgePosition.topRight,
textSize: 8);
Widget callpage(int currentIndex) {
switch (currentIndex) {
case 0: return UserProfile();
case 1: return Menu();
case 2: return Cart();
break;
default: return UserProfile();
}
}
#override
Widget build(BuildContext context) {
if(admin==1 && _currentindex==2) {
//if you're the admin and have called the history page
return MaterialApp(
debugShowCheckedModeBanner: false,
home: DefaultTabController(
length: 2,
child: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: PreferredSize(
preferredSize: Size.fromHeight(80.0),
child: AppBar(
bottom: TabBar(
indicatorColor: Colors.white,
indicatorWeight: 5,
tabs: <Widget>[
Tab(
child: Align(
alignment: Alignment.center,
child: Text(
'Order History',
style: TextStyle(
fontSize: 20
),
)
)
),
Tab(
child: Align(
alignment: Alignment.center,
child: Text(
'Deposit / Withdraw\nHistory',
style: TextStyle(
fontSize: 17
),
textAlign: TextAlign.center,
)
)
),
],
),
),
),
body: TabBarView(
children: <Widget>[
AdminOrderHistory(),
DepositWithdrawHistory()
],
),
bottomNavigationBar: BottomNavigationBar(
elevation: 10,
currentIndex: _currentindex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
title: Text('Profile'),
),
BottomNavigationBarItem(
icon: Icon(Icons.restaurant_menu),
title: Text('Menu'),
),
BottomNavigationBarItem(
icon: Icon(Icons.history),
title: Text('History'),
),
],
onTap: (index){
setState(() {
_currentindex=index;
});
}
),
),
),
theme: appTheme,
);
}
else if(admin==1){
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
resizeToAvoidBottomPadding: false,
body: callpage(_currentindex),
bottomNavigationBar: BottomNavigationBar(
elevation: 10,
currentIndex: _currentindex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
title: Text('Profile'),
),
BottomNavigationBarItem(
icon: Icon(Icons.restaurant_menu),
title: Text('Menu'),
),
BottomNavigationBarItem(
icon: Icon(Icons.history),
title: Text('History'),
),
],
onTap: (index){
setState(() {
_currentindex=index;
});
}
),
),
theme: appTheme,
);
}
else if(admin==0){
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
resizeToAvoidBottomPadding: false,
body: callpage(_currentindex),
bottomNavigationBar:BottomNavigationBar(
elevation: 10,
currentIndex: _currentindex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
title: Text('Profile'),
),
BottomNavigationBarItem(
icon: Icon(Icons.restaurant_menu),
title: Text('Menu'),
),
BottomNavigationBarItem(
title: Text('Cart'),
icon: Badge(
showBadge: true,
badgeContent: Text(
GlobalVariables.no_of_cart_items.toString(),
style: TextStyle(
color: Colors.white
),
),
child: Icon(Icons.shopping_cart)
)
),
],
onTap: (index){
setState(() {
_currentindex=index;
});
}
),
),
theme: appTheme,
);
}
}
}
This is menu.dart
//import lines
int admin=GlobalVariables.admin;
List snacksmenuitems=[
['Vada Pav', 15],
['Samosa Pav', 15],
['Punjabi Samosa', 25],
['Pav', 5]
];
List ricemenuitems=[
['Fried Rice', 62],
['Schezwan Rice', 69],
['Singapore Rice', 69],
['Manchow Rice', 73],
];
class Menu extends StatefulWidget {
#override
_MenuState createState() => _MenuState();
}
class _MenuState extends State<Menu> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
MenuTopPart(),
MenuBottomPart(),
],
);
}
}
Color firstColor = Color(0xFFF47D15);
Color secondColor = Color(0xFFEF772C);
class MenuTopPart extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
ClipPath(
clipper: CustomShapeClipper(),
child: Container(
height:140.0,
width:MediaQuery.of(context).size.width,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [firstColor, secondColor],
)
),
child: Column(
children: <Widget>[
SizedBox(height: 53.0),
Text(
'MENU',
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
textAlign: TextAlign.center,
),
],
)
)
)
],
);
}
}
class MenuBottomPart extends StatefulWidget {
#override
_MenuBottomPartState createState() => _MenuBottomPartState();
}
class _MenuBottomPartState extends State<MenuBottomPart> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(height: 10),
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 10),
child: Container(
height: MediaQuery
.of(context)
.size
.height * 0.60,
child: ListView(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
scrollDirection: Axis.vertical,
children: <Widget>[
Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
child: ExpansionTile(
title: Text('SNACKS'),
children: snacksmenuitems.map((menuitem) {
//print(menuitem);
return MenuItem(menuitem: menuitem);
/*SizedBox(height:10),
MenuItem(),
SizedBox(height:10),
MenuItem(),
SizedBox(height:10),
MenuItem()*/
}).toList()
),
)
),
SizedBox(height: 10),
Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
child: ExpansionTile(
title: Text('RICE ITEMS'),
children: ricemenuitems.map((menuitem) {
//print(menuitem);
return MenuItem(menuitem: menuitem);
/*SizedBox(height:10),
MenuItem(),
SizedBox(height:10),
MenuItem(),
SizedBox(height:10),
MenuItem()*/
}).toList()
),
)
)
]
),
),
)
]
);
}
}
class MenuItem extends StatefulWidget {
List menuitem=[];
MenuItem({Key key, this.menuitem}): super(key: key);
#override
_MenuItemState createState() => _MenuItemState();
}
class _MenuItemState extends State<MenuItem> {
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.black12),
borderRadius: BorderRadius.all(Radius.circular(10))
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image(
image: NetworkImage('https://www.whiskaffair.com/wp-content/uploads/2018/08/Mumbai-Pav-Bhaji-4.jpg'),
width: 80,
height: 80
),
SizedBox(width:10),
Padding(
padding: const EdgeInsets.fromLTRB(0, 5, 0, 0),
child: Container(
width:190,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.menuitem[0],
style: TextStyle(
fontSize:19,
color: Colors.grey[900]
),
),
SizedBox(height:5.0),
Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Row(
children: <Widget>[
Row(
children: <Widget>[
Text(
'₹',
style: TextStyle(
fontSize: 15,
color: Colors.grey[800]
),
),
Text(
widget.menuitem[1].toString(),
style: TextStyle(
fontSize: 15,
color: Colors.grey[800]
),
)
],
),
SizedBox(width:70),
Container(
child: Row(
children: <Widget>[
SizedBox(
width:30,
height:30,
child: FloatingActionButton(
onPressed: (){
setState(() {
if(GlobalVariables.allcartitems[widget.menuitem[0]][0]>0){
GlobalVariables.no_of_cart_items-=1;
GlobalVariables.allcartitems[widget.menuitem[0]][0]-=1;
GlobalVariables.totalcost-=GlobalVariables.allcartitems[widget.menuitem[0]][1];
// CHECK IF CART HAS NO ITEMS AND REMOVE BADGE HERE
}
});
},
elevation: 1,
child: Icon(Icons.remove, size: 18),
backgroundColor: Colors.red[300],
mini: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
child: Text(
GlobalVariables.allcartitems[widget.menuitem[0]][0].toString(),
style: TextStyle(
fontSize: 18
),
),
),
SizedBox(
width:30,
height:30,
child: FloatingActionButton(
onPressed: (){
setState(() {
GlobalVariables.no_of_cart_items+=1;
GlobalVariables.allcartitems[widget.menuitem[0]][0]+=1;
GlobalVariables.totalcost+=GlobalVariables.allcartitems[widget.menuitem[0]][1];
// SET BADGE HERE
});
},
elevation: 1,
child: Icon(Icons.add, size: 20),
backgroundColor: Colors.green[300],
mini:true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))),
),
)
],
)
)
],
),
),
],
),
),
)
],
),
);
}
}
This is cart.dart:
import 'dart:convert';
import 'package:canteen_app/pages/globalvar.dart';
import 'package:canteen_app/pages/globalvar.dart' as prefix0;
import 'package:canteen_app/pages/orderReceipt.dart';
import 'package:flutter/material.dart';
import 'package:canteen_app/pages/CustomShapeClipper.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
class Cart extends StatefulWidget {
#override
_CartState createState() => _CartState();
}
class _CartState extends State<Cart> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
CartTopPart(),
CartBottomPart()
],
);
}
}
class CartTopPart extends StatefulWidget {
#override
_CartTopPartState createState() => _CartTopPartState();
}
Color firstColor = Color(0xFFF47D15);
Color secondColor = Color(0xFFEF772C);
class _CartTopPartState extends State<CartTopPart> {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
ClipPath(
clipper: CustomShapeClipper(),
child: Container(
height:140.0,
width:MediaQuery.of(context).size.width,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [firstColor, secondColor],
)
),
child: Column(
children: <Widget>[
SizedBox(height: 53.0),
Text(
'CART',
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
textAlign: TextAlign.center,
),
],
)
)
)
],
);
}
}
var cartmenuitems = GlobalVariables.allcartitems.keys.toList();
class CartBottomPart extends StatefulWidget {
#override
_CartBottomPartState createState() => _CartBottomPartState();
}
class _CartBottomPartState extends State<CartBottomPart> {
bool _isLoading=false;
createAlertDialog(BuildContext context, String errormessage){
return showDialog(
context: context,
builder: (context){
return AlertDialog(
content: Text(errormessage)
);
}
);
}
#override
Widget build(BuildContext context) {
if(GlobalVariables.no_of_cart_items>0) {
return _isLoading==true ? Center(child: CircularProgressIndicator()) : Padding(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
child: Column(
children: <Widget>[
Container(
height: MediaQuery
.of(context)
.size
.height * 0.40,
child: ListView(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
scrollDirection: Axis.vertical,
children: cartmenuitems.map((menuitem) {
//print(cartmenuitems);
// print(GlobalVariables.allcartitems[menuitem]);
//if(GlobalVariables.allcartitems[menuitem]>=1) {
//print('heyy');
return CartOrderDish(menuitem: menuitem);
//}
}).toList()
),
),
Divider(
color: Colors.black
),
Row(
children: <Widget>[
Text(
'Total Amount:',
style: TextStyle(
fontSize: 20
),
),
SizedBox(width: 140),
Row(
children: <Widget>[
Text(
'₹',
style: TextStyle(
fontSize: 20
),
),
Text(
GlobalVariables.totalcost.toString(),
style: TextStyle(
fontSize: 20
)
)
],
)
],
),
SizedBox(height: 5),
Align(
alignment: Alignment.centerLeft,
child: Text(
'(Inclusive of GST)',
style: TextStyle(
fontSize: 15
),
),
),
SizedBox(height: 18),
RaisedButton(
onPressed: () {
if((GlobalVariables.accountbalance-GlobalVariables.totalcost)<0){
createAlertDialog(context, "Whoops! The total cost exceeds your account balance!\n\nYour account balance can be updated at the CASH COUNTER.");
}
else {
setState(() {
_isLoading = true;
});
// creating a list of all cart items to send to php
List cart = [];
cartmenuitems.map((menuitem) {
if (GlobalVariables.allcartitems[menuitem][0] > 0) {
cart.add([menuitem, GlobalVariables.allcartitems[menuitem][0], GlobalVariables.allcartitems[menuitem][1] * GlobalVariables.allcartitems[menuitem][0]]);
}
}).toList();
print(jsonEncode(cart));
Future placeOrderFunction() async {
print(GlobalVariables.username);
final response = await http.post(
"https://kjscecanteenapp.000webhostapp.com/place_order_sys.php",
body: {
"cart": json.encode(cart),
"username": GlobalVariables.username
});
// print(response.body);
var decodedResponse = json.decode(response.body);
print(decodedResponse);
setState(() {
_isLoading = false;
});
if (decodedResponse['error'] != -1) {
// means no error
int orderId=decodedResponse['error'];
int cost=GlobalVariables.totalcost;
GlobalVariables.no_of_cart_items = 0;
String date=DateFormat('dd-MMM-yyyy').format(DateTime.now());
cartmenuitems.map((menuitem) {
if (GlobalVariables.allcartitems[menuitem][0] > 0) {
GlobalVariables.allcartitems[menuitem][0] = 0;
}
});
GlobalVariables ob = new GlobalVariables();
ob.resetcart();
GlobalVariables.accountbalance -= GlobalVariables.totalcost;
GlobalVariables.totalcost = 0;
Navigator.of(context)
.push(MaterialPageRoute<Null>(
builder: (BuildContext context) {
return new OrderReceipt(orderId: orderId, cost: cost, date: date, cart: cart);
}));
}
else{
createAlertDialog(context, "There was some error during the order placement. Don't worry tho try again in a few seconds!");
}
}
placeOrderFunction();
}
},
elevation: 5.0,
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
padding: const EdgeInsets.all(0.0),
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
Color(0xFF0083B0),
Color(0xFF00B4DB),
]
)
),
padding: const EdgeInsets.fromLTRB(40, 15, 40, 15),
child: Text(
'Place Order',
style: TextStyle(
fontSize: 20
),
),
)
)
],
),
);
}
else {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
height: MediaQuery.of(context).size.height*0.6,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Please add some items into your cart.',
style: TextStyle(
fontSize: 20,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
}
}
class CartOrderDish extends StatefulWidget {
String menuitem;
CartOrderDish({Key key, this.menuitem}): super(key: key);
#override
_CartOrderDishState createState() => _CartOrderDishState();
}
class _CartOrderDishState extends State<CartOrderDish> {
#override
Widget build(BuildContext context) {
if(GlobalVariables.allcartitems[widget.menuitem][0]>0) {
int price=GlobalVariables.allcartitems[widget.menuitem][0]*GlobalVariables.allcartitems[widget.menuitem][1];
return Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 10),
child: Row(
children: <Widget>[
Expanded(
child: Text(
widget.menuitem,
style: TextStyle(
fontSize: 20
),
),
),
SizedBox(width: 50),
Container(
child: Row(
children: <Widget>[
SizedBox(
width: 30,
height: 30,
child: FloatingActionButton(
heroTag: 'fab1',
elevation: 1,
child: Icon(Icons.remove, size: 18),
backgroundColor: Colors.red[300],
mini: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0))),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
child: Text(
GlobalVariables.allcartitems[widget.menuitem][0].toString(),
style: TextStyle(
fontSize: 18
),
),
),
SizedBox(
width: 30,
height: 30,
child: FloatingActionButton(
heroTag: 'fab2',
elevation: 1,
child: Icon(Icons.add, size: 20),
backgroundColor: Colors.green[300],
mini: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5.0))),
),
)
],
)
),
SizedBox(width: 50),
Row(
children: <Widget>[
Text(
'₹',
style: TextStyle(
fontSize: 20
),
),
Text(
price.toString(),
style: TextStyle(
fontSize:20
)
)
],
)
],
),
);
}
else{
return Container();
}
}
}

Instead of changing the entire icon to indicate items added to cart, you could use Badge
Badge package: https://pub.dev/packages/badges
Update-1: For implementing the badges:
var p1badge = false;
var p2badge = false;
List<BottomNavigationBarItem> buildBottomNavBarItems() {
return [
BottomNavigationBarItem(
icon: Badge(
showBadge: p1badge,
child: Icon(Icons.filter_1),
),
title: Text('Page-1')),
BottomNavigationBarItem(
icon: Badge(
showBadge: p2badge,
child: Icon(Icons.filter_2),
),
title: Text('Page-2'))
];
}
Use a VoidCallback to update the badge:
class Page1 extends StatelessWidget {
VoidCallback onP1Badge;
Page1({this.onP1Badge});
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RaisedButton(
child: Text('P1 BADGE'),
onPressed: () {onP1Badge();},
),
],
);
}
}
Change the value of p1badge to true and call setState():
pages = [
Page1(
onP1Badge: () {
p1badge = true;
setState(() {});
},
),
Page2()
];
Update-2: Check this out: https://github.com/TheArhaam/Flutter-BottomNavigationBar-Badge
GIF:

Related

How do i correctly initialise a variable from a stateful widget to another widget in flutter and maintain state?

I have two widgets
JobsHeaderWidget
JobsView
JobsHeaderWidget is a stateful widget where i code all the logic and initialise int current = 0; in the state. In this same file, i have another class named CategoriesBuilder where i use switch cases to make sure at each switch case a different container is returned. ( a switch case for each tab )
This switch cases is now responsible for switching containers depending on the tab bar selected as seen in this image:
I will also drop the code snippet of the JobsHeaderWidget for better clarifications.
The problem is - when i use this CategoriesBuilder in same widget as the 'JobsHeaderWidget' it works.
But i don't want to use it in same widget cos of the logic of my design. I want to be able to use this builder in JobsView widget which is another dart file and it doesn't work maybe because of wrong approach.
I tried converting the JobsView to a stateful widget and initialising 'int current = 0;' but it doesn't work.
I also tried making int current = 0; global var, it worked but the state doesn't change when i select individual tab bars. ( I mean my switch cases don't seem to work ).
I have gone round stackoverflow for answers before asking this but can't find a solution.
Snippets of each widgets below.
JobsHeaderWidget
class JobsHeaderWidget extends StatefulWidget {
const JobsHeaderWidget({
Key key,
}) : super(key: key);
#override
State<JobsHeaderWidget> createState() => _JobsHeaderWidgetState();
}
class _JobsHeaderWidgetState extends State<JobsHeaderWidget> {
List<String> items = [
"All",
"Critical",
"Open",
"Closed",
"Overdue",
];
ValueChanged<int> onChange;
int current = 0;
List<DropdownMenuItem<String>> get dropdownItems {
List<DropdownMenuItem<String>> menuItems = [
DropdownMenuItem(
child: Text(
"Today",
),
value: "Today"),
];
return menuItems;
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 15.0, right: 15.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Jobs',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w600),
),
Row(
children: [
Text(
'View Insights ',
style: GoogleFonts.poppins(
color: Color(0xff3498DB),
fontSize: 12,
fontWeight: FontWeight.w500),
),
Icon(
Icons.arrow_forward_ios,
color: Color(0xff3498DB),
size: 12,
),
],
),
SizedBox(
height: 10,
),
filterJobs(),
],
),
),
);
}
Widget filterJobs() {
String selectedValue = "Today";
return Column(
children: [
Container(
constraints: const BoxConstraints(maxWidth: 600, maxHeight: 100),
width: double.infinity,
child: IntrinsicWidth(
child: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int i = 0; i < items.length; i++) ...[
GestureDetector(
onTap: () {
setState(() {
current = i;
});
},
child: AnimatedContainer(
height: 40,
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.all(5),
padding: const EdgeInsets.only(
left: 14.0, right: 14.0, top: 4, bottom: 4),
decoration: BoxDecoration(
color: current == i
? const Color(0xff34495E)
: const Color(0xffF5F5F5),
borderRadius: BorderRadius.circular(50),
),
child: Center(
child: Text(
items[i],
style: GoogleFonts.poppins(
fontSize: 15,
fontWeight: FontWeight.w500,
color:
current == i ? Colors.white : Colors.grey),
),
),
),
),
]
],
),
),
),
),
Divider(
color: Color(0xff34495E).withOpacity(0.2),
),
Row(
children: [
Text(
'All Jobs',
style:
GoogleFonts.poppins(fontSize: 9, fontWeight: FontWeight.w400),
),
SizedBox(
width: 5,
),
Text(
' * This Week',
style:
GoogleFonts.poppins(fontSize: 9, fontWeight: FontWeight.w400),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'25',
style: GoogleFonts.poppins(
fontSize: 20, fontWeight: FontWeight.w600),
),
Container(
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: Color(0xffF4F4F4)),
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xff34495E),
borderRadius: BorderRadius.circular(2)),
child: Icon(
Icons.tune,
size: 15,
color: Colors.white,
),
),
SizedBox(
width: 5,
),
DropdownMenuItem(
child: DropdownButtonHideUnderline(
child: Container(
child: DropdownButton(
isDense: true,
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Color(0xff34495E),
),
onChanged: (value) {},
items: dropdownItems,
value: selectedValue,
),
),
),
),
],
),
),
),
],
),
//If i uncomment this line and use the category builder here, it works fine! CategoriesBuilder(current: current)
],
);
}
}
class CategoriesBuilder extends StatelessWidget {
const CategoriesBuilder({
Key key,
#required this.current,
}) : super(key: key);
final int current;
#override
Widget build(BuildContext context) {
return Builder(
builder: (context) {
switch (current) {
case 0:
return AllJobsListView();
case 1:
return CriticalJobsListView();
case 2:
return OpenJobsListView();
case 3:
return ClosedJobsListView();
case 4:
return OverdueJobsListView();
default:
return SizedBox.shrink();
}
},
);
}
}
JobsView
class JobsView extends StatefulWidget {
const JobsView({
Key key,
}) : super(key: key);
#override
State<JobsView> createState() => _JobsViewState();
}
class _JobsViewState extends State<JobsView> {
int current = 0;
#override
Widget build(BuildContext context) {
final controller = Get.put(EServicesController());
return Scaffold(
// floatingActionButton: new FloatingActionButton(
// child: new Icon(Icons.add, size: 32, color: Get.theme.primaryColor),
// onPressed: () => {Get.offAndToNamed(Routes.E_SERVICE_FORM)},
// backgroundColor: Get.theme.colorScheme.secondary,
// ),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
body: RefreshIndicator(
onRefresh: () async {
Get.find<LaravelApiClient>().forceRefresh();
controller.refreshEServices(showMessage: true);
Get.find<LaravelApiClient>().unForceRefresh();
},
child: CustomScrollView(
controller: controller.scrollController,
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: false,
slivers: <Widget>[
SliverAppBar(
backgroundColor: Color(0xffFFFFFF),
expandedHeight: MediaQuery.of(context).size.height * 0.4,
elevation: 0.5,
primary: true,
pinned: false,
floating: false,
//iconTheme: IconThemeData(color: Get.theme.primaryColor),
// title: Text(
// "Jobs".tr,
// style: Get.textTheme.headline6
// .merge(TextStyle(color: Get.theme.primaryColor)),
// ),
centerTitle: false,
automaticallyImplyLeading: false,
// leading: new IconButton(
// icon: new Icon(Icons.arrow_back_ios,
// color: Get.theme.primaryColor),
// onPressed: () => {Get.back()},
// ),
actions: [
SearchButtonWidget(),
],
//bottom: HomeSearchBarWidget(),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: JobsHeaderWidget(),
)),
SliverToBoxAdapter(
child: Wrap(
children: [
//ServicesListWidget(),
// The state doesnt change here for some reasosns CategoriesBuilder(current: current)
],
),
),
],
),
),
);
}
}
Try this: keep your 'int current' within _JobsViewState as you have in your code.
When you call your JobsHeaderWidget , pass the function it will use to update the the value of the current variable; and rebuild the state from here.
Something like this:
class JobsHeaderWidget extends StatefulWidget {
final Function changeCurrentValue(int newValue);
const JobsHeaderWidget({
this.changeCurrentValue,
Key key,
}) : super(key: key);
#override
State<JobsHeaderWidget> createState() => _JobsHeaderWidgetState();
}
class _JobsHeaderWidgetState extends State<JobsHeaderWidget> {
#override
Widget build(BuildContext context) {
// Somewhere inside build, instead calling setState()
// call the function you passed to the widget
GestureDetector(
onTap: () {
changeCurrentValue(i);
},
)
}
}
class _JobsViewState extends State<JobsView> {
int current = 0;
void changeCurrentValue(int newValue) {
setState(() {
current = newValue;
});
}
#override
Widget build(BuildContext context) {
//somewhere inside build
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: JobsHeaderWidget(changeCurrentValue: changeCurrentValue),
)),
}
}
After hours and even sleeping overnight on this question i came up with a work-around that works. (minor refactoring)
This was my approach :
Convert my JobsView widget to a stateful widget and put all my controllers in place.
Copied all my variables from JobHeaderWidget and put it in the state of my JobsView widget.
Instead of returning a widget in the title of my sliver app as thus :
flexibleSpace: FlexibleSpaceBar( collapseMode: CollapseMode.parallax, title: JobsHeaderWidget(), )),
I copied all of my code from the widget tree from JobsHeaderWidget and put converted to a method and replaced it in my title.
My builder CategoryBuilder was put in a separate then imported as i used it in my SliverAppAdapter .
Of cos i got rid of the unnecessary dart file JobsHeaderWidget.
FULL CODE BELOW
class JobsView extends StatefulWidget {
#override
State<JobsView> createState() => _JobsViewState();
}
class _JobsViewState extends State<JobsView> {
List<String> items = [
"All",
"Critical",
"Open",
"Closed",
"Overdue",
];
int current = 0;
List<DropdownMenuItem<String>> get dropdownItems {
List<DropdownMenuItem<String>> menuItems = [
DropdownMenuItem(
child: Text(
"Today",
),
value: "Today"),
];
return menuItems;
}
#override
Widget build(BuildContext context) {
final controller = Get.put(EServicesController());
return Scaffold(
// floatingActionButton: new FloatingActionButton(
// child: new Icon(Icons.add, size: 32, color: Get.theme.primaryColor),
// onPressed: () => {Get.offAndToNamed(Routes.E_SERVICE_FORM)},
// backgroundColor: Get.theme.colorScheme.secondary,
// ),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
body: RefreshIndicator(
onRefresh: () async {
Get.find<LaravelApiClient>().forceRefresh();
controller.refreshEServices(showMessage: true);
Get.find<LaravelApiClient>().unForceRefresh();
},
child: CustomScrollView(
controller: controller.scrollController,
physics: AlwaysScrollableScrollPhysics(),
shrinkWrap: false,
slivers: <Widget>[
SliverAppBar(
backgroundColor: Color(0xffFFFFFF),
expandedHeight: MediaQuery.of(context).size.height * 0.4,
elevation: 0.5,
primary: true,
pinned: false,
floating: false,
//iconTheme: IconThemeData(color: Get.theme.primaryColor),
// title: Text(
// "Jobs".tr,
// style: Get.textTheme.headline6
// .merge(TextStyle(color: Get.theme.primaryColor)),
// ),
centerTitle: false,
automaticallyImplyLeading: false,
// leading: new IconButton(
// icon: new Icon(Icons.arrow_back_ios,
// color: Get.theme.primaryColor),
// onPressed: () => {Get.back()},
// ),
actions: [
SearchButtonWidget(),
],
//bottom: HomeSearchBarWidget(),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: mainHeader(),
)),
SliverToBoxAdapter(
child: Wrap(
children: [
//ServicesListWidget(),
CategoriesBuilder(current: current)
],
),
),
],
),
),
);
}
Padding mainHeader() {
return Padding(
padding: const EdgeInsets.only(left: 15.0, right: 15.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Jobs',
style: GoogleFonts.poppins(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w600),
),
Row(
children: [
Text(
'View Insights ',
style: GoogleFonts.poppins(
color: Color(0xff3498DB),
fontSize: 12,
fontWeight: FontWeight.w500),
),
Icon(
Icons.arrow_forward_ios,
color: Color(0xff3498DB),
size: 12,
),
],
),
SizedBox(
height: 10,
),
() {
String selectedValue = "Today";
return Column(
children: [
Container(
constraints:
const BoxConstraints(maxWidth: 600, maxHeight: 100),
width: double.infinity,
child: IntrinsicWidth(
child: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (int i = 0; i < items.length; i++) ...[
GestureDetector(
onTap: () {
setState(() {
current = i;
});
},
child: AnimatedContainer(
height: 40,
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.all(5),
padding: const EdgeInsets.only(
left: 14.0,
right: 14.0,
top: 4,
bottom: 4),
decoration: BoxDecoration(
color: current == i
? const Color(0xff34495E)
: const Color(0xffF5F5F5),
borderRadius: BorderRadius.circular(50),
),
child: Center(
child: Text(
items[i],
style: GoogleFonts.poppins(
fontSize: 15,
fontWeight: FontWeight.w500,
color: current == i
? Colors.white
: Colors.grey),
),
),
),
),
]
],
),
),
),
),
Divider(
color: Color(0xff34495E).withOpacity(0.2),
),
Row(
children: [
Text(
'All Jobs',
style: GoogleFonts.poppins(
fontSize: 9, fontWeight: FontWeight.w400),
),
SizedBox(
width: 5,
),
Text(
' * This Week',
style: GoogleFonts.poppins(
fontSize: 9, fontWeight: FontWeight.w400),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'25',
style: GoogleFonts.poppins(
fontSize: 20, fontWeight: FontWeight.w600),
),
Container(
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: Color(0xffF4F4F4)),
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xff34495E),
borderRadius: BorderRadius.circular(2)),
child: Icon(
Icons.tune,
size: 15,
color: Colors.white,
),
),
SizedBox(
width: 5,
),
DropdownMenuItem(
child: DropdownButtonHideUnderline(
child: Container(
child: DropdownButton(
isDense: true,
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Color(0xff34495E),
),
onChanged: (value) {},
items: dropdownItems,
value: selectedValue,
),
),
),
),
],
),
),
),
],
),
//CategoriesBuilder(current: current)
],
);
}(),
],
),
),
);
}
}

Custom Sliver App Bar in flutter with an Image and 2 Text widgets going into app bar on scrolling

i want to implement the sliver app bar as shown in the the 2 pictures given below. After much googling , I fount about the CustomScrollView widget and the SliverAppBar widget but all the tutorials and blogs online about sliver app bars show a simple one where an image disappears into an app bar with a text as title on scrolling. However here what I want to achieve is slightly different and I am having a hard time trying to figure out how to do it. Can anyone help me with it?
You can try this:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ScrollController? _scrollController;
bool lastStatus = true;
double height = 200;
void _scrollListener() {
if (_isShrink != lastStatus) {
setState(() {
lastStatus = _isShrink;
});
}
}
bool get _isShrink {
return _scrollController != null &&
_scrollController!.hasClients &&
_scrollController!.offset > (height - kToolbarHeight);
}
#override
void initState() {
super.initState();
_scrollController = ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
_scrollController?.removeListener(_scrollListener);
_scrollController?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: 'Horizons Weather',
home: Scaffold(
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
elevation: 0,
backgroundColor: Colors.blueGrey,
pinned: true,
expandedHeight: 275,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: _isShrink
? const Text(
"Profile",
)
: null,
background: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 48),
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(
headerImage,
fit: BoxFit.cover,
height: 100,
width: 100,
),
),
),
const SizedBox(
height: 16,
),
Text(
"Flipkart",
style: textTheme.headline4,
),
const SizedBox(
height: 8,
),
const Text(
"flipkart.com",
),
const SizedBox(
height: 5,
),
const Text(
"Info about the company",
),
],
),
),
),
actions: _isShrink
? [
Padding(
padding: const EdgeInsets.only(left: 8, right: 12),
child: Row(
children: [
Padding(
padding:
const EdgeInsets.only(left: 8, right: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"Flipkart",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
"flipkart.com",
style: TextStyle(
fontSize: 12,
),
),
],
),
),
ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(
headerImage,
fit: BoxFit.cover,
height: 30,
width: 30,
),
),
],
),
),
]
: null,
),
];
},
body: CustomScrollView(
scrollBehavior: const ConstantScrollBehavior(),
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(child: Text("Item: $index")),
);
},
childCount: 50,
),
),
],
),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
const UserAccountsDrawerHeader(
accountName: Text("Zakaria Hossain"),
accountEmail: Text("zakariaaltime#gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.orange,
child: Text(
"A",
style: TextStyle(fontSize: 40.0),
),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text("Home"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.contacts),
title: Text("Contact Us"),
onTap: () {
Navigator.pop(context);
},
),
],
),
),
),
);
}
}
Demo

How to make a 3 dot pop up menu in app-bar in flutter. Image link is below

enter image description hereAs per image I want popup in app-bar in flutter
Try the below code and you will store the SVG image in image directory
actions[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 5),
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(width: 2, color: Colors.blue)),
child: SvgPicture.asset(
"images/ic_more.svg",
height: 30,
color: Colors.white,
),
),
onTapDown: (details) {
_showPopUpMenu(details.globalPosition);
})
)
]
popUpMenu:
_showPopUpMenu(Offset offset) async {
final screenSize = MediaQuery.of(context).size;
double left = offset.dx;
double top = offset.dy;
double right = screenSize.width - offset.dx;
double bottom = screenSize.height - offset.dy;
await showMenu<MenuItemType>(
context: context,
position: RelativeRect.fromLTRB(left, top, right, bottom),
items: MenuItemType.values
.map((MenuItemType menuItemType) =>
PopupMenuItem<MenuItemType>(
value: menuItemType,
child: Text(getMenuItemString(menuItemType)),
))
.toList(),
).then((MenuItemType item) {
if (item == MenuItemType.EDIT) {
// here set your route
}
});
}
And your enum data for popup menu
import 'package:flutter/foundation.dart';
enum MenuItemType {
EDIT,
DUPLICATE
}
getMenuItemString(MenuItemType menuItemType) {
switch (menuItemType) {
case MenuItemType.EDIT:
return "Edit";
case MenuItemType.DUPLICATE:
return "Duplicate";
}
}
Please refer to below code
Using custom_pop_up_menu: ^1.2.2
https://pub.dev/packages/custom_pop_up_menu
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<ChatModel> messages;
List<ItemModel> menuItems;
CustomPopupMenuController _controller = CustomPopupMenuController();
#override
void initState() {
menuItems = [
ItemModel('Chat', Icons.chat_bubble),
ItemModel('Add', Icons.group_add),
ItemModel('View', Icons.settings_overscan),
];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CustomPopupMenu'),
actions: <Widget>[
CustomPopupMenu(
child: Container(
child: Icon(
Icons.more_horiz,
color: Colors.white,
size: 24.0,
),
padding: EdgeInsets.symmetric(
horizontal: 30.0,
vertical: 20.0,
),
),
menuBuilder: () => ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Container(
color: Colors.white,
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: menuItems
.map(
(item) => GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: _controller.hideMenu,
child: Container(
height: 40,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
children: <Widget>[
Icon(
item.icon,
size: 15,
color: Colors.black,
),
Expanded(
child: Container(
margin: EdgeInsets.only(left: 10),
padding:
EdgeInsets.symmetric(vertical: 10),
child: Text(
item.title,
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
),
),
],
),
),
),
)
.toList(),
),
),
),
),
pressType: PressType.singleClick,
verticalMargin: -10,
controller: _controller,
barrierColor: Colors.black54,
horizontalMargin: 0.0,
arrowColor: Colors.white,
showArrow: true,
),
],
),
body: Container(
child: Center(
child: Text(
"Pop up menu",
),
),
),
);
}
}
Solution Using PopupmenuButton
Widget popMenus({
List<Map<String, dynamic>> options,
BuildContext context,
}) {
return PopupMenuButton(
iconSize: 24.0,
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
icon: Icon(
Icons.more_horiz_rounded,
color: Colors.black,
size: 24.0,
),
offset: Offset(0, 10),
itemBuilder: (BuildContext bc) {
return options
.map(
(selectedOption) => PopupMenuItem(
height: 12.0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
selectedOption['menu'] ?? "",
style: TextStyle(
fontSize: ScreenUtil().setSp(14.0),
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
color: Colors.blue,
),
),
(options.length == (options.indexOf(selectedOption) + 1))
? SizedBox(
width: 0.0,
height: 0.0,
)
: Padding(
padding: EdgeInsets.symmetric(
vertical: 8.0,
),
child: Divider(
color: Colors.grey,
height: ScreenUtil().setHeight(1.0),
),
),
],
),
value: selectedOption,
),
)
.toList();
},
onSelected: (value) async {},
);
}
class PopUpmenusScreen extends StatefulWidget {
const PopUpmenusScreen({Key key}) : super(key: key);
#override
_PopUpmenusScreenState createState() => _PopUpmenusScreenState();
}
class _PopUpmenusScreenState extends State<PopUpmenusScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Examples"),
actions: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0,),
child: popMenus(
context: context,
options: [
{
"menu": "option 1" ?? '',
"menu_id": 1,
},
{
"menu": "option 2" ?? "",
"menu_id": 2,
},
{
"menu": "option 3" ?? "",
"menu_id": 3,
},
{
"menu": "option 4" ?? "",
"menu_id": 4,
},
],
),
)
],
),
);
}
}
Solution 2:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Examples"),
actions: [
IconButton(
icon: Icon(
Icons.more_horiz,
color: Colors.black,
size: 20.0,
),
onPressed: () {},
)
],
),
);
}
You can do that easily using DropdownButton2 which is customizable Flutter's core DropdownButton.
It has customButton parameter which will replace the normal Button with Image, Icon or any widget you want. You can customize everything and design what you need by using many options described with the package. Also, you can change the position of the dropdown menu by using the offset parameter.
Here's an example of using DropdownButton2 as a Popup Menu with Icon:
class CustomButtonTest extends StatefulWidget {
const CustomButtonTest({Key? key}) : super(key: key);
#override
State<CustomButtonTest> createState() => _CustomButtonTestState();
}
class _CustomButtonTestState extends State<CustomButtonTest> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButtonHideUnderline(
child: DropdownButton2(
customButton: const Icon(
Icons.list,
size: 46,
color: Colors.red,
),
customItemsIndexes: const [3],
customItemsHeight: 8,
items: [
...MenuItems.firstItems.map(
(item) =>
DropdownMenuItem<MenuItem>(
value: item,
child: MenuItems.buildItem(item),
),
),
const DropdownMenuItem<Divider>(enabled: false, child: Divider()),
...MenuItems.secondItems.map(
(item) =>
DropdownMenuItem<MenuItem>(
value: item,
child: MenuItems.buildItem(item),
),
),
],
onChanged: (value) {
MenuItems.onChanged(context, value as MenuItem);
},
itemHeight: 48,
itemWidth: 160,
itemPadding: const EdgeInsets.only(left: 16, right: 16),
dropdownPadding: const EdgeInsets.symmetric(vertical: 6),
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Colors.redAccent,
),
dropdownElevation: 8,
offset: const Offset(0, 8),
),
),
),
);
}
}
class MenuItem {
final String text;
final IconData icon;
const MenuItem({
required this.text,
required this.icon,
});
}
class MenuItems {
static const List<MenuItem> firstItems = [home, share, settings];
static const List<MenuItem> secondItems = [logout];
static const home = MenuItem(text: 'Home', icon: Icons.home);
static const share = MenuItem(text: 'Share', icon: Icons.share);
static const settings = MenuItem(text: 'Settings', icon: Icons.settings);
static const logout = MenuItem(text: 'Log Out', icon: Icons.logout);
static Widget buildItem(MenuItem item) {
return Row(
children: [
Icon(
item.icon,
color: Colors.white,
size: 22
),
const SizedBox(
width: 10,
),
Text(
item.text,
style: const TextStyle(
color: Colors.white,
),
),
],
);
}
static onChanged(BuildContext context, MenuItem item) {
switch (item) {
case MenuItems.home:
//Do something
break;
case MenuItems.settings:
//Do something
break;
case MenuItems.share:
//Do something
break;
case MenuItems.logout:
//Do something
break;
}
}
}

Is there a way to control the leading element behavior of NavigationRail in Flutter?

Flutter recently launched their v1.17 (latest stable release) and in that they have included a new widget "Navigation Rail".
Though this widget is still in its early stages(and I'm expecting a bit more additional properties like padding for NavigationRailDestination) it gives a whole new perspective to orthodox navigation.
So while Implementing this widget I've encountered a problem to which I'm searching for a workaround maybe a solution(if anybody has one!).
And that problem is when we try to implement the toggle between leading elements and the navigationRailDestinations using setState(){...} , the toggling happens only once and not for the whole lifecycle of the app.
I'm struggling to implement it please help!.
Here's the code snippet:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _selectedIndex = 0, menuColor = 0xFfFCCFA8;
final padding = 8.0;
//bool leadingProfileFlag = false, leadingSettingsFlag = false, contentFlag = true;
String profilePic, contentView = "dash";
getView(String contentView,int selectedIndex,int menuColor) {
switch (contentView) {
case 'MenuRails.selectedIndex':
return MenuRails(selectedIndex: selectedIndex,menuColor: menuColor,);
case '' : return MenuRails(selectedIndex: selectedIndex,menuColor: menuColor,);
case '2' : return MenuRails(selectedIndex: selectedIndex,menuColor: menuColor,);
case 'settings': return Expanded(child: Container());
case 'profile' : return Expanded(child: Container());
default: return MenuRails(selectedIndex: selectedIndex,menuColor: menuColor,);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xff28292E),
resizeToAvoidBottomPadding: false,
body: Row(
children: <Widget>[
NavigationRail(
leading: Column(
children: <Widget>[
SizedBox(
height: 38,
),
InkWell(
splashColor: Color(0xffFCCFA8),
onTap: () {
setState(() {
contentView = 'profile';
});
},
child: Center(
child: CircleAvatar(
radius: 16,
backgroundImage: profilePic != null
? NetworkImage(profilePic)
: AssetImage('assets/dummy_profile.png'),
),
),
),
SizedBox(
height: 88,
),
RotatedBox(
quarterTurns: -1,
child: GestureDetector(
onTap: (){
setState(() {
contentView = 'settings';
});
},
child: IconButton(
icon: Icon(Icons.tune),
color: Color(0xffFCCFA8),
onPressed: () {
setState(() {});
},
),
),
)
],
),
backgroundColor: Color(0xff2D3035),
groupAlignment: 1.0,
minWidth: MediaQuery.of(context).size.width * 0.07,
elevation: 8.0,
minExtendedWidth: MediaQuery.of(context).size.width * 0.4,
selectedIndex: _selectedIndex,
onDestinationSelected: (int index) {
setState(() {
_selectedIndex = index;
contentView = _selectedIndex.toString();
});
},
selectedLabelTextStyle: TextStyle(
color: Color(0xffFCCFA8),
fontSize: 13,
letterSpacing: 0.8,
decoration: TextDecoration.underline,
decorationThickness: 2.0,
),
unselectedLabelTextStyle: TextStyle(
fontSize: 13,
letterSpacing: 0.8,
),
labelType: NavigationRailLabelType.all,
destinations: [
buildRotatedTextRailDestination("Dashboard", padding),
buildRotatedTextRailDestination("Shop", padding),
buildRotatedTextRailDestination("Service", padding),
],
/*
trailing: Column(
children: <Widget>[
SizedBox(height: 15,),
Icon(
Icons.exit_to_app,//Logout icon
color: Colors.white70,
),
SizedBox(height: 10,)
],
),
*/
),
//VerticalDivider(thickness: 1, width: 1),
// This is the main content.
getView(contentView,_selectedIndex,menuColor),
],
),
);
}
Widget menuRail() {
}
NavigationRailDestination buildRotatedTextRailDestination(
String text, double padding) {
return NavigationRailDestination(
icon: SizedBox.shrink(),
label: Padding(
padding: EdgeInsets.symmetric(vertical: padding),
child: RotatedBox(
quarterTurns: -1,
child: Text(text),
),
),
);
}
}
// ignore: must_be_immutable
class MenuRails extends StatefulWidget {
int menuColor;
final int selectedIndex;
MenuRails({this.menuColor,this.selectedIndex});
#override
_MenuRailsState createState() => _MenuRailsState();
}
class _MenuRailsState extends State<MenuRails> {
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
color: Colors.black54,
child: Column(
children: <Widget>[
SizedBox(height: MediaQuery.of(context).size.height * 0.07),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
IconButton(
icon: Icon(
Icons.clear_all,
color: Color(widget.menuColor),
),
onPressed: () {
setState(() {
if (widget.menuColor == 0xFfFCCFA8)
widget.menuColor = 0xffffffff;
else
widget.menuColor = 0xFfFCCFA8;
});
},
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.07,
)
],
),
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(
MediaQuery.of(context).size.width * 0.08, 0, 0, 0),
child: ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(55)),
child: Container(
color: Color(0xfffff9c4),
height: MediaQuery.of(context).size.height,
// Here we have to write code for content.
child: Center(
child: Text(
'selectedIndex: $widget.selectedIndex',
),
),
),
),
),
)
],
),
),
);
}
}
Try using Navigation Rails with PageView inside the Expanded widget
class NavRail extends StatefulWidget {
#override
_NavRailState createState() => _NavRailState();
}
class _NavRailState extends State<NavRail> {
int selectedIndex = 0;
PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
NavigationRail(
labelType: NavigationRailLabelType.all,
selectedIconTheme: IconThemeData(color: Colors.green),
unselectedIconTheme: IconThemeData(color: Colors.blueGrey),
selectedLabelTextStyle: TextStyle(color: Colors.green),
unselectedLabelTextStyle: TextStyle(color: Colors.blueGrey),
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
pageController.animateToPage(index,
duration: Duration(milliseconds: 200),
curve: Curves.easeIn);
});
},
destinations: [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.info),
label: Text('About'),
),
NavigationRailDestination(
icon: Icon(Icons.message),
label: Text('Feedback'),
),
],
),
Expanded(
child: PageView(
controller: pageController,
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
color: Colors.blue,
),
Container(
color: Colors.green,
),
Container(
color: Colors.indigo,
),
],
))
],
),
);
}
}

How To Dynamically add Dropdown buttons or delete dropdown(s) in listview with flutter Web?

i have created a page where a user who will click a title to edit the contents and can add and delete devices that the article is attached to,
but i could not figure it out on how to program the drop down dynamically, to add more drop downs when a user press the add device button,
the additional function they want me to add as well is on the Dropdown list, when they select 'NONE' it should remove that dropdown as well,
i am planning to use mysql,xampp or sqlite for my database if im done with the UI,
import 'package:flutter/material.dart';
import 'package:surveyadminpanel/Contents/tabbar.dart';
import 'package:surveyadminpanel/widgets/button.dart';
import 'package:surveyadminpanel/widgets/simplewidgets.dart';
import 'homepage.dart';
import 'dart:ui';
class Item {
Item(this.name);
String name;
}
class editsurvey extends StatefulWidget {
#override
_editsurveyState createState() => _editsurveyState();
}
class _editsurveyState extends State<editsurvey>{
int surveyquestionnum = 1;
int surveyquestiontotal = 1;
List<Item> selectedUser = [null, null];
List<Item> selecteddata = [null, null];
List<Item> users;
int linkdevices = 1;
String dropdownvalue= "SELECT FROM DROPDOWN";
List data = [
'Sample Data 1',
'Sample Data 2',
'Sample Data 3',
'Sample Data 4',
'Sample Data 5',
'Sample Data 6',
];
#override
void initState() {
super.initState();
users = <Item>[
Item('Sample device 1'),
Item('Sample device 2'),
Item('Sample device 3'),
Item('Sample device 4'),
];
}
#override
Widget _dropdownbutton (List<Item> userlist, int index){
return Container(
padding: EdgeInsets.all(1),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.all(
Radius.circular(15.0) //
),
),
child: DropdownButton<Item>(
underline: SizedBox(),
isExpanded: true,
icon: Icon(Icons.arrow_drop_down),
hint: Text(" $dropdownvalue"),
value: selectedUser[index],
onChanged: (Item Value) {
setState(() {
selectedUser[index] = Value;
});
},
items: userlist.map((Item user) {
return DropdownMenuItem<Item>(
value: user,
child: Row(
children: <Widget>[
SizedBox(width: 10,),
Text(
user.name,
style: TextStyle(color: Colors.black),
),
],
),
);
}).toList(),
),
);
}
Widget _text(texthere,bold,size,color){
return Text(texthere,style: TextStyle(fontWeight: bold,fontSize: size,color: color),overflow: TextOverflow.ellipsis,maxLines: 1);
}
Widget _logo(){
return InkWell(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => admincontent()),
);
},
child: Container(width: 500,height: 200,child: Image.asset("images/v2.jpg")));
}
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Padding(padding: EdgeInsets.only(left: 30),
child: RichText(
text: TextSpan(
text: 'Good Morning Welcome to Sample:',
style: TextStyle(
color: Colors.blueAccent, fontSize: 18),
children: <TextSpan>[
TextSpan(text: usernametitle,
style: TextStyle(
color: Colors.black, fontSize: 18),
)
]
),
)
),
elevation: 1,
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
leading: _logo(),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.notifications),
color: Colors.blueAccent,
tooltip: 'Show Notification',
onPressed: () {
},
),
IconButton(
color: Colors.lightGreen,
icon: const Icon(Icons.account_circle),
tooltip: 'Check your Profile',
onPressed: () {
},
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
_text("EDIT SURVEY", FontWeight.bold, 20,Colors.blue),
roundedRectButton("BACK", signInGradients),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(
width: screenWidth/1.6,
height: screenHeight/1.6,
decoration: BoxDecoration(
color: Colors.orange[200],
borderRadius: new BorderRadius.all(new Radius.circular(20.0)),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_text("SURVEY TITLE", FontWeight.bold, 17,Colors.white),
_text(data[0], FontWeight.bold, 19, Colors.black),
_text("DATE CREATED", FontWeight.bold, 17,Colors.white),
_text(data[1], null, 19, Colors.black),
_text("CURRENT STATUS", FontWeight.bold, 17,Colors.white),
_text(data[2], null, 19, Colors.black),
_text("LANGUAGE VERSION", FontWeight.bold, 17,Colors.white),
_text(data[3], null, 19, Colors.black),
_text("NUMBERS OF ASSESSORS", FontWeight.bold, 17,Colors.white),
_text(data[4], null, 19, Colors.black),
_text("TOTAL RENDERED SURVEYS", FontWeight.bold, 17,Colors.white),
_text(data[5], null, 19, Colors.black),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
onTap: (){
},
child: Container(width: 100,height: 50,child: Text("EDIT SURVEY")),
),
_text("LINKED DEVICES : $linkdevices", FontWeight.bold, 17,Colors.white),
],
)
],
),
)
),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 1.0,
style: BorderStyle.solid
)
),
width: screenWidth/1.6,
height: screenHeight/1.6,
child: Column(
children: <Widget>[
_text("DEVICES PINNED", FontWeight.bold, 20,Colors.blue),
ListView.separated(
shrinkWrap: true,
itemCount: linkdevices,
itemBuilder: (context, index){
return Padding(
padding: const EdgeInsets.all(8.0),
child: _dropdownbutton(users, index),
);
},
separatorBuilder: (context, index) => Container(height: 10),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
InkWell(
child: roundedRectButton("ADD DEVICE", signInGradients),
onTap: (){
},
),
InkWell(
child: roundedRectButton("CLEAR ALL DEVICE", signInGradients),
onTap: (){
},
),
],
),
],
),
),
],
)
],
),
),
),
);
}
}
The Plan
The Result
im still trying to figure this out, but if someone can give me a lift,, im gonna be very thankful to who can help me out here ,,
You can copy paste run full code below
You can increase linkdevices and selectedUser
code snippet
List<Item> selectedUser = [null];
...
InkWell(
child: Text("ADD DEVICE"),
onTap: () {
selectedUser.add(null);
linkdevices ++;
setState(() {
});
working demo
full code
import 'package:flutter/material.dart';
class Item {
Item(this.name);
String name;
}
class editsurvey extends StatefulWidget {
#override
_editsurveyState createState() => _editsurveyState();
}
class _editsurveyState extends State<editsurvey> {
int surveyquestionnum = 1;
int surveyquestiontotal = 1;
List<Item> selectedUser = [null];
List<Item> selecteddata = [null, null];
List<Item> users;
int linkdevices = 1;
String dropdownvalue = "SELECT FROM DROPDOWN";
List data = [
'Sample Data 1',
'Sample Data 2',
'Sample Data 3',
'Sample Data 4',
'Sample Data 5',
'Sample Data 6',
];
#override
void initState() {
super.initState();
users = <Item>[
Item('Sample device 1'),
Item('Sample device 2'),
Item('Sample device 3'),
Item('Sample device 4'),
];
}
#override
Widget _dropdownbutton(List<Item> userlist, int index) {
return Container(
padding: EdgeInsets.all(1),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.all(Radius.circular(15.0) //
),
),
child: DropdownButton<Item>(
underline: SizedBox(),
isExpanded: true,
icon: Icon(Icons.arrow_drop_down),
hint: Text(" $dropdownvalue"),
value: selectedUser[index],
onChanged: (Item Value) {
print(Value.toString());
print(index);
setState(() {
selectedUser[index] = Value;
});
},
items: userlist.map((Item user) {
return DropdownMenuItem<Item>(
value: user,
child: Row(
children: <Widget>[
SizedBox(
width: 10,
),
Text(
user.name,
style: TextStyle(color: Colors.black),
),
],
),
);
}).toList(),
),
);
}
Widget _text(texthere, bold, size, color) {
return Text(texthere,
style: TextStyle(fontWeight: bold, fontSize: size, color: color),
overflow: TextOverflow.ellipsis,
maxLines: 1);
}
Widget _logo() {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => admincontent()),
);
},
child: Container(
width: 500, height: 200, child: Image.asset("images/v2.jpg")));
}
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Padding(
padding: EdgeInsets.only(left: 30),
child: RichText(
text: TextSpan(
text: 'Good Morning Welcome to Sample:',
style: TextStyle(color: Colors.blueAccent, fontSize: 18),
children: <TextSpan>[
TextSpan(
text: "usernametitle",
style: TextStyle(color: Colors.black, fontSize: 18),
)
]),
)),
elevation: 1,
automaticallyImplyLeading: false,
backgroundColor: Colors.white,
leading: _logo(),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.notifications),
color: Colors.blueAccent,
tooltip: 'Show Notification',
onPressed: () {},
),
IconButton(
color: Colors.lightGreen,
icon: const Icon(Icons.account_circle),
tooltip: 'Check your Profile',
onPressed: () {},
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
_text("EDIT SURVEY", FontWeight.bold, 20.0, Colors.blue),
//roundedRectButton("BACK", signInGradients),
Text("BACK"),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(
width: screenWidth / 1.6,
height: screenHeight / 1.6,
decoration: BoxDecoration(
color: Colors.orange[200],
borderRadius:
new BorderRadius.all(new Radius.circular(20.0)),
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_text("SURVEY TITLE", FontWeight.bold, 17.0,
Colors.white),
_text(data[0], FontWeight.bold, 19.0, Colors.black),
_text("DATE CREATED", FontWeight.bold, 17.0,
Colors.white),
_text(data[1], null, 19.0, Colors.black),
_text("CURRENT STATUS", FontWeight.bold, 17.0,
Colors.white),
_text(data[2], null, 19.0, Colors.black),
_text("LANGUAGE VERSION", FontWeight.bold, 17.0,
Colors.white),
_text(data[3], null, 19.0, Colors.black),
_text("NUMBERS OF ASSESSORS", FontWeight.bold, 17.0,
Colors.white),
_text(data[4], null, 19.0, Colors.black),
_text("TOTAL RENDERED SURVEYS", FontWeight.bold,
17.0, Colors.white),
_text(data[5], null, 19.0, Colors.black),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
onTap: () {},
child: Container(
width: 100,
height: 50,
child: Text("EDIT SURVEY")),
),
_text("LINKED DEVICES : $linkdevices",
FontWeight.bold, 17.0, Colors.white),
],
)
],
),
)),
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 1.0,
style: BorderStyle.solid)),
width: screenWidth / 1.6,
height: screenHeight / 1.6,
child: Column(
children: <Widget>[
_text("DEVICES PINNED", FontWeight.bold, 20.0,
Colors.blue),
ListView.separated(
shrinkWrap: true,
itemCount: linkdevices,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: _dropdownbutton(users, index),
);
},
separatorBuilder: (context, index) => Container(height: 10),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
InkWell(
child: Text("ADD DEVICE"),
onTap: () {
selectedUser.add(null);
linkdevices ++;
setState(() {
});
/*listWidget.add( ListView.separated(
shrinkWrap: true,
itemCount: linkdevices,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: _dropdownbutton(users, index),
);
},
separatorBuilder: (context, index) => Container(height: 10),
));
setState(() {
});*/
},
),
InkWell(
child: Text("CLEAR ALL DEVICE"),
onTap: () {},
),
],
),
],
),
),
],
)
],
),
),
),
);
}
}
class admincontent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container();
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: editsurvey(),
);
}
}