Use setstate() inside a search delegate buildResults() ..? - flutter

I am using the flutter show search() and a class extended by SearchDelegate.
i pass a list of my class objects to the delegate class. In the buildResult() method, i need to create a custom list view. each list view attribute needs to change its underlying widgets. but i can not use setstate() in the buildResults() function since it does not have a builder.
here is my code.hope it all makes sense. just FYI its a simple search for product for online store.
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:rashan/models/order.dart';
import 'package:rashan/models/product.dart';
class ProductScreen extends StatefulWidget {
static final String id = "product_screen";
final String category;
final String subCategory;
ProductScreen({
#required this.category,
#required this.subCategory,
});
#override
_ProductScreenState createState() => _ProductScreenState();
}
class _ProductScreenState extends State<ProductScreen> {
final productList = <Product>[];
#override
void initState() {
super.initState();
FirebaseDatabase.instance
.reference()
.child("Data")
.child("All Products")
.child(widget.category)
.child(widget.subCategory)
.once()
.then((snapshot) {
setState(() {
var result = snapshot.value.values as Iterable;
for (var item in result) {
var product = Product(
id: item['id'],
name: item['name'],
image: item['image'],
price: item['price'],
quantity: item['quantity'],
category: item['category'],
subCategory: item['subCategory']);
productList.add(product);
}
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {
Navigator.pop(context);
},
),
title: Text(
"Rashan",
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 48.0,
fontFamily: "Billabong"),
),
actions: <Widget>[
productList.length != 0
? IconButton(
icon: Icon(
Icons.search,
color: Colors.black,
),
onPressed: () {
showSearch(
context: context, delegate: ProductSearch(productList));
},
)
: Container()
],
),
body: Center(
child: Container(
child: productList.length == 0
? Text(
"Loading Products ...!",
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 16.0,
fontWeight: FontWeight.w500),
)
: ListView.builder(
itemCount: productList.length,
itemBuilder: (BuildContext context, int index) {
return productUI(productList[index]);
},
)),
),
);
}
Widget productUI(Product product) {
String _price = product.price;
var order = new Order();
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
color: Colors.white,
elevation: 5.0,
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Container(
padding: EdgeInsets.all(5.0),
child: Row(
children: <Widget>[
Container(
height: 120.0,
width: 120.0,
child: Image.network(product.image),
),
SizedBox(
width: 10.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 5.0),
padding: EdgeInsetsDirectional.only(end: 4.0),
child: Text(
product.name,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.w600),
),
),
SizedBox(
height: 10.0,
),
Text(product.category),
Text(product.subCategory),
Text(
'Price: Rs $_price ',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 16.0,
fontWeight: FontWeight.w500),
),
SizedBox(
height: 10.0,
),
Container(
margin:
EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
height: 40.0,
color: Theme.of(context).primaryColor,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
order.decrementQuantity(product.id);
});
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.remove,
color: Colors.white,
),
),
),
Expanded(
child: Center(
child: Text(
order.getQuantity(product.id) == 0
? "Add to Cart"
: order.getQuantity(product.id).toString(),
style: TextStyle(color: Colors.white),
)),
),
GestureDetector(
onTap: () {
setState(() {
order.addOrIncrement(product.id);
});
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.add,
color: Colors.white,
),
),
)
],
),
)
],
),
)
],
),
),
);
}
}
class ProductSearch extends SearchDelegate<Product> {
List<Product> _productList;
ProductSearch(this._productList);
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(
Icons.clear,
),
onPressed: () {
query = '';
})
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(
Icons.arrow_back,
),
onPressed: () {
close(context, null);
});
}
#override
Widget buildResults(BuildContext context) {
List<Product> searchResult = [];
for (var product in _productList) {
if (query.isNotEmpty && product.name.toLowerCase().contains(query)) {
searchResult.add(product);
print(product.name);
}
}
return Container(
child: ListView.builder(
itemCount: searchResult.length,
itemBuilder: (BuildContext context, int index) {
return productUI(context, searchResult[index]);
},
),
);
}
#override
Widget buildSuggestions(BuildContext context) {
return Container();
}
Widget productUI(BuildContext context, Product product) {
String _price = product.price;
var order = new Order();
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
color: Colors.white,
elevation: 5.0,
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Container(
padding: EdgeInsets.all(5.0),
child: Row(
children: <Widget>[
Container(
height: 120.0,
width: 120.0,
child: Image.network(product.image),
),
SizedBox(
width: 10.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 5.0),
padding: EdgeInsetsDirectional.only(end: 4.0),
child: Text(
product.name,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.w600),
),
),
SizedBox(
height: 10.0,
),
Text(product.category),
Text(product.subCategory),
Text(
'Price: Rs $_price ',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 16.0,
fontWeight: FontWeight.w500),
),
SizedBox(
height: 10.0,
),
Container(
margin:
EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
height: 40.0,
color: Theme.of(context).primaryColor,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
// setState(() {
order.decrementQuantity(product.id);
// });
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.remove,
color: Colors.white,
),
),
),
Expanded(
child: Center(
child: Text(
order.getQuantity(product.id) == 0
? "Add to Cart"
: order.getQuantity(product.id).toString(),
style: TextStyle(color: Colors.white),
)),
),
GestureDetector(
onTap: () {
// setState(() {
order.addOrIncrement(product.id);
// });
},
child: Container(
height: 50.0,
width: 50.0,
child: Icon(
Icons.add,
color: Colors.white,
),
),
)
],
),
)
],
),
)
],
),
),
);
}
}

