Why is selected value not reflecting in showModalBottomSheet with flutter? - flutter

First of all, I created a designed bottom sheet, In which, there are two lists to show numbers(left side) and options(hour, day, week, month) with the help of CupertinoPicker widget,
Numbers will depend on what option I select, If I select the hour, numbers of the left side should be 1-24, and if I select week, numbers should be 1-4, I select the day, numbers should be 1-30 and last I select month number should be 1-12.
Code :
All Lists variable:
List<String> reminderDay = ['hour','day','week','month'];
List<String> reminderHoursVal =['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24'];
List<String> reminderDaysVal =['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31'];
List<String> reminderMonthsVal =['1','2','3','4','5','6','7','8','9','10','11','12'];
List<String> reminderWeeksVal =['1','2','3','4'];
String selectedReminderVal='1';
String selectedReminderDay ='hour';
Code of bottom sheet:
addReminder(){
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
curve: Curves.decelerate,
child: Container(
padding: const EdgeInsets.only(top:8,right: 8, left:8,bottom: 8),
height: MediaQuery.of(context).size.height/2,
// color: Colors.transparent,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30)
)
),
child: Container(
child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(height:10),
Text("Set a reminder",
style: TextStyle(
fontSize:18,
fontWeight:FontWeight.bold,
color: Colors.grey
),
),
SizedBox(height:20),
Container(
margin: const EdgeInsets.only(left: 10, right: 10),
height: MediaQuery.of(context).size.height/4,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(10)
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: 0,
),
itemExtent: 30,
backgroundColor: Colors.grey[100],
onSelectedItemChanged: (int val) {
setState(() {
if(selectedReminderDay=='day'){
selectedReminderVal = reminderDaysVal[val];
}else if(selectedReminderDay=='week'){
selectedReminderVal = reminderWeeksVal[val];
}else if(selectedReminderDay=='month'){
selectedReminderVal = reminderMonthsVal[val];
}else{
selectedReminderVal = reminderHoursVal[val];
}
print("selectedReminderVal:$selectedReminderVal");
});
},
children:selectedReminderDay=='day'?reminderDaysVal
:selectedReminderDay=='week'?reminderWeeksVal
:selectedReminderDay=='month'?reminderMonthsVal:reminderHoursVal// ['hour','day','week','month']; reminderHoursVal
.map(
(item) => Center(
child: Text(
item,
style: TextStyle(
fontSize: 16,
// fontWeight:FontWeight.bold,
),
),
),
)
.toList()),
),
Expanded(
child: CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: 0,
),
itemExtent: 30,
backgroundColor: Colors.grey[100],
onSelectedItemChanged: (int val) {
setState(() {
selectedReminderDay = reminderDay[val];
print("selectedReminderDay:$selectedReminderDay");
});
},
children: reminderDay
.map(
(item) => Center(
child: Text(
item,
style: TextStyle(
fontSize: 16,
// fontWeight:FontWeight.bold,
),
),
),
)
.toList()),
),
])
),
SizedBox(height:15),
// selectedVal!=null?Text(selectedVal.toString()):Container()
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("You'll get the reminder"),
Text('$selectedReminderVal $selectedReminderDay before the event')
],
),
SizedBox(height:25),
Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: (){
Navigator.pop(context);
},
child: Text("Cancel",
style: TextStyle(
fontSize: 18,
color: Colors.blue,
fontWeight: FontWeight.bold
),
),
),
InkWell(
onTap: (){
Navigator.pop(context);
},
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8)
),
width: MediaQuery.of(context).size.width/5,
height: MediaQuery.of(context).size.height/25 ,
child: Text("Save", style:TextStyle(
color: Colors.white,
fontSize: 19
)),
),
)
],
),
)
],
),
)
),
);
},
);
}
Screenshot:

