Related
Hello guys i have two stateful widget InactiveCustomersListView and MessageCustomerHeader
InactiveCustomersListView has a List of tiles, and these tiles have checkboxes
class InactiveCustomersListView extends StatefulWidget {
final bool checkAll;
const InactiveCustomersListView({
Key key,
this.checkAll,
}) : super(key: key);
#override
State<InactiveCustomersListView> createState() =>
_InactiveCustomersListViewState();
}
class _InactiveCustomersListViewState extends State<InactiveCustomersListView> {
bool checkAll;
List<CheckBoxModel> listOfCustomers = [];
#override
void initState() {
listOfCustomers.addAll({
CheckBoxModel(selected: false),
});
super.initState();
}
#override
Widget build(BuildContext context) {
final controller = Get.put(CustomersController());
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: listOfCustomers.length,
itemBuilder: (context, index) {
return Obx(() {
return Container(
color: Color(0xffFFFFFF),
child: ListTile(
contentPadding: controller.isCheboxVisible.isTrue
? EdgeInsets.only(left: 0, right: 22, top: 10, bottom: 10)
: EdgeInsets.only(left: 22, right: 22, top: 10, bottom: 10),
horizontalTitleGap: 5,
leading: controller.isCheboxVisible.isTrue
? Visibility(
visible: controller.isCheboxVisible.value,
child: Transform.scale(
scale: 1.3,
child: Checkbox(
side: MaterialStateBorderSide.resolveWith(
(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return const BorderSide(
width: 2, color: Color(0xff34495E));
}
return const BorderSide(
width: 1, color: Color(0xffB0BEC1));
},
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5)),
activeColor: Color(0xff34495E),
//materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
//visualDensity: VisualDensity(horizontal: -4, vertical: -4),
value: listOfCustomers[index].selected,
onChanged: (value) {
setState(() {
listOfCustomers[index].selected = value;
final check = listOfCustomers
.every((element) => element.selected);
checkAll = check;
});
},
),
),
)
: null,
title: Row(
children: [
SizedBox(
width: 60,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(80)),
child: CachedNetworkImage(
height: 100,
width: double.infinity,
fit: BoxFit.cover,
imageUrl:
Get.find<AuthService>().user.value.avatar.thumb,
placeholder: (context, url) => Image.asset(
'assets/img/loading.gif',
fit: BoxFit.cover,
width: double.infinity,
height: 80,
),
errorWidget: (context, url, error) =>
Icon(Icons.error_outline),
),
),
),
SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'John Cletus',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xff151515)),
),
Container(
decoration: BoxDecoration(
color: Color(0xffECF0F1),
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: const EdgeInsets.only(
left: 10.0,
right: 10.0,
top: 5.0,
bottom: 5.0),
child: Text(
'Inactive',
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w400,
color: Color(0xff7F8D90)),
),
),
),
],
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
'2',
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xff151515)),
),
SizedBox(
width: 5,
),
Text(
'jobs',
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w400,
color: Color(0xff151515)),
),
],
),
Text(
'Last job 5 days ago',
style: GoogleFonts.poppins(
fontSize: 9,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w400,
color: Color(0xff151515)),
),
],
),
],
),
),
],
),
),
);
});
});
}
}
class CheckBoxModel {
bool selected;
CheckBoxModel({this.selected});
}
MessageCustomerHeader widget class has a checkbox that controls the Check boxes in the InactiveCustomersListView widget class:
class MessageCustomerHeader extends StatefulWidget {
final List<CheckBoxModel> listOfCustomers;
const MessageCustomerHeader({
Key key,
this.listOfCustomers,
}) : super(key: key);
#override
State<MessageCustomerHeader> createState() => _MessageCustomerHeaderState();
}
class _MessageCustomerHeaderState extends State<MessageCustomerHeader> {
bool checkAll = false;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Select customers you woult like to message',
style: GoogleFonts.poppins(
color: Color(0xff596780).withOpacity(0.8),
fontSize: 14,
fontWeight: FontWeight.w500),
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Transform.scale(
scale: 1.3,
child: Checkbox(
side: MaterialStateBorderSide.resolveWith(
(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return const BorderSide(
width: 2, color: Color(0xff34495E));
}
return const BorderSide(
width: 1, color: Color(0xffB0BEC1));
},
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5)),
activeColor: Color(0xff34495E),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
value: checkAll,
onChanged: (value) {
setState(() {
checkAll = value;
widget.listOfCustomers.forEach((element) {
element.selected = value;
});
});
},
),
),
SizedBox(
width: 10,
),
Text(
'Select all',
style: GoogleFonts.poppins(
color: Colors.black87,
fontSize: 12,
fontWeight: FontWeight.w400),
),
],
),
],
),
),
);
}
}
I tried using constructor to pass "listOfCustomers" from InactiveCustomersListView widget to MessageCustomerHeader widget
And also passing "checkAll" from MessageCustomerHeader to InactiveCustomersListView widget by also using constructors.
The problem is having is that when i click on the checkbox in MessageCustomerHeader widget to check all the check boxes in InactiveCustomersListView widget, the state don't seem to change and it doesn't even after setting all the logic. What am i doing wrong guys and how do i resolve this issue :(
This is the error i get when i tap the checkbox on the MessageCustomerHeader checkbox.
"The method 'forEach' was called on null.
Receiver: null
Tried calling: forEach(Closure: (CheckBoxModel) => Null)"
Thank you.
If you have the proper lints enabled you would have seen this warning: DON'T put any logic in createState().
From the associated description:
Implementations of createState() should return a new instance of a State object and do nothing more. Since state access is preferred via the widget field, passing data to State objects using custom constructor parameters should also be avoided and so further, the State constructor is required to be passed no arguments.
(emphasis added)
You are passing arguments to your State constructors. Instead, use widget.checkAll and widget.listOfCustomers to access widget properties from the state objects.
Also:
It would be better to specify a type for checkAll. In your code you just have final checkAll;.
It is difficult to read the screen shot of the error message. It would be better to include the error message as text in the question, along with an indication in your code of the line to which the error message is referring.
I am using DropdownButton and I am facing the following error. When opening the DropdownMenuItem list, I do not have an indent from the button itself. That is, I need to get the padding between the button (DropdownButton) and the dropdown list (DropdownMenuItem) so that there is a distance. But so far I haven't been able to do it. How can you make an indent between them?
code
#override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
child: DropdownButtonHideUnderline(
child: DropdownButton2(
items: List.generate(
widget.items.length,
(index) => DropdownMenuItem<String>(
value: widget.items[index],
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.white.withOpacity(0.1),
width: 1,
),
),
),
child: StatefulBuilder(
builder: (context, setStateSB) => GFCheckboxListTile(
value: _selectedTitles.contains(widget.items[index]),
onChanged: (bool selected) {
_onItemSelect(selected, index);
setStateSB(() {});
},
selected: selected,
title: Text(
widget.items[index],
style: constants.Styles.smallTextStyleWhite,
),
padding: const EdgeInsets.only(top: 12, bottom: 13),
margin: const EdgeInsets.only(right: 0, left: 0),
size: 22,
activeBgColor: constants.Colors.greyCheckbox,
activeBorderColor: constants.Colors.greyXMiddle,
inactiveBgColor: constants.Colors.greyCheckbox,
activeIcon: SvgPicture.asset(constants.Assets.checkboxIcon),
inactiveBorderColor: constants.Colors.greyXMiddle,
type: type,
),
),
),
),
),
hint: _selectedTitles.length > 1
? const Text('Selecte EV',
style: constants.Styles.bigBookTextStyleWhite)
: Text(_selectedTitles.join().toString(),
style: constants.Styles.bigBookTextStyleWhite),
value: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value as String;
});
},
icon: SvgPicture.asset(constants.Assets.arrowDropdown),
iconSize: 21,
buttonHeight: 27,
itemHeight: 47,
dropdownMaxHeight: 185,
dropdownWidth: 140,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: constants.Colors.purpleMain,
),
color: constants.Colors.greyDark),
selectedItemBuilder: (context) {
return widget.items.map(
(item) {
return Row(
children: [
widget.icon ?? const SizedBox(),
const SizedBox(width: 8),
Text(
item,
style: constants.Styles.bigBookTextStyleWhite,
),
],
);
},
).toList();
},
),
),
);
}
}
In the dropdown_button2 documentation, there is a property for moving the dropdown menu Offset. You can see it here https://pub.dev/documentation/dropdown_button2/latest/dropdown_button2/DropdownButton2/offset.html
On that property you just need to set an Offset, which is composed of an X and Y values.
In your case it would look something like this:
DropdownButton2(
offset: Offset(0,10),
...
),
I want to implement OTP verification screen without any package.
when i entered the number it should move to next input field
I m using this code in my current project take a refrence this will help
class Otp extends StatefulWidget {
final String? phnNumber;
final String ? code;
String? from;
Otp({Key ?key, this.phnNumber, this.from, this.code}) : super(key:
key);
#override
_OtpState createState() => _OtpState();
}
class _OtpState extends State<Otp> {
double ? height ;
double ? width;
TextEditingController ? contrller1;
TextEditingController ? contrller2;
TextEditingController ? contrller3;
TextEditingController ? contrller4;
SendOtpRequest resend = SendOtpRequest();
SharedPreferences ? prefs;
getSharedPreferences () async
{
prefs = await SharedPreferences.getInstance();
}
String Code = "";
#override
void initState() {
// TODO: implement initState
super.initState();
contrller1 = TextEditingController();
contrller2 = TextEditingController();
contrller3 = TextEditingController();
contrller4 = TextEditingController();
getSharedPreferences();
}
#override
Widget build(BuildContext context) {
height= MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.height;
final verifyprovider = Provider.of<PostDataProvider>(context);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
toolbarHeight:height! * 0.07802345,
titleSpacing: 0,
backgroundColor: HexColor("#18263d"),
automaticallyImplyLeading: false,
leading: Padding(
padding: const EdgeInsets.only(left: 8.0,),
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
color: Colors.transparent,
child: Image.asset("assets/images/back_ic-1.png")),
),
),
// SizedBox(width: width!*0.001234,),
title:Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: height!/15,
width: height!/15,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 2,
color:HexColor("#fc4f00"),
)),
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Container(
height: height!/11,
width: height!/11,
decoration: BoxDecoration(
image: const DecorationImage(
image:
AssetImage("assets/images/home_logo.png"),
fit: BoxFit.fill
),
shape: BoxShape.circle,
border: Border.all(
width: 1,
color:HexColor("#fc4f00"),
)),
),
),
),
SizedBox(width: width! * 0.04234,),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text("Verification",
style: GoogleFonts.oswald(fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: width! * 0.03345
),),
),
],
) ,
),
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 32),
child: Column(
children: [
Text("We have send verification code on your mobile number",
style: GoogleFonts.oswald(fontStyle: FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#8b8b8b")),
),
SizedBox(height: height!/38,),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_textFieldOTP(first: true, last: false,controllerr:
contrller1),
_textFieldOTP(first: false, last: false,controllerr:
contrller2),
_textFieldOTP(first: false, last: false,controllerr:
contrller3),
_textFieldOTP(first: false, last: true, controllerr:
contrller4),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap:() {
resend.phoneNumber= widget.phnNumber;
resend.countryCode = widget.code;
verifyprovider.resendOtp(context,
jsonEncode(resend));
},
child: Text("Resend OTP?",
style: GoogleFonts.oswald(fontStyle:
FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#fc4f00")),
),
),
),
SizedBox(height: height!/28,),
GestureDetector(
onTap: (){
if(contrller1!.text.isNotEmpty&&
contrller2!.text.isNotEmpty&&contrller3!.
text.isNotEmpty&&contrller4!.text.isNotEmpty){
verifyOtpRequest verify = verifyOtpRequest();
verify.phoneNumber = widget.phnNumber;
verify.otp=
contrller1!.text+contrller2!.
text+contrller3!.text+contrller4!.text;
verifyprovider.otpVerification(context,
jsonEncode(verify), widget.from);
}else{
CommonUtils.showToast(msg: "Please fill all the
fields ");
}
},
child: Container(
height: height!/18,
width: width,
decoration: BoxDecoration(
color: HexColor("#fc4f00"),
borderRadius: BorderRadius.circular(10)
),
child: Center(
child: Text("Verify",style: TextStyle(
color: Colors.white,
fontSize: width!*0.02345
),),
)
),
),
],
),
],
),
),
),
);
}
Widget _textFieldOTP({bool ? first, last,
TextEditingController ?
controllerr}) {
return Container(
height:height!/12 ,
child: AspectRatio(
aspectRatio: 1.0,
child: TextField(
controller: controllerr,
autofocus: true,
onChanged: (value) {
if (value.length == 1 && last == false) {
FocusScope.of(context).nextFocus();
}
if (value.length == 0 && first == false) {
FocusScope.of(context).previousFocus();
}
},
showCursor: false,
readOnly: false,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
counter: Offstage(),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
),
),
),
);
}
}
When the length of the input data reaches one, you will have to change the text field focus node.
For Example
If you are in the first field, and you enter a number field one focus should be lost, and field two should be in focus. This can be done, by requestFocus.
This article will of help for you: Flutter Focus
I am working on a project in which i have a class which has a row that has two children. Child one contains a TabView with a child class TabViewChild in which i am generating a gridview. On the other hand child two contains a listView. So the problem is, when i click on the gridview item i am passing that item's value to a static list and passing that list to a listview of other class. But i have no idea how to change the state of that class on item clicked or how can i achieve this task in a better way as i am new to Flutter. I want that when a person click on any gridview's item that item appears in the listview simultaneously.
class ItemMenus {
int id;
String code;
String name;
String salePrice;
String photo;
String categoryName;
String percentage;
int quantity;
ItemMenus({this.id, this.code, this.name, this.salePrice, this.photo,
this.categoryName, this.percentage, this.quantity});
ItemMenus.fromJson(Map<String, dynamic> json)
:
id = int.parse(json['id']),
code = json['code'],
name = json['name'],
salePrice = json['sale_price'],
photo = json['photo'],
categoryName = json['category_name'],
percentage = json['percentage'];
#override
String toString() {
return 'ItemMenus{id: $id, code: $code, name: $name, salePrice: $salePrice, photo: $photo, categoryName: $categoryName, percentage: $percentage}';
}
}
import 'package:food_app/model/mdl_item_menus.dart';
class ItemMenuList{
List<ItemMenus> _itmMenuLst = [];
ItemMenuList._();
static final ItemMenuList instanceItmMenuLst = ItemMenuList._();
static ItemMenuList get instance => instanceItmMenuLst;
void addItem(ItemMenus im){
_itmMenuLst.add(im);
}
List<ItemMenus> get list => _itmMenuLst;
#override
String toString() {
return 'ItemMenuList{_itmMenuLst: $_itmMenuLst}';
}
}
import 'package:flutter/material.dart';
import 'package:food_app/database/tables/tbl_categories.dart';
import 'package:food_app/model/list/item_menu_list.dart';
import 'package:food_app/model/mdl_categories.dart';
import 'package:food_app/model/mdl_item_menus.dart';
import 'package:food_app/pos/tab_bar_view.dart';
class EPos extends StatefulWidget{
#override
_EPosState createState() => _EPosState();
}
class _EPosState extends State<EPos> {
final categoryDBHelper = TblCategories.categoriesInstance;
final ItemMenuList instance2 = ItemMenuList.instance;
String _orderType = 'Dine-In', _info = 'Table No. 1', _userName = 'ZiaUddin';
List<Categories> catLst = [];
List<ItemMenus> itmLst = [];
Future getCategories() async {
catLst.clear();
var categories = await categoryDBHelper.getCategories();
categories.forEach((element) {
catLst.add(Categories(
id: element['id'],
categoryName: element['category_name'],
description: element['description'],
userId: element['user_id'],
companyId: element['company_id'],
delStatus: element['del_status']));
});
catLst.forEach((element) {
print(element);
});
return catLst;
}
#override
void initState() {
// TODO: implement initState
super.initState();
// itmLst = instance2.list;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
//#region AppBar
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.15,
color: Colors.redAccent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
margin: EdgeInsets.fromLTRB(8, 10, 0, 0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(20),
color: Colors.red,
),
child: Row(
children: [
Icon(
Icons.arrow_back,
color: Colors.white,
size: 25,
),
Padding(
padding: const EdgeInsets.fromLTRB(4, 8, 10, 8),
child: Text(
_orderType,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
fontFamily: 'Ubuntu',
letterSpacing: 2.0,
),
),
),
],
),
),
Container(
margin: EdgeInsets.fromLTRB(8, 10, 0, 0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(10),
color: Colors.red,
),
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 8),
child: Text(
_info,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.amberAccent,
fontFamily: 'Ubuntu',
letterSpacing: 2.0,
),
),
),
),
Container(
margin: EdgeInsets.only(top: 15, right: 5),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(30),
color: Colors.red,
),
child: Row(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 8, 8, 8),
child: Text(
_userName,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
color: Colors.white,
fontFamily: 'Ubuntu',
letterSpacing: 1.0,
),
),
),
CircleAvatar(
backgroundColor: Colors.red,
radius: MediaQuery.of(context).size.height * 0.041,
child: CircleAvatar(
radius: MediaQuery.of(context).size.height * 0.04,
backgroundImage: AssetImage('assets/user.png'),
),
),
],
),
),
],
),
),
//endregion
Expanded(
child: Row(
children: [
//# region Menu
Flexible(
flex: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.yellowAccent,
shape: BoxShape.rectangle,
),
child: Column(
children: [
Expanded(
child: Container(
color: Colors.white,
child: FutureBuilder(
future: getCategories(),
builder: (context, snapShot) {
if (snapShot.connectionState ==
ConnectionState.none &&
snapShot.hasData == null) {
return Center(
child: CircularProgressIndicator());
}
return MaterialApp(
debugShowCheckedModeBanner: false,
home: DefaultTabController(
length: catLst.length,
child: Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize:
Size.fromHeight(kToolbarHeight),
child: Container(
height: MediaQuery.of(context)
.size
.height *
0.1,
child: TabBar(
indicatorColor:
Colors.amberAccent,
isScrollable: true,
tabs: catLst
.map<Widget>((Categories c) {
return Tab(
icon: Icon(
Icons.style,
color: Colors.amberAccent,
size: 15,
),
child: Text(
c.categoryName
.toUpperCase(),
style: TextStyle(
color: Colors.black,
fontWeight:
FontWeight.w400,
),
),
);
}).toList(),
),
),
),
body: TabBarView(
children: catLst.map((Categories c) {
return TabBarViewChild(categoryName:c.categoryName,
callback: (){
setState(() {
instance2.addItem(ItemMenus(name: c.categoryName));
itmLst = instance2.list;
print('I am Callback');
});
},);
}).toList(),
),
),
),
);
}),
),
),
],
),
),
),
//endregion
//# region OrderList
Flexible(
flex: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
),
child: ListView.builder(
itemCount: itmLst.length,
itemBuilder: (context, index){
return ListTile(
title: Text(itmLst[index].name),
);
},
),
),
),
//endregion
],
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:food_app/database/tables/tbl_item_menus.dart';
import 'package:food_app/model/list/item_menu_list.dart';
import 'package:food_app/model/mdl_item_menus.dart';
import 'package:food_app/pos/new_sale.dart';
class TabBarViewChild extends StatefulWidget {
String categoryName;
VoidCallback callback;
TabBarViewChild({Key key,#required this.categoryName, this.callback}) : super(key:key);
#override
_TabBarViewChildState createState() => _TabBarViewChildState();
}
class _TabBarViewChildState extends State<TabBarViewChild> {
final ItemMenuList instance1 = ItemMenuList.instance;
List<ItemMenus> itmLst = [];
final itemMenus = TblItemMenus.itemMenusInstance;
Future getSpecificItemMenus() async{
var items = await itemMenus.getSpecificItemMenus(widget.categoryName);
itmLst.clear();
items.forEach((e) {
itmLst.add(ItemMenus(id: e['id'], categoryName: e['category_name'], code: e['code'],
name: e['name'], percentage: e['percentage'], photo: e['photo'], quantity: e['quantity'],
salePrice: e['sale_price']));
});
for (var value in itmLst) {
print(value);
}
return itmLst;
}
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: FutureBuilder(
future: getSpecificItemMenus(),
builder: (context, snapShot) {
if (snapShot.connectionState == ConnectionState.none
&& snapShot.hasData == null) {
return Center(child: CircularProgressIndicator());
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
scrollDirection: Axis.vertical,
itemCount: itmLst.length,
itemBuilder: (context, index){
return Padding(
padding: const EdgeInsets.all(5.0),
child: InkWell(
child: Card(
elevation: 4,
color: Colors.amberAccent,
child: Center(
child: Text(
itmLst[index].name.toUpperCase(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
fontSize: 13,
fontWeight: FontWeight.w700,
fontFamily: 'Ubuntu',
letterSpacing: 1.0,
),
),
),
),
onTap: (){
// Provider.of<ProItemMenus>(context, listen: false).addNewItemInList(ItemMenus(name: itmLst[index].name.toUpperCase()));
instance1.addItem(ItemMenus(categoryName: itmLst[index].name.toUpperCase()));
print(itmLst[index].name.toUpperCase());
},
),
);
}
);
}
),
);
}
}
So the problem now, there are two stateful widgets.
And as we are going to share the data or the state between them, basically we need to have more one stateful Widget which will the parent of them.
This term of is this problem known as State Management.
Currently there are many reknown State Management , such as Provider and Bloc. Reference.
But personally, I recommend to use Provider, which has simple Syntaxes and Concept.
First of all, here is my condition. I'm trying to pass all the data that were filled in Dropdownbutton and Date & Time picker at the first screen (left picture) to the second screen(right picture). The problem is, I extracted the DropDownButton widget to another class, and I don't understand how to implement it.
Before that, this is the first screen Code :
class InformationDetail extends StatefulWidget {
static const String id = 'InformationDetail';
#override
_InformationDetailState createState() => _InformationDetailState();
}
class _InformationDetailState extends State<InformationDetail> {
String addressText, addNotes;
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay.now();
Future<Null> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate)
setState(() {
selectedDate = picked;
});
}
Future<Null> _selectTime(BuildContext context) async {
final TimeOfDay picked = await showTimePicker(
context: context,
initialTime: selectedTime,
);
if (picked != null && picked != selectedTime)
setState(() {
selectedTime = picked;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(25.0, 68.0, 70.0, 26.0),
child: Text(
'Information Detail',
style: TextStyle(fontSize: 35.0),
),
),
Column(
// Wrap Column
children: <Widget>[
Column(
children: <Widget>[
TitleName(
titleText: 'Grooming Type',
infoIcon: Icons.info,
),
MenuDropDown(
dropdownText: 'Grooming Type...',
type: "groomingType",
),
TitleName(
titleText: 'Cat Breeds',
),
MenuDropDown(
dropdownText: 'Cat Breeds...',
type: "catBreeds",
),
TitleName(
titleText: 'Cat Size',
infoIcon: Icons.info,
),
MenuDropDown(
dropdownText: 'Cat Size...',
type: "catSize",
),
TitleName(
titleText: 'Add-On Services',
),
MenuDropDown(
dropdownText: 'Add - On Services...',
type: "addOnServices",
),
TitleName(
titleText: 'Reservation Date',
),
Row(
children: <Widget>[
Container(
width: 130,
height: 30,
margin: EdgeInsets.fromLTRB(50.0, 0, 62, 0),
child: RaisedButton(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
elevation: 6,
child: Text(
'Choose Date',
style: TextStyle(
fontSize: 12.0,
),
),
onPressed: () => _selectDate(context),
),
),
Text("${selectedDate.toLocal()}".split(' ')[0]),
],
),
TitleName(
titleText: 'Reservation Time',
),
Row(
children: <Widget>[
Container(
width: 130,
height: 30,
margin: EdgeInsets.fromLTRB(50.0, 0, 62, 0),
decoration: BoxDecoration(),
child: RaisedButton(
color: Colors.white,
child: Text(
'Choose Time',
style: TextStyle(
fontSize: 12.0,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
elevation: 6,
onPressed: () => _selectTime(context),
),
),
Text("${selectedTime.toString()}".split(' ')[0]),
],
),
TitleName(
titleText: 'Pick Up Address',
),
Container(
width: 320,
height: 40,
child: TextFormField(
maxLines: null,
minLines: null,
expands: true,
decoration: InputDecoration(
contentPadding:
EdgeInsets.fromLTRB(35.0, 10.0, 0, 10.0),
hintText: 'Address Here...',
hintStyle: TextStyle(
fontSize: 15.0,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
),
),
onChanged: (value) {
addressText = value;
},
),
),
TitleName(
titleText: 'Additional Notes',
infoIcon: Icons.info,
),
Container(
width: 320,
child: TextFormField(
maxLines: 4,
decoration: InputDecoration(
contentPadding:
EdgeInsets.fromLTRB(35.0, 10.0, 0, 10.0),
hintText: 'E.g. ',
hintStyle: TextStyle(
fontSize: 15.0,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
),
),
onChanged: (value) {
addNotes = value;
},
),
),
Container(
margin: EdgeInsets.fromLTRB(0, 15.0, 0, 0),
width: 75.0,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
border: Border.all(
color: Colors.black,
),
borderRadius: BorderRadius.circular(12.0),
),
child: IconButton(
icon: Icon(Icons.arrow_forward),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ConfirmationOrder(
addressText: addressText,
addNotes: addNotes,
)));
}),
),
],
),
],
),
],
),
),
],
)),
);
}
}
Below the first picture, there's a button to navigate to the second screen.
And here is the class where I extracted DropDownButton :
class MenuDropDown extends StatefulWidget {
final String dropdownText;
final String type;
MenuDropDown({this.dropdownText, this.type});
#override
_MenuDropDownState createState() => _MenuDropDownState();
}
class _MenuDropDownState extends State<MenuDropDown> {
String selectedItem;
List<String> dropdownItems = [];
List<String> groomingTypeList = ['Basic Grooming', 'Full Grooming'];
List<String> catBreedsList = [
'Persia',
'Anggora',
'Domestic',
'Maine Coon',
'Russian Blue',
'Slamese',
'Munchkin',
'Ragdoll',
'Scottish Fold',
];
List<String> catSizeList = [
'Small Size',
'Medium Size',
'Large Size',
'Extra Large Size',
];
List<String> addOnServicesList = [
'Spa & Massage',
'Shaving Hair / Styling',
'Injection Vitamis Skin & Coat',
'Cleaning Pet House and Environment',
'Fur Tangled Treatment',
];
List<String> getListBasedOnName(String value) {
print(value);
switch (value) {
case "groomingType":
return groomingTypeList;
break;
case "catBreeds":
return catBreedsList;
break;
case "catSize":
return catSizeList;
break;
case "addOnServices":
return addOnServicesList;
break;
}
return null;
}
#override
void initState() {
super.initState();
print(widget.type);
dropdownItems = getListBasedOnName(widget.type);
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(0, 8.0, 0, 10.0),
child: Container(
width: 325.0,
height: 50.0,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black45,
offset: Offset(2.5, 5.5),
blurRadius: 5.0,
)
],
borderRadius: BorderRadius.circular(8),
color: Colors.white,
),
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: selectedItem,
hint: Padding(
padding: const EdgeInsets.fromLTRB(22.0, 0, 0, 0),
child: Text(
widget.dropdownText,
style: TextStyle(),
),
),
items: dropdownItems.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedItem = value;
});
}),
),
),
);
}
}
I really got confused because the onChanged function in DropDownButton already used. I barely manage to do the normal pass data from the text widget. But from the Dropdownbutton and the date & time picker, I have no idea how to do it.
Is there any way to get the data from the first screen, because at the moment I still haven't learned about state management or Bloc. And the code still messy I haven't done the refactoring yet. I really Hope you can help with the solution, Thank you!
First, for the MenuDropDown, you're going to want to do a sort of extension for the onChanged method. Add a VoidCallback parameter to the widget's constructor like so:
typedef OnChangeCallback = void Function(dynamic value);
class MenuDropDown extends StatefulWidget {
final String dropdownText;
final String type;
final OnChangeCallback onChanged;
MenuDropDown({this.dropdownText, this.type, this.onChanged});
#override
_MenuDropDownState createState() => _MenuDropDownState();
}
and in the state, call that method as a part of DropdownButton's native onChanged callback:
onChanged: (value) {
setState(() {
selectedItem = value;
});
widget.onChanged(value);
}
And in _InformationDetailState you'll store the currently selected item for each input field and pass an onChanged function that updates the respective field for each input:
String catSize; //Declare at the top of _InformationDetailState
...
MenuDropDown(
dropdownText: 'Cat Size...',
type: "catSize",
onChanged: (value) {
catSize = value;
}
),
Now to pass the data to the next screen. It's never really absolutely necessary to use any kind of state management in your app and I've found that many people use it unnecessarily. In your case, it's absolutely not necessary for just passing data to a single other widget. You're already passing the addressText and addNotes correctly. Just extend this for each parameter you need to show on the confirmation screen. Alternatively, you could store all of the fields in a single Map instead of having a variable for each field and pass that Map to the confirmation page.