If you want to have a state for each list item, you can just create a StatefulWidget instead of using the method productUI(). If you want to control the state from the upper Widget (List), then just create a StatefulWidget for that level.
class ProductUi extends StatefulWidget {
final Product product;
ProductScreen(#required this.product) : assert(product != null);
#override
_ProductUiState createState() => _ProductUiState();
}
class _ProductUiState extends State<ProductUi> {
#override
Widget build(BuildContext context) => productUI(context, widget.product);
Widget productUI(BuildContext context, Product product) {
// ...
// now you can setState from taps and delegates
}
}

Related

Container not changing color until file is refreshed

I want to change color of container onTap from shade of grey to white and everything works alright in my FeedScreen file but in filter file I need to exit and then go back to see the change even tho i did everything exactly same in FeedScreen and Filter file. i think the problem has something to do with the fact that i enter filter menu from feed screen on tap of the button but im not sure.
this is filter file that doesnt work properly:
class _ExampleState extends State<Example> {
int _selectedindex = 0;
void _onItemTapped(int index) {
setState(() {
_selectedindex = index;
});
}
int index = 0;
var random = Random();
List<Color> myColors = [
Colors.white,
Colors.grey.shade900,
Colors.green,
Colors.cyan,
Colors.pink,
];
void changeColorsIndex() {
setState(() {
index = random.nextInt(4);
});
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
width: size.width,
height: 40,
color: Color.fromARGB(255, 28, 28, 28),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 1.5),
child: IconButton(
onPressed: (() {
setState(
() {
showDialog(
context: context,
builder: (BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
width: size.width,
height: size.height,
child: Scaffold(
backgroundColor: Colors.black,
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Column(
children: [
Center(
child: Padding(
padding:
const EdgeInsets.only(top: 5),
child: IconButton(
onPressed: () {
Navigator.pop(
context,
MaterialPageRoute(
builder: (context) =>
TopTab(),
),
);
},
icon: const Icon(
Icons.arrow_drop_down),
iconSize: 45,
color: Colors.white,
),
),
),
const Gap(15),
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
setState(() {
changeColorsIndex();
});
},
child: Container(
width: 199,
height: 50,
color: myColors[index],
child: const Center(
child: Text(
'Men\'s',
style: TextStyle(
fontSize: 14,
fontWeight:
FontWeight.w600,
color: Colors.white),
),
),
),
),
],
),
),
],
),
),
),
),
),
);
},
);
},
);
}),
icon: Icon(Ionicons.options_outline),
iconSize: 20,
color: Colors.white,
),
),
const Gap(6),
Text(
'Filter by size',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
fontWeight: FontWeight.w700),
),
],
),
);
}
}
and this is FeedScreen file that works normally:
class _FeedScreenState extends State<FeedScreen> {
int index = 0;
var random = Random();
List<Color> myColors = [
Colors.white,
Colors.grey.shade900,
Colors.green,
Colors.cyan,
Colors.pink,
];
void changeColorsIndex() {
setState(() {
index = random.nextInt(4);
});
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
body: ListView(
children: [
Container(
width: size.width,
height: size.height,
color: Colors.black,
child: Column(
// this column is start of everything and container below is filter part just below app bar
children: [
Example(),
InkWell(
onTap: changeColorsIndex,
child: Container(
width: size.width,
height: 450,
color: myColors[index],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Padding(
padding: EdgeInsets.only(top: 15, left: 15),
child: Text(
'Air Force 1 x Premium Goods',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: Colors.white),
),
),
Gap(5),
Padding(
padding: EdgeInsets.only(left: 15),
child: Text(
'The Sophia',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
color: Colors.white),
),
),
Gap(50),
Image(
image: AssetImage('assets/AF1xPGmain.png'),
),
],
),
),
),
],
),
)
],
),
);
}
}
i tried with GestureDecoration also even tho its same thing but it just doesnt work
you can add a StatefulBuilder Widget to make your showDialog able to use setState
try it and let me please know if it works
class _ExampleState extends State<Example> {
int _selectedindex = 0;
void _onItemTapped(int index) {
setState(() {
_selectedindex = index;
});
}
int index = 0;
var random = Random();
List<Color> myColors = [
Colors.white,
Colors.grey.shade900,
Colors.green,
Colors.cyan,
Colors.pink,
];
void changeColorsIndex() {
setState(() {
index = random.nextInt(4);
});
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
width: size.width,
height: 40,
color: Color.fromARGB(255, 28, 28, 28),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 1.5),
child: IconButton(
onPressed: (() {
setState(
() {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(builder: (context, setState) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
width: size.width,
height: size.height,
child: Scaffold(
backgroundColor: Colors.black,
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Column(
children: [
Center(
child: Padding(
padding:
const EdgeInsets.only(top: 5),
child: IconButton(
onPressed: () {
Navigator.pop(
context,
MaterialPageRoute(
builder: (context) =>
TopTab(),
),
);
},
icon: const Icon(
Icons.arrow_drop_down),
iconSize: 45,
color: Colors.white,
),
),
),
const Gap(15),
Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
setState(() {
changeColorsIndex();
});
},
child: Container(
width: 199,
height: 50,
color: myColors[index],
child: const Center(
child: Text(
'Men\'s',
style: TextStyle(
fontSize: 14,
fontWeight:
FontWeight.w600,
color: Colors.white),
),
),
),
),
],
),
),
],
),
),
),
),
),
);
});
},
);
},
);
}),
icon: Icon(Ionicons.options_outline),
iconSize: 20,
color: Colors.white,
),
),
const Gap(6),
Text(
'Filter by size',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
fontWeight: FontWeight.w700),
),
],
),
);
}
}
you have written different onTap method in the filter file
change
onTap: () {
setState(() {
changeColorsIndex();
});
},
To
onTap: changeColorsIndex,
In filter file wrap your dialog with stateful builder as shown here