This is because the state you are setting is different from the state in the modal bottom sheet.
Right now, when you are calling setState, you are actually rebuilding the stateful widget under the modal bottom sheet.
To fix this, just wrap your bottom sheet in a Stateful Builder.
StatefulBuilder(
builder: (context, setState) {
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets,
duration: const Duration(milliseconds: 100),
curve: Curves.decelerate,
child: Container(
padding: const EdgeInsets.only(top:8,right: 8, left:8,bottom: 8),
height: MediaQuery.of(context).size.height/2,
// color: Colors.transparent,
decoration: BoxDecoration(
color: Colors.white,
....

when we create new context widgets in existing state there states become different, bottomSheets are similar to dialogBox with new context and builder build a completely new widget out of the parent state, to create its own stateful state Wrap it with the stateful builder and user its own setState to change anything in this context not the parent one
eg:
StatefulBuilder(
builder: (context, setStateChild) {
return AnimatedPadding(...
Expanded(
child: CupertinoPicker(
scrollController: new FixedExtentScrollController(
initialItem: 0,
),
itemExtent: 30,
backgroundColor: Colors.grey[100],
onSelectedItemChanged: (int val) {
setStateChild(() {
selectedReminderDay = reminderDay[val];
print("selectedReminderDay:$selectedReminderDay");
});
},);
child: ... ),
}

Related

How to show loading indicator on only one list item in Flutter?

I have a list of items with buttons. By clicking on one element on the button, I get a CircularProgressIndicator. I ran into a problem that when I click on one card per button, I get a loading indicator on all cards. How can I make the loading indicator appear on only one card that I click on?
bool isApprovedLoading = false;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: Padding(
padding: const EdgeInsets.only(top: 10, bottom: 18),
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: list.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 7.0, sigmaY: 7.0),
child: Container(
height: 152,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color:
constants.Colors.greyXDark.withOpacity(0.8),
),
child: Row(
children: [
Expanded(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
isApprovedLoading
? const CircularProgressIndicator(
color: constants
.Colors.purpleMain)
: OrderButton(
text: 'Approved',
svgIcon:
SvgPicture.asset(
constants.Assets
.enable,
color: constants
.Colors
.purpleMain),
textStyle: const TextStyle(
fontFamily: constants
.FontFamily
.AvenirLtStd,
fontSize: 12,
fontWeight:
FontWeight.w800,
color: constants
.Colors
.purpleMain),
borderColor: constants
.Colors.purpleMain,
buttonColor: constants
.Colors.greyXDark
.withOpacity(0.5),
onTap: () {
setState(() {
isApprovedLoading =
true;
});
_confirmOrder(
context,
list[index].id,
poyntBookingsCubit,
user,
);
},
),
],
),
),
],
),
),
),
),
],
),
),
),
),
);
},
)
),
),
);
you need define new variable like:
int selectedItem = -1;
and change your asdasd to this:
onTap: () {
setState(() {
isApprovedLoading = true;
selectedItem = index;
});
_confirmOrder(
context,
list[index].id,
poyntBookingsCubit,
user,
);
},
then use it like this:
isApprovedLoading && selectedItem == index
? const CircularProgressIndicator(
color: constants.Colors.purpleMain)
: OrderButton(...),

Searchbar didn't worked properly in the draggablescrollablesheet (Flutter)