Pixel Overflow Problem While Opening the Keyboard

For the following code, I am getting a pixel overflow whenever I am trying to open the keyboard to search for something in the search bar.
I cannot remove the container having the specified height of 500 because doing so produces other errors.
import 'package:chatapp/services/database.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Search extends StatefulWidget {
_Search createState() => _Search();
}
class _Search extends State<Search> {
QuerySnapshot<Map<String, dynamic>>? searchResultSnapshot;
bool isLoading = false;
bool haveUserSearched = false;
DatabaseMethods databaseMethods = new DatabaseMethods();
var searchEditingController = TextEditingController();
InitiateSearch() async {
if (searchEditingController.text.isNotEmpty) {
setState(() {
isLoading = true;
});
await databaseMethods.GetUserByUsername(searchEditingController.text)
.then((snapshot) {
searchResultSnapshot = snapshot;
print("$searchResultSnapshot");
setState(() {
isLoading = false;
haveUserSearched = true;
});
});
}
}
Widget UserList() {
return Container(
height: 500,
child: isLoading
? Container(
child: Center(
child: CircularProgressIndicator(),
),
)
: ListView.builder(
itemCount: searchResultSnapshot?.docs.length ?? 0,
itemBuilder: (context, index) {
return ListTile(
searchResultSnapshot?.docs[index].data()["userName"],
searchResultSnapshot?.docs[index].data()["userEmail"],
);
}),
);
}
Widget ListTile(String? userName, String? userEmail) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userName ?? "",
style: TextStyle(color: Colors.white, fontSize: 16),
),
Text(
userEmail ?? "",
style: TextStyle(color: Colors.white, fontSize: 16),
)
],
),
Spacer(),
GestureDetector(
onTap: () {
null;
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(24)),
child: Text(
"Message",
style: TextStyle(color: Colors.white, fontSize: 16),
),
),
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
titleSpacing: 0,
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
Navigator.pop(context);
});
},
),
title: Text('Search'),
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
Container(
height: 36,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10)),
boxShadow: [
BoxShadow(
color: Colors.black,
),
BoxShadow(
color: Colors.black,
spreadRadius: -12.0,
blurRadius: 12.0,
),
],
),
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 0, horizontal: 10.0),
child: TextFormField(
style: TextStyle(color: Colors.black),
controller: searchEditingController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Search',
hintStyle: TextStyle(color: Colors.grey),
icon: GestureDetector(
onTap: () {
InitiateSearch();
},
child: GestureDetector(
onTap: () {
InitiateSearch();
print('tap');
},
child: Icon(
Icons.search,
color: Colors.grey,
),
),
),
suffixIcon: IconButton(
onPressed: searchEditingController.clear,
icon: Icon(
Icons.cancel_rounded,
color: Colors.grey,
),
),
),
),
),
), //Search
SizedBox(
height: 12,
),
UserList(),
],
),
));
}
}
Could anyone solve this? Also are there anyways to make the container height dynamic. Your help will be highly appreciated.
Okay I found the solution myself. Under UserList() I replaced:
return Container(...);
with:
return Expanded(...);

data not rendered in the first time

currently i'm trying to implement getx to my app, so far so good, i got the data i wanted but i'm kinda having some trouble when i tried to display the data to the screen.
This is where data supposed to be rendered as a horizontal listview
Home Screen
But apparently the data will only appear if i click the promo section and click back to the home section on bottom navigation.
Home Screen 2
Here is my home_controller.dart
class HomeController extends GetxController {
RxList<Hotels> listHotel = <Hotels>[].obs;
RxList<Province> listProvince = <Province>[].obs;
Future getListHotel() async {
final listHotel = await ApiService.getHotel();
this.listHotel.value = listHotel;
}
Future getListProvince() async {
final listProvince = await ApiService.getProvince();
this.listProvince.value = listProvince;
}
#override
void onInit() {
super.onInit();
getListHotel();
getListProvince();
}
}
and this is my home_screen.dart
Widget build(BuildContext context) {
final homeController = Get.put(HomeController());
final authController = Get.put(AuthController());
final orientation = MediaQuery.of(context).orientation;
return Scaffold(
body: SingleChildScrollView(
child: Builder(builder: (context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SafeArea(
child: Padding(
padding: EdgeInsets.only(
left: 5.w, right: 5.w, top: 100.h <= 667 ? 5.h : 4.h),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Daftar Hotel",
style: TextStyle(
color: const Color(0xffF0B900),
fontSize: 10.sp,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 31.h,
width: orientation == Orientation.landscape
? 100.h
: 100.w,
child: ListView.separated(
shrinkWrap: true,
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
width: 1.h,
);
},
scrollDirection: Axis.horizontal,
itemCount: homeController.listHotel.length,
itemBuilder: (context, i) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HotelDetailScreen(
id: homeController
.listHotel[i].id,
checkin: checkInController.text
.toString(),
checkout: checkOutController.text
.toString(),
)));
},
child: SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 70.w,
height: 20.h,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image.network(
homeController.listHotel[i].cover,
fit: BoxFit.cover,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
homeController.listHotel[i].name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 10.sp),
),
SizedBox(
width: 70.w,
child: Text(
homeController.listHotel[i].address,
maxLines: 2,
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 10.sp),
overflow: TextOverflow.clip,
),
)
],
),
),
],
)),
);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
"Rekomendasi",
style: TextStyle(
color: const Color(0xffF0B900),
fontSize: 10.sp,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 25.h,
width: orientation == Orientation.landscape
? 100.h
: 100.w,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: homeController.listProvince.length,
itemBuilder: (context, i) {
String imageUrl = "http://$CURRENT_URL/image/" +
homeController.listProvince[i].cover;
return InkWell(
onTap: () async {
await launch("https://turu.id/property");
},
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SizedBox(
width: 30.w,
child: Stack(
children: [
SizedBox(
width: 30.w,
height: 25.h,
child: ClipRRect(
borderRadius:
BorderRadius.circular(12),
child: Image.network(
imageUrl,
fit: BoxFit.cover,
),
),
),
Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(12),
color:
Colors.black.withOpacity(0.2),
),
),
Padding(
padding: EdgeInsets.only(
left: 2.h, bottom: 2.h),
child: Column(
mainAxisAlignment:
MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
homeController
.listProvince[i].name,
style: TextStyle(
color: Colors.white,
fontSize: 10.sp,
fontWeight:
FontWeight.bold),
),
],
),
)
],
),
),
),
);
}),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
"Promo Mantap",
style: TextStyle(
color: const Color(0xffF0B900),
fontSize: 10.sp,
fontWeight: FontWeight.bold),
),
),
],
),
),
),
],
);
}),
),
);
}
also my index.dart (botnav)
class Index extends StatefulWidget {
const Index({Key? key}) : super(key: key);
#override
_IndexState createState() => _IndexState();
}
class _IndexState extends State<Index> {
int _currentIndex = 0;
final List<Widget> _container = [
const HomeScreen(),
const PromoScreen(),
const BookingStatusScreen(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _container[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
selectedItemColor: const Color(0xffF0B900),
unselectedItemColor: const Color(0xffAFAFAF),
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(
Icons.home,
),
label: "Beranda",
),
BottomNavigationBarItem(
icon: Icon(
Icons.price_change_outlined,
),
label: "Promo",
),
BottomNavigationBarItem(
icon: Icon(
Icons.receipt_long_rounded,
),
label: "Transaksi",
),
])
);
}
}
Any help will be appreciated, Thank you.