I'm a Flutter newbie. I had a trouble with my own searchbar in the draggable bottom sheet. I created a draggable bottom sheet to search for information when clicking to the search field from the main screen. I added a searchbar and a listview in the sheet but the searchbar did not work properly. It could not instantly filter out the data, it just showed the results when closing the keyboard. Anyone helps me please.
class AddPersonalInfo extends StatefulWidget {
final String mail, password;
const AddPersonalInfo({Key? key, required this.mail, required this.password})
: super(key: key);
#override
_AddPersonalInfoState createState() => _AddPersonalInfoState();
}
class _AddPersonalInfoState extends State<AddPersonalInfo> {
TextEditingController _search = TextEditingController();
List<City> cites = [
City(id: 1, code: "HN", name: "Ha Noi"),
City(id: 2, code: "HCM", name: "Ho CHi Minh"),
City(id: 3, code: "DN", name: "Da Nang"),
City(id: 4, code: "HP", name: "Hai Phong"),
City(id: 5, code: "CT", name: "Can Tho"),
City(id: 6, code: "DNN", name: "Dong Nai"),
City(id: 7, code: "KH", name: "Khanh Hoa"),
City(id: 8, code: "PY", name: "Phu Yen"),
City(id: 9, code: "NT", name: "Nha Trang"),
City(id: 10, code: "VL", name: "Vinh Long"),
City(id: 11, code: "HD", name: "Hai Duong"),
City(id: 12, code: "BD", name: "Binh Duong")
];
City? selected;
List<City> foundCity = [];
#override
void initState() {
super.initState();
setState(() {
foundCity = cites;
});
}
#override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
Orientation orientation = MediaQuery.of(context).orientation;
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: LayoutBuilder(builder: (context, snapshot) {
if (snapshot.maxWidth <= screenSize.width &&
orientation == Orientation.portrait) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
padding: EdgeInsets.fromLTRB(30 * screenScale(context), 0,
30 * screenScale(context), 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
children: [
Container(
alignment: Alignment.topCenter,
padding: EdgeInsets.only(
bottom: 20 * screenScale(context)),
child: introText("Create account", context),
),
backBtn(context),
],
),
mainText(
"Let's create an account to grab all latest gadgets and enjoy the best experiences.",
context),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [_fnameField(), _lnameField()],
),
_phoneField(),
GestureDetector(
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => buildSheet(),
),
child: Container(
height: 50,
margin: EdgeInsets.only(top: 15),
padding: EdgeInsets.only(left: 10, right: 7),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(7),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
selected == null
? Text("Select city",
style: TextStyle(
fontSize: 16,
color: Colors.grey.shade700))
: Text("${selected!.name}",
style: TextStyle(
fontSize: 16, color: Colors.black)),
Icon(Ionicons.chevron_down_outline, size: 24)
],
),
),
)
],
),
),
);
} else {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
padding: EdgeInsets.fromLTRB(30 * screenScale(context), 0,
30 * screenScale(context), 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [],
),
),
);
}
}),
),
),
);
}
Widget buildSheet() {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pop();
},
child: DraggableScrollableSheet(
initialChildSize: 0.9,
builder: (_, controller) => Container(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Column(
children: [
Divider(
thickness: 4,
color: Colors.grey.shade300,
endIndent: 170,
indent: 170,
),
Padding(
padding: EdgeInsets.only(bottom: 15, top: 5),
child: Text("Select city",
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.bold)),
),
Padding(
padding: EdgeInsets.only(bottom: 10),
child: TextFormField(
controller: _search,
keyboardType: TextInputType.name,
style: TextStyle(fontSize: 16 * fontScale(context)),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(7 * screenScale(context))),
contentPadding:
EdgeInsets.only(top: 10 * screenScale(context)),
hintText: 'Search',
prefixIcon: Icon(Ionicons.search_outline),
),
onChanged: (value) {
setState(() {
foundCity = cites
.where(
(city) => city.name.toLowerCase().contains(value))
.toList();
});
},
),
),
Expanded(
child: foundCity.length > 0
? ListView.builder(
itemCount: foundCity.length,
itemBuilder: (context, index) {
final city = foundCity[index];
return ListTile(
title: Text(city.name),
onTap: () {
Navigator.of(context).pop();
setState(() {
selected = city;
});
},
);
},
)
: Padding(
padding: EdgeInsets.only(top: 50),
child: Text(
"No data.",
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
);
}
}
Ok I checked you code, and it seems you missed a small issue, when you're creating a showModalBottomSheet, it's no longer part of your stateful widget
I've adjusted the code so that it works now:
Widget buildSheet(BuildContext context) {
return StatefulBuilder(builder: (BuildContext context, setState) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pop();
},
child: DraggableScrollableSheet(
initialChildSize: 0.9,
builder: (_, controller) => Container(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Column(
children: [
Divider(
thickness: 4,
color: Colors.grey.shade300,
endIndent: 170,
indent: 170,
),
Padding(
padding: EdgeInsets.only(bottom: 15, top: 5),
child: Text("Select city",
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.bold)),
),
Padding(
padding: EdgeInsets.only(bottom: 10),
child: TextFormField(
controller: _search,
keyboardType: TextInputType.name,
style: TextStyle(fontSize: 16),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(7)),
contentPadding: EdgeInsets.only(top: 10),
hintText: 'Search',
prefixIcon: Icon(Icons.access_alarm),
),
onChanged: (value) {
setState(() {
foundCity = cites
.where((city) =>
city.name!.toLowerCase().contains(value))
.toList();
});
},
),
),
Expanded(
child: foundCity.isNotEmpty
? ListView.builder(
itemCount: foundCity.length,
itemBuilder: (context, index) {
final city = foundCity[index];
return ListTile(
title: Text(city.name!),
onTap: () {
Navigator.of(context).pop();
setState(() {
selected = city;
});
},
);
},
)
: Padding(
padding: EdgeInsets.only(top: 50),
child: Text(
"No data.",
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
);
});
}

Flutter: How can i put Textfield input into a list to build a ListView.builder

Im trying to build a listviewbuilder since a few days. For that i need the textfield input from another screen. I looked a lot of tutorials and question but it doesnt work.Im trying to put the input from multiple textfields into multiple lists to build a Listview builder. It would be the best if i can save all Textfield input when i press on flatbutton. I hope someone can help me.
First page
List<String> time = ["8:00"];List<String>
List<String> where = ["Am See"];
List<String> who = ["Eric"];
List<String> when = ["Donnerstag 21.4.21"];
body: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(children: [
Upperscreen(size: size),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: where.length,
itemBuilder: (BuildContext context, int Index) {
return Column(children: [
SizedBox(
height: 40,
),
Container(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Meet1()));
},
child: Container(
width: size.width * 0.9,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(70)),
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomRight,
colors: [
Colors.green,
Colors.orange,
],
),
),
child: Column(children: <Widget>[
SizedBox(
height: 10,
),
Padding(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
Text(
time[Index],
style: TextStyle(
color: Colors.white,
fontSize: 40,
fontWeight:
FontWeight.bold),
),
SizedBox(
height: 10,
),
Text(
who[Index],
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight:
FontWeight.bold),
),
Text(
when[Index],
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight:
FontWeight.bold),
),
Text(
where[Index],
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight:
FontWeight.bold),
Second page
child: Column(children: <Widget>[
SizedBox(
height: 10,
),
Padding(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
TextField(decoration: InputDecoration(hintText: " Time ")),
SizedBox(
height: 10,
),
TextField(
decoration: InputDecoration(hintText: " Who "),
),
SizedBox(
height: 10,
),
TextField(
decoration: InputDecoration(hintText: " Date "),
),
SizedBox(
height: 10,
),
TextField(
decoration: InputDecoration(hintText: " Where "),
),
SizedBox(height: 10)
],
),
),
]));
Here the Flatbutton to add all.
return FlatButton(
child: Icon(
Icons.check_circle_outline_rounded,
color: Colors.green,
size: 120,
),
onPressed: () {
Navigator.of(context).popUntil((route) => route.isFirst);
},
Use a TextEditingController(), just like this -
TextEditingController() myController = TextEditingController();
Then assign this controller to controller property in TextField() -
TextField(
controller: myController,
),
Then use myController.text to retrieve the text from TextField(), and pass it to other pages as a String parameter -
Example -
class Screen1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
//....
body: FlatButton(
child: Icon(
Icons.check_circle_outline_rounded,
color: Colors.green,
size: 120,
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (builder) {
return Screen2(text: myController.text);
}));
},
//....
),
);
}
}
Second Page -
class Screen2 extends StatelessWidget {
String text;
Screen2({this.text});
#override
Widget build(BuildContext context) {
return Scaffold(
//....
body: Text(text),
);
}
}
Go to this link to see another example
Now, here I used only 1 parameter "text". You can use multiple parameters like - "text1", "text2", "text3" and so on, as per your requirement, and use as many TextEditingController() for this.
*****Also Note that use of FlatButton() is depreciated, you can use a TextButton() instead