How to dynamically sort by date in flutter?

I am working on a to do list app on flutter and I will like to sort the uploaded tasks by date(new ones appear first). for doing this I think a key should be givin to each of the uploaded items in the to do list and this key should include a Date. In react native for doing this I added a function to the data in a flatlist which sorted by date but since I am new to flutter I dont know how can that be done. Any Idea helps :)
main.dart
List<String> _toDoItems = []; //this is the array in which the uploaded tasks are stored, btw Im not using any database.
TextEditingController _controller = TextEditingController();
void _addToDoItem(String task) {
if(task.length > 0) {
setState(() {
_toDoItems.add(task);
});
}
}
void _removeTodoItem(int index) {
setState(() => _toDoItems.removeAt(index));
}
Widget _buildToDoItem(String toDoText, int index) {
return SizedBox(
child: Container(
height: 58,
margin: EdgeInsets.only(left: 22.0, right: 22.0, bottom: 12,),
decoration: BoxDecoration(
border: Border.all(width: 1.5, color: Colors.red),
borderRadius: BorderRadius.all(Radius.circular(18)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children:[
Expanded(
child: ListTile(
title: Text(
toDoText,
style: TextStyle(fontSize: 18),
),
onTap: () => _removeTodoItem(index),
),
),
FlatButton(
child: Text('Delete', style: TextStyle(color: Colors.red, fontSize: 16.5),),
onPressed: () => _removeTodoItem(index),
),
],
),
),
);
}
Widget _buildToDoList() {
return Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
if (index < _toDoItems.length) {
return _buildToDoItem(_toDoItems[index], index);
}
},
),
);
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50),
child: AppBar(
centerTitle: true,
backgroundColor: Colors.red,
title: Text('To Do List', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold,),),
)
),
backgroundColor: Colors.white,
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 60,
margin: EdgeInsets.all(22),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 10,
child: Container(
height: double.infinity,
child: TextField(
controller: _controller,
autofocus: true,
onSubmitted: (val) {
_addToDoItem(val);
_controller.clear();
},
style: TextStyle(fontSize: 18,),
),
),
),
Expanded(
flex: 4,
child: Container(
height: double.infinity,
margin: EdgeInsets.only(left: 12),
child: RaisedButton(
textColor: Colors.white,
color: Colors.red,
child: Text('ADD', style: TextStyle(fontSize: 18)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
onPressed: () {
_addToDoItem(_controller.text);
_controller.clear();
FocusScope.of(context).requestFocus(FocusNode());
},
),
),
),
],
),
),
_buildToDoList()
]
),
),
);
}
You need to use the sort() method of List, also you need to be able to stock in a way or another the time of creation of your task.
I've made an example with the code you've posted. Using it your last created element will always be the first of the list.
import 'package:flutter/material.dart';
class ToDoElement {
final String task;
final DateTime timeOfCreation;
ToDoElement(this.task, this.timeOfCreation);
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
createState() => _MyAppState();
}
class _MyAppState extends State {
TextEditingController _controller = TextEditingController();
List<ToDoElement> _toDoItems = [];
void _removeTodoItem(int index) {
setState(() => _toDoItems.removeAt(index));
}
void _addToDoItem(String task) {
if (task.isNotEmpty) {
setState(() => _toDoItems.add(ToDoElement(task, DateTime.now())));
}
}
Widget _buildToDoItem(String toDoText, int index) {
return SizedBox(
child: Container(
height: 58,
margin: EdgeInsets.only(left: 22, right: 22, bottom: 12),
decoration: BoxDecoration(
border: Border.all(width: 1.5, color: Colors.red),
borderRadius: BorderRadius.all(Radius.circular(18)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: ListTile(
title: Text(
toDoText,
style: TextStyle(fontSize: 18),
),
onTap: () => _removeTodoItem(index),
),
),
FlatButton(
child: Text(
'Delete',
style: TextStyle(color: Colors.red, fontSize: 16.5),
),
onPressed: () => _removeTodoItem(index),
),
],
),
),
);
}
int compareElement(ToDoElement a, ToDoElement b) =>
a.timeOfCreation.isAfter(b.timeOfCreation) ? -1 : 1;
Widget _buildToDoList() {
_toDoItems.sort(compareElement);
return Expanded(
child: ListView.builder(
itemCount: _toDoItems.length,
itemBuilder: (context, index) {
return _buildToDoItem(_toDoItems[index].task, index);
},
),
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50),
child: AppBar(
centerTitle: true,
backgroundColor: Colors.red,
title: Text(
'To Do List',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
)),
backgroundColor: Colors.white,
body: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
Container(
height: 60,
margin: EdgeInsets.all(22),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 10,
child: Container(
height: double.infinity,
child: TextField(
controller: _controller,
autofocus: true,
onSubmitted: (val) {
_addToDoItem(val);
_controller.clear();
},
style: TextStyle(
fontSize: 18,
),
),
),
),
Expanded(
flex: 4,
child: Container(
height: double.infinity,
margin: EdgeInsets.only(left: 12),
child: RaisedButton(
textColor: Colors.white,
color: Colors.red,
child: Text('ADD', style: TextStyle(fontSize: 18)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
onPressed: () {
_addToDoItem(_controller.text);
_controller.clear();
FocusScope.of(context).requestFocus(FocusNode());
},
),
),
),
],
),
),
_buildToDoList()
]),
),
);
}
}

Flutter: Updating UI from outside of a StatefulWidget

I am new to flutter, and working on a shopping cart project. i don't know how exatly i am suppose to ask this question but, here is what i wanted.
I am trying to update my UI when the cart item and prices changes, which means to display sum amount of products from ListView (Stateful widget) to main OrderPage Stateful widget. I know about setState() method, but i think i should use some callback methods here, dont know exactly.
I have explained in short in Image - see below img
What i have done: when user modify cart products, I have saved the value (price/product/count) in to constant value , i calculate the value and save in constant value, and later use that const var in Main widget (Main Ui), which does update when i close and reopen the page, but could not able to update when button pressed (product +/-buttons)
What i want to do is,
Update my total value when +/- buttons are pressed.
Here is my full code:
class cartConstant{
static int packageCount;
static List<int> list;
}
class OrderPage extends StatefulWidget {
#override
_OrderPageState createState() => _OrderPageState();
}
class _OrderPageState extends State<OrderPage> {
int data = 3;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
automaticallyImplyLeading: true,
iconTheme: IconThemeData(
color: Colors.black54, //change your color here
),
backgroundColor: Colors.white,
elevation: 1,
title: Text("Your order Summery",style: TextStyle(color: Colors.black54),),
centerTitle: true,
),
body: Container(
child:
FutureBuilder(
builder: (context, snapshot){
// var datas = snapshot.data;
return
ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: data,
itemBuilder: (BuildContext context, int index){
// Cart cart = datas[index];
return CartListView();
},
padding: EdgeInsets.symmetric(horizontal: 10.0),
scrollDirection: Axis.vertical,
);
},
),
),
bottomNavigationBar: _buildTotalContainer(),
);
}
Widget _buildTotalContainer() {
return Container(
height: 220.0,
padding: EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
children: <Widget>[
SizedBox(
height: 10.0,
),
Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Subtotal",
style: TextStyle(
color: Color(0xFF9BA7C6),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
Text(
cartConstant.packageCount.toString(),
style: TextStyle(
color: Color(0xFF6C6D6D),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
],
),
),
SizedBox(
height: 15,
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Discount",
style: TextStyle(
color: Color(0xFF9BA7C6),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
Text(
"0.0",
style: TextStyle(
color: Color(0xFF6C6D6D),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
],
),
SizedBox(
height: 10.0,
),
Divider(
height: 2.0,
),
SizedBox(
height: 20.0,
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Cart Total",
style: TextStyle(
color: Color(0xFF9BA7C6),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
Text(
cartConstant.packageCount.toString(),
style: TextStyle(
color: Color(0xFF6C6D6D),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
],
),
SizedBox(
height: 20.0,
),
GestureDetector(
onTap: () {
// Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => SignInPage()));
},
child: Container(
height: 50.0,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(35.0),
),
child: Center(
child: Text(
"Proceed To Checkout",
style: TextStyle(
color: Colors.white,
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
),
),
),
SizedBox(
height: 20.0,
),
],
),
);
}
}
class CartListView extends StatefulWidget {
#override
_CartListViewState createState() => _CartListViewState();
}
class _CartListViewState extends State<CartListView> {
int _counter = 1;
int getPrice(int i,int priceC){
cartConstant.packageCount = i*priceC;
return cartConstant.packageCount;
}
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 15.0),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFD3D3D3), width: 2.0),
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
InkWell(
onTap: (){
setState(() {
_counter++;
if (_counter > 20) {
_counter = 20;
}
});
},
child: Icon(Icons.add, color: Color(0xFFD3D3D3))),
Text(
"$_counter",
style: TextStyle(fontSize: 18.0, color: Colors.grey),
),
InkWell(
onTap:(){
setState(() {
_counter--;
if (_counter < 2) {
_counter = 1;
}
});
},
child: Icon(Icons.remove, color: Color(0xFFD3D3D3))),
],
),
),
),
SizedBox(
width: 20.0,
),
Container(
height: 70.0,
width: 70.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/food.jpg"),
fit: BoxFit.cover),
borderRadius: BorderRadius.circular(35.0),
boxShadow: [
BoxShadow(
color: Colors.black54,
blurRadius: 5.0,
offset: Offset(0.0, 2.0))
]),
),
SizedBox(
width: 20.0,
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Employee Package",
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
SizedBox(height: 5.0),
SizedBox(height: 5.0),
Container(
height: 25.0,
width: 120.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Row(
children: <Widget>[
Text("Price",
style: TextStyle(
color: Color(0xFFD3D3D3),
fontWeight: FontWeight.bold)),
SizedBox(
width: 5.0,
),
InkWell(
onTap: () {},
child: Text(
getPrice(_counter, 2000).toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
),
SizedBox(
width: 10.0,
),
],
),
],
),
),
],
),
Spacer(),
GestureDetector(
onTap: () {
},
child: Icon(
Icons.cancel,
color: Colors.grey,
),
),
],
),
),
);
}
}
you can also check from my cart screen shot below:
You can copy paste run full code below
You can use callback refresh() and pass callback to CartListView
code snippet
class _OrderPageState extends State<OrderPage> {
int data = 3;
void refresh() {
setState(() {});
}
...
itemBuilder: (BuildContext context, int index) {
// Cart cart = datas[index];
return CartListView(refresh);
},
...
class CartListView extends StatefulWidget {
VoidCallback callback;
CartListView(this.callback);
...
InkWell(
onTap: () {
setState(() {
_counter++;
if (_counter > 20) {
_counter = 20;
}
});
widget.callback();
},
working demo
full code
import 'package:flutter/material.dart';
class cartConstant {
static int packageCount;
static List<int> list;
}
class OrderPage extends StatefulWidget {
#override
_OrderPageState createState() => _OrderPageState();
}
class _OrderPageState extends State<OrderPage> {
int data = 3;
void refresh() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
automaticallyImplyLeading: true,
iconTheme: IconThemeData(
color: Colors.black54, //change your color here
),
backgroundColor: Colors.white,
elevation: 1,
title: Text(
"Your order Summery",
style: TextStyle(color: Colors.black54),
),
centerTitle: true,
),
body: Container(
child: FutureBuilder(
builder: (context, snapshot) {
// var datas = snapshot.data;
return ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: data,
itemBuilder: (BuildContext context, int index) {
// Cart cart = datas[index];
return CartListView(refresh);
},
padding: EdgeInsets.symmetric(horizontal: 10.0),
scrollDirection: Axis.vertical,
);
},
),
),
bottomNavigationBar: _buildTotalContainer(),
);
}
Widget _buildTotalContainer() {
return Container(
height: 220.0,
padding: EdgeInsets.only(
left: 10.0,
right: 10.0,
),
child: Column(
children: <Widget>[
SizedBox(
height: 10.0,
),
Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Subtotal",
style: TextStyle(
color: Color(0xFF9BA7C6),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
Text(
cartConstant.packageCount.toString(),
style: TextStyle(
color: Color(0xFF6C6D6D),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
],
),
),
SizedBox(
height: 15,
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Discount",
style: TextStyle(
color: Color(0xFF9BA7C6),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
Text(
"0.0",
style: TextStyle(
color: Color(0xFF6C6D6D),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
],
),
SizedBox(
height: 10.0,
),
Divider(
height: 2.0,
),
SizedBox(
height: 20.0,
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Cart Total",
style: TextStyle(
color: Color(0xFF9BA7C6),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
Text(
"8000",
style: TextStyle(
color: Color(0xFF6C6D6D),
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
],
),
SizedBox(
height: 20.0,
),
GestureDetector(
onTap: () {
// Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => SignInPage()));
},
child: Container(
height: 50.0,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(35.0),
),
child: Center(
child: Text(
"Proceed To Checkout",
style: TextStyle(
color: Colors.white,
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
),
),
),
SizedBox(
height: 20.0,
),
],
),
);
}
}
class CartListView extends StatefulWidget {
VoidCallback callback;
CartListView(this.callback);
#override
_CartListViewState createState() => _CartListViewState();
}
class _CartListViewState extends State<CartListView> {
int _counter = 1;
int getPrice(int i, int priceC) {
cartConstant.packageCount = i * priceC;
return cartConstant.packageCount;
}
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 15.0),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFD3D3D3), width: 2.0),
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 10.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
InkWell(
onTap: () {
setState(() {
_counter++;
if (_counter > 20) {
_counter = 20;
}
});
widget.callback();
},
child: Icon(Icons.add, color: Color(0xFFD3D3D3))),
Text(
"$_counter",
style: TextStyle(fontSize: 18.0, color: Colors.grey),
),
InkWell(
onTap: () {
setState(() {
_counter--;
if (_counter < 2) {
_counter = 1;
}
});
widget.callback();
},
child: Icon(Icons.remove, color: Color(0xFFD3D3D3))),
],
),
),
),
SizedBox(
width: 20.0,
),
Container(
height: 70.0,
width: 70.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/food.jpg"),
fit: BoxFit.cover),
borderRadius: BorderRadius.circular(35.0),
boxShadow: [
BoxShadow(
color: Colors.black54,
blurRadius: 5.0,
offset: Offset(0.0, 2.0))
]),
),
SizedBox(
width: 20.0,
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Employee Package",
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
SizedBox(height: 5.0),
SizedBox(height: 5.0),
Container(
height: 25.0,
width: 120.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Row(
children: <Widget>[
Text("Price",
style: TextStyle(
color: Color(0xFFD3D3D3),
fontWeight: FontWeight.bold)),
SizedBox(
width: 5.0,
),
InkWell(
onTap: () {},
child: Text(
getPrice(_counter, 2000).toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
),
SizedBox(
width: 10.0,
),
],
),
],
),
),
],
),
Spacer(),
GestureDetector(
onTap: () {},
child: Icon(
Icons.cancel,
color: Colors.grey,
),
),
],
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: OrderPage(),
);
}
}
When the button is tapped, you just need to update the Subtotal amount and cart total.
Step 1 : Replace the harcoded value by a variable. So, something like :
Row(
children: <Widget>[
Text(
"Subtotal",
style: ...
),
Text(
"$subTotal",
style: ....
),
],
),
Step 2 : Now, add a code to calcualte subTotal and cartTotal. You already have code which increments and decrements the variable _counter. You just need to tweak that part like :
setState( (){
_counter++;
// So, the quantity is increased. Just get the price of current item and add in the sub total.
subTotal += priceC;
cartTotal = subTotal - discount;
} );