trying to add to bag and to update prices and cups

I am trying to do this test TODOs: but i have been have issuses pls help: i am trying to Uncomment the _confirmOrderModalBottomSheet() method to show summary of order, Uncomment the setState() function to clear the price and cups, and Change the 'price' to 0 when this button is clicked Increment the _cupsCounter when 'Add to Bag' button is clicked, and to Call setState((){}) method to update both price and cups counter when 'Add to Bag' button is clicked
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Coffee Test',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.white,
),
home: MyHomePage(title: 'Coffee Test'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedPosition = -1;
String _coffeePrice ="0";
int _cupsCounter =0;
int price = 0;
String _currency ="₦";
static const String coffeeCup ="images/coffee_cup_size.png";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: FlatButton(
onPressed: (){
//TODO: Uncomment the _confirmOrderModalBottomSheet() method to show summary of order
//_confirmOrderModalBottomSheet(totalPrice: "$_currency$price", numOfCups: "x $_cupsCounter");
},
child: Text("Buy Now",style: TextStyle(color: Colors.black87),),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18.0), side: BorderSide(color: Colors.blue))
),
actions: [
InkWell(
onTap: () {
//TODO: Uncomment the setState() function to clear the price and cups
//TODO: Change the 'price' to 0 when this button is clicked
setState(() {
this.price = -1;
this._cupsCounter = 0;
});
Icon(Icons.clear);
}),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
height: double.maxFinite,
alignment: Alignment.center,
child: Text("$_cupsCounter Cups = $_currency$price.00", style: TextStyle(fontSize: 18),),
),
)
],
),
body: Padding(padding: EdgeInsets.all(20), child: _mainBody(),) // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _mainBody(){
return SingleChildScrollView(
child: Container(
height: double.maxFinite,
width: double.maxFinite,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 0,
child: Stack(
children: [
Container(
width: double.maxFinite,
height: 250,
margin: EdgeInsets.only(left: 50, right: 50, bottom: 50, top: 60),
decoration: BoxDecoration(borderRadius:
BorderRadius.all(Radius.circular(180)),
color: Color.fromRGBO(239, 235, 233, 100)),
),
Container(
alignment: Alignment.center,
width: double.maxFinite,
height: 350,
child: Image.asset("images/cup_of_coffee.png", height: 300,),
)
],
)),
Padding(padding: EdgeInsets.all(10),),
Expanded(flex: 0,child: Text("Caffè Americano",
style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 30),)),
Padding(padding: EdgeInsets.all(6),),
Expanded(flex: 0, child: Text("Select the cup size you want and we will deliver it to you in less than 48hours",
style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 14, color: Colors.black45,),
textAlign: TextAlign.start,),
),
Container(
margin: EdgeInsets.only(top: 30, left: 20),
height: 55,
width: double.maxFinite,
alignment: Alignment.center,
child:Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RichText(text: TextSpan(
text: _currency,
style: TextStyle(fontWeight: FontWeight.bold,
fontSize: 25, color: Colors.black87),
children: [
TextSpan(text: _coffeePrice, style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold))
]
),),
Padding(
padding: EdgeInsets.only(right: 15),
),
ListView.builder(itemBuilder: (context, index){
return InkWell(
child: _coffeeSizeButton(_selectedPosition == index,
index ==0? "S" : index ==1? "M": "L"),
onTap: (){
setState(() {
this._coffeePrice= index ==0? "300" : index ==1? "600": "900";
_selectedPosition = index;
});
},
);
}, scrollDirection: Axis.horizontal,
itemCount: 3, shrinkWrap: true,),
],),
),
Container(
margin: EdgeInsets.only(top: 30),
padding: EdgeInsets.all(10),
width: double.maxFinite,
height: 70,
child: FlatButton(onPressed: (){
//TODO: Currently _cupsCounter only show 1 when this button is clicked.
// TODO: Increment the _cupsCounter when 'Add to Bag' button is clicked'
//TODO: Call setState((){}) method to update both price and cups counter when 'Add to Bag' button is clicked
this._cupsCounter = 1;
this.price += int.parse(_coffeePrice);
}, child: Center(child: Text("Add to Bag",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),)
,),
color: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50)
),
),
)
],
),
),
);
}
Widget _coffeeSizeButton(bool isSelected, String coffeeSize){
return Stack(
children: [
Container(alignment: Alignment.center, width: 55,
child: Text(coffeeSize, style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold,
color: isSelected? Colors.blue: Colors.black45),),),
new Container(
margin: EdgeInsets.only(right: 10),
child: Image.asset(coffeeCup, width:50, color: isSelected ? Colors.blue: Colors.black45,),
decoration: BoxDecoration(border: Border(top: BorderSide(color: isSelected? Colors.blue: Colors.black45,
width: isSelected? 2: 1), left: BorderSide(color: isSelected? Colors.blue: Colors.black45,
width: isSelected? 2: 1), bottom: BorderSide(color: isSelected? Colors.blue: Colors.black45,
width: isSelected? 2: 1), right: BorderSide(color: isSelected ?Colors.blue: Colors.black45 ,
width: isSelected? 2: 1)), borderRadius: BorderRadius.all(Radius.circular(5))),
)
],
);
}
void _confirmOrderModalBottomSheet({String totalPrice, String numOfCups}){
showModalBottomSheet(
context: context,
builder: (builder){
return new Container(
height: 150.0,
color: Colors.transparent, //could change this to Color(0xFF737373),
//so you don't have to change MaterialApp canvasColor
child: new Container(
padding: EdgeInsets.all(10),
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(10.0),
topRight: const Radius.circular(10.0))),
child: Column(
children: [
Container(
child: Text("Confirm Order",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),),
alignment: Alignment.center, height: 30, decoration: BoxDecoration(
), ),
_getEstimate(totalPrice, numOfCups)
],
)),
);
}
);
}
Widget _getEstimate(String totalPrice, String numOfCups){
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Image.asset("images/cup_of_coffee.png", height: 70, width: 50,),
Padding(padding: EdgeInsets.all(10)),
Text(numOfCups, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),),
Padding(padding: EdgeInsets.all(10)),
Text(totalPrice, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),)
],
);
}
}
You're setting 1 to your _cupsCounter instead of adding one to it.
When updating your values, putting the operation i.e _cupsCounter += 1; in setState will update the state of your widget which makes values change in your widgets.
setState((){
_cupsCounter += 1; // make it +=1 instead of =1.
price += int.parse(_coffeePrice);
});
Also you can use setState by putting it after your operation which will update the state of your widget after the operation is done.
_cupsCounter += 1; // make it +=1 instead of =1.
price += int.parse(_coffeePrice);
setState((){});
Full code should look like this.
Container(
margin: EdgeInsets.only(top: 30),
padding: EdgeInsets.all(10),
width: double.maxFinite,
height: 70,
child: FlatButton(onPressed: (){
// Currently _cupsCounter only show 1 when this button is clicked.
// Increment the _cupsCounter when 'Add to Bag' button is clicked'
// Call setState((){}) method to update both price and cups counter when 'Add to Bag' button is clicked
setState((){
_cupsCounter += 1; // make it +=1 instead of =1.
price += int.parse(_coffeePrice);
}); // call setState like this
}, child: Center(child: Text("Add to Bag",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),)
,),
color: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50)
),
),
)

List.Builder giving range error in Flutter

I have added my entire code over here. The getRecords method takes more time to add to the lists and hence my list returns empty at first and so listbuilder fails giving range error and that only range accepted is 0. BTW, Its a Todo app.
InitState :
void initState() {
super.initState();
setState(() {
getRecords();
});
}
Getting from the database
void getRecordsAndDisplay() async {
final records = await Firestore.instance.collection('tasks').getDocuments();
for (var record in records.documents) {
if (record.data['phone'] == '1') {
int len = record.data['task'].length;
if (len != null || len != 0) {
for (int i = 0; i < len; i++) {
String temp = record.data['task'][i];
tasks.add(temp);
}
}
else
continue;
}
else
continue;
}
setState(() {
listView = ListView.builder(
scrollDirection: Axis.vertical,
itemCount: tasks.length,
itemBuilder: (BuildContext context,int index) {
return Container(
margin: EdgeInsets.only(bottom: 10.0),
decoration: BoxDecoration(
color: Colors.deepPurple[700],
borderRadius: BorderRadius.all(Radius.circular(20.0)),
),
child: ListTile(
onTap: (){},
leading: IconButton(
icon: Icon(Icons.delete),
iconSize: 25.0,
color: Colors.white,
onPressed: () {
setState(() {
tasks.removeAt(index);
checkValue.removeAt(index);
updateValue();
});
},
),
title: Text(
'${tasks[index]}',
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
fontWeight: FontWeight.bold,
decoration: checkValue[index]
? TextDecoration.lineThrough
: null,
),
),
trailing: Checkbox(
value: checkValue[index],
activeColor: Colors.white,
checkColor: Colors.deepPurple[700],
onChanged: (bool value) {
setState(() {
checkValue[index] = !checkValue[index];
});
},
),
),
);
},
);
});
}
Scaffold:
return Scaffold(
backgroundColor: Color(0xff8780FF),
body: SafeArea(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
colors: [Colors.deepPurple[400], Color(0xff6B63FF)])),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.fromLTRB(30.0, 30.0, 30.0, 15.0),
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: 1.0,
blurRadius: 50.0,
),
]),
child: Icon(
Icons.list,
color: Colors.white,
size: 30.0,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: EdgeInsets.only(
bottom: 20.0,
left: 30.0,
),
child: Text(
'Todo List',
style: TextStyle(
color: Colors.white,
fontSize: 35.0,
fontWeight: FontWeight.w900,
),
),
),
Expanded(
child: SizedBox(
width: 20.0,
),
),
IconButton(
padding: EdgeInsets.only(
right: 10.0,
bottom: 20.0,
),
icon: Icon(Icons.add),
iconSize: 30.0,
color: Colors.white,
onPressed: () async {
final resultText = await showModalBottomSheet(
context: context,
builder: (context) => AddTaskScreen(),
isScrollControlled: true);
setState(() {
tasks.add(resultText);
checkValue.add(false);
Firestore.instance
.collection('tasks')
.document('1')
.updateData({
'task': FieldValue.arrayUnion([resultText]),
});
});
},
),
IconButton(
padding: EdgeInsets.only(
right: 10.0,
bottom: 20.0,
),
icon: Icon(Icons.delete_outline),
iconSize: 30.0,
color: Colors.white,
onPressed: () {
setState(() {
tasks.clear();
checkValue.clear();
Firestore.instance
.collection('tasks')
.document('1')
.updateData({
'task': null,
});
});
},
),
],
),
],
),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0),
height: MediaQuery.of(context).size.height,
child: listView,
),
),
],
),
),
),
);
Please help me out. I am stuck with this for a long time :(
the problem is that in initstate you cannot await for async methods so you should implement a StreamBuilder that wraps your listview..
A streambuilder is a widget that takes a stream and waits for the call completition then when the data is ok shows a widget -> your listview
A little example
StreamBuilder(
stream: YOUR_ASYNC_CALL_THAT_RETURN_A_STREAM,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
alignment: Alignment.center,
child: Text(
"NO ITEMS"
),
);
}
else {
var yourList = snapshot.data.documents;//there you have to do your implementation
return ListView.builder(
itemBuilder: (context, index) => buildItem(index,yourList[index]),
itemCount: yourList.length,
);
}
},
),