Related
I have a code that is responsible for filtering by certain categories (I shortened it for ease of reading). When opening the filter window, the user sees these category names ('Select a brand', 'Select a operation system', 'Select a color' etc).
Next, the user can open the category (initially, the dropdown list is in the closed position.), and select the parameters from the drop-down list (and click the apply button). The next time you open the filter window, the checkboxes in front of the parameters remain, but the drop-down list collapses.
Tell me how to do it: if in any category there are options marked with a checkmark, so that the drop-down list will be open the next time the window with filters is opened.
class FilterDialog extends StatefulWidget {
final void Function(Map<String, List<String>?>) onApplyFilters;
final Map<String, List<String>?> initialState;
const FilterDialog({
Key? key,
required this.onApplyFilters,
this.initialState = const {},
}) : super(key: key);
#override
State<FilterDialog> createState() => _FilterDialogState();
}
class _FilterDialogState extends State<FilterDialog> {
// Temporary storage of filters.
Map<String, List<String>?> filters = {};
bool needRefresh = false;
// Variable for the ability to hide all elements of filtering by any parameter.
bool isClickedBrand = false;
List manufacturer = [];
#override
void initState() {
super.initState();
filters = widget.initialState;
}
// A function to be able to select an element to filter.
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key] ?? [];
if (checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
setState(() {
filters[key] = currentFilters;
});
}
// Building a dialog box with filters.
#override
Widget build(BuildContext context) {
return SimpleDialog(
// Window title.
title: const Text('Filters',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w600,
)),
contentPadding: const EdgeInsets.all(16),
// Defining parameters for filtering.
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Here and in subsequent Column, there will be a definition of parameters for filtering,
// a title, the ability to hide/show the items of list
Column(children: [
InkWell(
onTap: () async {
manufacturer = await getManufacturerOptions();
setState(() {
isClickedBrand = !isClickedBrand;
});
},
child: Row(children: [
Text('Select a brand'.toString(),
style: const TextStyle(
fontSize: 18,
)),
const Spacer(),
isClickedBrand
? const Icon(Icons.arrow_circle_up)
: const Icon(Icons.arrow_circle_down)
])),
!isClickedBrand
? Container()
: Column(
children: manufacturer
.map(
(el) => CustomCheckboxTile(
value: filters['manufacturer']?.contains(el) ??
false,
label: el,
onChange: (check) =>
_handleCheckFilter(check, 'manufacturer', el),
),
)
.toList())
]),
const SizedBox(
height: 5,
),
// Building a button to apply parameters.
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
widget.onApplyFilters(filters);
needRefresh = true;
},
child:
const Text('APPLY', style: TextStyle(color: Colors.black)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.grey),
)),
// Building a button to reset parameters.
const SizedBox(
height: 5,
),
ElevatedButton(
onPressed: () async {
setState(() {
filters.clear();
});
widget.onApplyFilters(filters);
},
child: const Text('RESET FILTERS',
style: TextStyle(color: Colors.black)),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.grey),
)),
],
),
],
);
}
}
For example: the user clicks on the filter box, selects the brands to search for, and clicks the apply button. My task is that the next time the user opens the filter window, the categories with active checkboxes (in this example, the brand) are in an expanded state
The concept is, you need to check filter data with while opening dialog, To simplify the process I am using ExpansionTile. You can check this demo and customize the behavior and look.
Run on dartPad, Click fab to open dialog and touch outside the dialog to close this.
class ExTExpample extends StatefulWidget {
ExTExpample({Key? key}) : super(key: key);
#override
State<ExTExpample> createState() => _ExTExpampleState();
}
class _ExTExpampleState extends State<ExTExpample> {
// you can use map or model class or both,
List<String> filter_data = [];
List<String> brands = ["Apple", "SamSung"];
List<String> os = ["iOS", "Android"];
_showFilter() async {
await showDialog(
context: context,
builder: (c) {
// you can replace [AlertDialog]
return AlertDialog(
content: StatefulBuilder(
builder: (context, setSBState) => SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ExpansionTile(
title: const Text("Brand"),
/// check any of its's item is checked or not
initiallyExpanded: () {
// you can do different aproach
for (final f in brands) {
if (filter_data.contains(f)) return true;
}
return false;
}(),
children: [
...brands.map(
(brandName) => CheckboxListTile(
value: filter_data.contains(brandName),
title: Text(brandName),
onChanged: (v) {
if (filter_data.contains(brandName)) {
filter_data.remove(brandName);
} else {
filter_data.add(brandName);
}
setSBState(() {});
//you need to reflect the main ui, also call `setState((){})`
},
),
),
],
),
ExpansionTile(
title: const Text("select OS"),
/// check any of its's item is checked or not
initiallyExpanded: () {
// you can do different aproach
for (final f in os) {
if (filter_data.contains(f)) return true;
}
return false;
}(),
children: [
...os.map(
(osName) => CheckboxListTile(
value: filter_data.contains(osName),
title: Text(osName),
onChanged: (v) {
if (filter_data.contains(osName)) {
filter_data.remove(osName);
} else {
filter_data.add(osName);
}
setSBState(() {});
//you need to reflect the main ui, also call `setState((){})`
},
),
),
],
),
],
),
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FloatingActionButton(
onPressed: () {
_showFilter();
},
),
),
);
}
}
Whenever you open filter the isClickedBrand is False so it won't showed you a list. So the solution is :
After selecting option from list, change the state of isClickedBrand state. I mean if it's true then it will show the list otherwise show container.
Hope you get my point.
I am Developing Cart Page for e com application. backend is firebase real time database. In cart Page Price Details is calculating when the page load. But when user change the quantity of cart item, It is going to update the cart. But the problem is Cant Calculate the Price Details On this time. When I reopen the page, Price Details is Calculating According to new Cart Quantity. But How can I can do that without re opening the Page
1.This is my cart_page.dart file
//this cart is show when user select my cart From the navigation drawer
import 'package:flutter/material.dart';
import 'package:buy_app_flutter/main.dart';
import 'package:buy_app_flutter/pages/notification_page.dart';
import 'package:buy_app_flutter/pages/search_page.dart';
import 'package:buy_app_flutter/componets/cart_products.dart';
import 'BuyNowDeliveryDetailsPage.dart';
import 'HomePage.dart';
class CartPage extends StatefulWidget {
#override
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: new DrawerCodeOnly(),
appBar: new AppBar(
elevation: 0.1,
backgroundColor: Colors.orangeAccent,
title: Text("My Cart"),
actions:<Widget> [
new IconButton(icon: Icon(Icons.search,color: Colors.white,), onPressed: (){
showSearch(context: context, delegate: DataSearch());
} ),
new IconButton(icon: Icon(Icons.notifications,color: Colors.white,), onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => new NotificationPage() ));
} ),
],
),
body:new Cart_products(),
bottomNavigationBar:new Container(
color: Colors.white,
child: Row(
children:<Widget> [
Expanded(child: ListTile(
title: new Text("Total:"),
subtitle:new Text("Rs 00.00") ,
)),
Expanded(
child: new MaterialButton(onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => new BuyNowDeliveryDetailsActivity() ));
},
child: new Text("Check Out"),
color: Colors.orangeAccent,
)
)
],
),
) ,
);
}
}
2.This is cart_procucts.dart file
import 'package:buy_app_flutter/DBModels/CartModel.dart';
import 'package:buy_app_flutter/DBModels/ProductModel.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
class Cart_products extends StatefulWidget {
#override
_Cart_productsState createState() => _Cart_productsState();
}
class _Cart_productsState extends State<Cart_products> {
//firebase database reference to cart===============================
final Cartref= FirebaseDatabase.instance.reference().child("UserData").child(FirebaseAuth.instance.currentUser.uid).child("Cart");
List<CartModel> Products_on_the_cart=List();
///=============================================firebase end
bool _progressController=true;
//variables to cal total
int TotalItemPrice=0;
int SavedAmount=0;
int DeliveryPrice=0;
int TotalAmount=0;
#override
initState(){
_getCartList(); //call function to get cart from firebase
super.initState();
}
///display the cart Items one by one===============================
#override
Widget build(BuildContext context) {
if(_progressController==true) //reading database
{
return Center(child: new CircularProgressIndicator());
}
else{ //db read end=========
if(Products_on_the_cart.isNotEmpty)
{
_calTotalPrice();
//===============================================if not empty the cart
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
itemBuilder: (context, index){
return Single_cart_product(
cart_product_key: Products_on_the_cart[index].productKey,
cart_product_name: Products_on_the_cart[index].ProductTitle,
cart_prod_picture: Products_on_the_cart[index].Img,
cart_prod_price: Products_on_the_cart[index].Price,
cart_prod_cutted_price: Products_on_the_cart[index].CuttedPrice,
cart_prod_qty: Products_on_the_cart[index].qty,
cart_product_status: Products_on_the_cart[index].Status,
);
},
//end of item builder
separatorBuilder:(context,index){
return Row();
},
itemCount: Products_on_the_cart.length,
shrinkWrap: true,
),
Divider(),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text("PRICE DETAILS",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.grey),),
),
Divider(),
///start of items total price row===========================
Padding(
padding: const EdgeInsets.only(left: 8.0,right: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("price (${Products_on_the_cart.length} items)",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.black),),
Text("Rs ${TotalItemPrice}/-",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.black),),
],
),
),
///end of items total price row============================
///start of delivery price row==================================
Padding(
padding: const EdgeInsets.only(left: 8.0,top: 8.0,right: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Delivery",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.black),),
Text("Rs ${DeliveryPrice}/-",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.green),),
],
),
),
///end of delivery price row============================
Divider(),
///Start of total amount row==================================
Padding(
padding: const EdgeInsets.only(left: 8.0,right: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Total Amount",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.black),),
Text("Rs ${TotalAmount}/-",style: TextStyle(fontWeight: FontWeight.bold,color: Colors.black),),
],
),
),
///end of total amount row===========================================
Divider(),
Padding(
padding: const EdgeInsets.only(left: 8.0,right: 8.0),
child: Text("You saved Rs ${SavedAmount}/- on this order",style: TextStyle(fontWeight: FontWeight.normal,color: Colors.green),),
),
],
);
//end of if not empty the cart===========================================
}
else
{
return Center(child: Text("Your cart is empty !"));
}
}
}
///end of displaying cart items one by one====================================
_getCartList()
async {
//get cart data from firebase==========
try{
await Cartref.once().then((DataSnapshot snapshot) {
var data = snapshot.value;
if(data==null) //if the no cart items found hide progress
{
setState(() {
_progressController=false;
});
}
Products_on_the_cart.clear();
data.forEach((key,value)
async {
CartModel model = new CartModel(
date: value["date"],
time: value["time"],
productKey: value["productKey"],
qty: value["qty"],
);
//use Pref(product ref) to get details from products record
try{
//Product details ref and list
final Pref= FirebaseDatabase.instance.reference().child("Product");
List<ProductModel> product =List();
//====================================
await Pref.child(model.productKey).once().then((DataSnapshot snap) {
// print('Product : ${snap.value}');
model.ProductTitle=snap.value["ProductTitle"];
model.Img=snap.value["img1"];
model.Price=snap.value["Price"];
model.DPrice=snap.value["DPrice"];
model.CuttedPrice=snap.value["CuttedPrice"];
model.Status=snap.value["Status"];
});
}
catch(e)
{
print("Error In try block for get data from real time db ");
print(e);
}
finally
{
setState(() {
_progressController=false;
});
}
//========================================================end of get product details
setState(() {
Products_on_the_cart.add(model);
});
}
);
});
}
catch(e)
{
print(e);
}
// end of get cart data from firebase
}
///function for calculate total price
_calTotalPrice(){
TotalItemPrice=0;
DeliveryPrice=0;
TotalAmount=0;
SavedAmount=0;
Products_on_the_cart.forEach((item){
//set total item price===========
TotalItemPrice=TotalItemPrice+int.parse(item.Price)*int.parse(item.qty);
//set delivery price==========
if (item.DPrice=="FREE")
{
//nothing
}
else
{
if (int.parse(item.DPrice)>DeliveryPrice)
{
DeliveryPrice=int.parse(item.DPrice);
}
}
//set Total amount=======
TotalAmount=TotalItemPrice+DeliveryPrice;
//set saved amount
SavedAmount=SavedAmount + (int.parse(item.CuttedPrice)-int.parse(item.Price))*int.parse(item.qty);
});
}
/// end of calculate total price function
}
///from here design the cart item display ==========================================
class Single_cart_product extends StatefulWidget {
final cart_product_key;
final cart_product_name;
final cart_prod_picture;
final cart_prod_price;
final cart_prod_cutted_price;
String cart_prod_qty;
final cart_product_status;
Single_cart_product({
this.cart_product_key,
this.cart_product_name,
this.cart_prod_picture,
this.cart_prod_price,
this.cart_prod_cutted_price,
this.cart_prod_qty,
this.cart_product_status,
});
#override
_Single_cart_productState createState() => _Single_cart_productState();
}
class _Single_cart_productState extends State<Single_cart_product> {
///==========================start of Cart Item Display Design==================================================================
#override
Widget build(BuildContext context) {
var cartQty=int.parse(widget.cart_prod_qty);
return Card(
child: Column(
children: [
ListTile(
///===============START LEADING SECTION ===============================
leading: FadeInImage.assetNetwork(
placeholder: 'assets/close_box.png',
image:widget.cart_prod_picture,
height: 80.0,
width: 80.0,
fit: BoxFit.contain,
),
///=======END OF LEADING SECTION=======================================
/// =========== TITLE SECTION =========================================
title: new Text(widget.cart_product_name),
///====END OF TITLE SECTION============================================
///========== SUBTITLE SECTION ==================================
subtitle: new Column(
children:<Widget> [
//Row inside the column
new Row(
children: <Widget> [
// =============this section is for the Product price =====
new Container(
alignment: Alignment.topLeft,
child: new Text("\Rs.${widget.cart_prod_price}",
style: TextStyle(
fontSize:16.0,
fontWeight: FontWeight.bold,
color: Colors.black
),
),
),
SizedBox(width: 4,),
//==========this section is for the cutted price of the product=========
Padding(
padding: const EdgeInsets.all(5.0),
child: new Text("\Rs.${widget.cart_prod_cutted_price}",
style: TextStyle(
fontSize:16.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.lineThrough,
color: Colors.grey
),
),
),
//end of cutted price of the product
],
),
],
),
///=====END OF SUBTITLE SECTION============================================
///======START OF TRAILING SECTION==========================================
trailing:
Column(
children:
<Widget>[
Expanded(child: new IconButton(
padding: const EdgeInsets.all(0.0),
icon: Icon(Icons.arrow_drop_up), onPressed: (){
cartQty++;
print(cartQty);
_updateCartQty(widget.cart_product_key,cartQty.toString()); //call to update cart qty
setState(() {
widget.cart_prod_qty=cartQty.toString();
});
})),
Padding(
padding: const EdgeInsets.all(0.0),
child: new Text(cartQty.toString()),
),
Expanded(child: new IconButton(
padding: const EdgeInsets.all(0.0),
icon: Icon(Icons.arrow_drop_down), onPressed: (){
if(cartQty>1)
{
cartQty--;
print(cartQty);
_updateCartQty(widget.cart_product_key,cartQty.toString()); //call to update cart qty
setState(() {
widget.cart_prod_qty=cartQty.toString();
});
}
})),
],
),
///==END OF TRAILING======================================================================
),
//REMOVE BTN AND STATUS
Row(
children: [
new IconButton(
icon: Icon(Icons.delete),
color:Colors.red ,
onPressed: (){}),
Padding(
padding: const EdgeInsets.all(4.0),
child: new Text(widget.cart_product_status,style: TextStyle(color: Colors.orangeAccent)),
),
],
)
//END OF REMOVE BTN SND STATUS
],
),
);
}
///==========================end of Cart Item Display Design==================================================================
/// function for update cart qty
_updateCartQty(String Pkey,String Qty){
try {
final Cartref = FirebaseDatabase.instance.reference()
.child("UserData")
.child(FirebaseAuth.instance.currentUser.uid)
.child("Cart");
Cartref.child(Pkey).child("qty").set(Qty);
}
catch(Ex)
{
Fluttertoast.showToast(
msg: Ex.message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
}
}
}
3.After that I need to view the calculated total amount in Bottom of the cart page
Updating product quantity value in Cart_products class:
You need to create a variable final ValueChanged<int> onProdQty; in Single_cart_product class and then where the quantity value changes use the callback like this widget.onProdQty?.call(here Pass the new quantity value). Also one last step is that in class Cart_products where you are calling Single_cart_product() add this in argument onProdQty: (value) => setState(() { Products_on_the_cart[index].qty = value; }),. By doing this whenever the callback is called the setstate would run in Cart_products class and wherever Products_on_the_cart[index].qty is used it would get updated.
Updating product quantity value in CartPage class:
Note: I'm not sure if these steps would work corectly as I have limited information about your project. But you will surely learn how to pass data around using callbacks:
Follow steps of Updating product quantity value in Cart_products
class and then continue.
Add final ValueChanged<int> onProdQty; in Cart_products class and
then change Single_cart_product() argument in class Cart_products
like this:
Single_cart_product(onProdQty: (value) => setState((){
Products_on_the_cart[index].qty = value;
widget.onProdQty?.call(value); //add this
}),)
Then add List<CartModel> Products_on_the_cart=List(); in the
_CartPageState class in cart_page.dart and at last in the body: Cart_products() make these changes:
Cart_products(onProdQty: (value) => setState((){
Products_on_the_cart[index].qty = value;
}),)
Now use Products_on_the_cart[index].qty in bottomNavigationBar.
Please let me know if you have any further queries.
I have a Stateful widget that has a child buttons and set of stateful containers (visible and invisible).
I want to try here is when i call the specific button, the specific button will refresh all layout and change it on its specifict view by setting visible while the others are not visible.
like this:
button1 = view1;
button2 = view2;
button3 = view3;
if (button1 is pressed){
view1 is visible}
else{
not visible}
upon my code, upon my first view(login button),I've set to go with my main_page like this:
child: MaterialButton(
minWidth: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MainPage(change1: true,change2: false,change3: false,)),
);
},
now i was display my main_page view (with a child view that has visibility property).
this is my code on main_page:
class MainPage extends StatefulWidget {
final bool change1 ;
final bool change2;
final bool change3 ;
const MainPage({Key key, this.change1,this.change2,this.change3}) : super(key: key);
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Container(
constraints: BoxConstraints.expand(
height: 280.0,
),
decoration: BoxDecoration(
image: new DecorationImage(
fit: BoxFit.cover,
colorFilter: new ColorFilter.mode(
Colors.blueAccent, BlendMode.colorBurn),
image: new ExactAssetImage("images/vector.jpg"),
),
),
child: Stack(
children: <Widget>[
Container(
alignment: Alignment.bottomCenter,
child: Row(
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
print("i pressed Official Business");
MainPage(change1: true,change2: false,change3: false);
//TODO: here is my problem, when i call the main_page on its page,
// the value of change1, change2, and chaange3 is not updating
// so that icanot update my view .
});
},
child: Container(
height: 50.0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: Text(
"Official Business",
style: TextStyle(
fontSize: 20.0, color: Colors.white),
),
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
print("i pressed file an OB");
MainPage(change1: false,change2: true,change3: false);
//TODO: here is my problem, when i call the main_page on its page,
// the value of change1, change2, and chaange3 is not updating
// so that icanot update my view .
});
},
child: Container(
height: 50.0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: Text(
"File an OB",
style: TextStyle(
fontSize: 20.0, color: Colors.white),
),
),
),
),
),
),
],
),
),
],
),
),
//TODO:
new Visibility(
//Called changed and viewOne
visible: widget.change1,
child: OfficialBusiness(),
),
new Visibility(
//Called not changed and viewTwo
visible: widget.change2,
child: FilingOb(),
),
new Visibility(
//Called not changed and viewTwo
visible: widget.change3,
child: ViewOfficialBusiness(),
)
],
),
),
);
}
}
the fillingob/officialbusiness/ViewOfficialBusiness includded was set of stateful layout, i didn't add the code to prevent over views.
sorry, in new on this programming language and i've like to enlighten with these problems i've encountered if my code is possible or not.Also if you need more reference just ping on comment so that i can provide my other codes
bool change1;
bool change2;
bool change3;
#override
initState() {
super.initState();
change1 = widget.change1;
change2 = widget.change2;
change3 = widget.change3;
}
void setChange1() {
setState(() {
change1 = true;
change2 = change3 = false;
});
}
// GestureDetector(onTap: setChange1)
// Visibility(
// visible: change1,
// child: OfficialBusiness(),
// )
Or use enum :
enum MyView {
officialBusiness,
filingOb,
viewOfficialBusiness,
}
MyView current;
// GestureDetector(onTap: () => setState(() { current = MyView.officialBusiness; }))
// Visibility(
// visible: current == MyView.officialBusiness,
// child: OfficialBusiness(),
// )
In Flutter, the standard way to call functions on a parent widget when something occurs in one of its children would be to pass a function from the parent widget to the child, so that the function gets triggered from the child, with data that only the parent widget knows.
In your case, in your first view, you could define a new method like this:
void onChildPressed() {
setState(() {
// Here you change the boolean change1, change2, whatever you want
});
}
And then, in the child view, you have to define a function parameter, such that your child can receive the function as a parameter, and trigger it from wherever you want in your child widget.
class MainPage extends StatefulWidget {
final bool change1 ;
final bool change2;
final bool change3 ;
final void Function() onPressed;
const MainPage({Key key, this.change1,this.change2,this.change3, this.onPressed}) : super(key: key);
#override
_MainPageState createState() => _MainPageState();
}
This way, you can instantiate the MainPage from your first view like this:
MainPage(change1: false,change2: true,change3: false, onPressed: onChildPressed);
And, finally, you can call the onPressed function in your MainPage, so that your child view updates the parent view the way you want.
I am trying to add a button on an image on tap position.
I have managed to get position on tap by using onTapUp's detail parameter.
However, I can't add an icon button when user taps on the image.
Below code shows my sample.
class ArticlesShowcase extends StatelessWidget {
final commentWidgets = List<Widget>();
#override
Widget build(BuildContext context) {
return new GestureDetector(
child: new Center(
child: Image.network(
'https://via.placeholder.com/300x500',
),
),
onTapUp: (detail) {
final snackBar = SnackBar(
content: Text(detail.globalPosition.dx.toString() +
" " +
detail.globalPosition.dy.toString()));
Scaffold.of(context).showSnackBar(snackBar);
new Offset(detail.globalPosition.dx, detail.globalPosition.dy);
var btn = new RaisedButton(
onPressed: () => {},
color: Colors.purple,
child: new Text(
"Book",
style: new TextStyle(color: Colors.white),
),
);
commentWidgets.add(btn);
},
);
}
}
I tried to add the button on the list but no chance.
So , there are a couple of things you are missing.
First you can't update a StatelessWidget state , so you need to use a StatefulWidget.
Second, when using a StatefulWidget , you need to call setState to update the State.
You will also need to use a Stack and Positioned widgets to put the buttons on your specific location.
Your code should end and look like this.
class ArticlesShowcaseState extends State<ArticlesShowcase> {
final commentWidgets = List<Widget>();
void addButton(detail) {
{
final snackBar = SnackBar(
content: Text(
"${detail.globalPosition.dx.toString()} ${detail.globalPosition.dy.toString()}"));
Scaffold.of(context).showSnackBar(snackBar);
var btn = new Positioned(
left: detail.globalPosition.dx,
top: detail.globalPosition.dy,
child: RaisedButton(
onPressed: () => {},
color: Colors.purple,
child: new Text(
"Book",
style: new TextStyle(color: Colors.white),
),
));
setState(() {
commentWidgets.add(btn);
});
}
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
GestureDetector(
child: new Center(
child: Image.network(
'https://via.placeholder.com/300x500',
),
),
onTapUp: (detail) => addButton(detail),
)
] +
commentWidgets,
);
}
}
I'm looking for a search bar in flutter docs but can't find it, is there a widget for the search bar with autocomplete in appbar. For example, I have a search icon on my appbar. When one press it show's the search box, when you type it should show autocomplete from the dropdown with listtile. I managed to implement this but it's not easy to use because I need a dropdown to show suggestion autocomplete, then use the suggestion for a new route if selected.
Here the search action
You can use Stack to achieve the autocomplete dropdown box effect. Example below has 2 Containers - both hold ListView as child objects. One holds search results, other has some random text as content for the body. ListView (search result) is placed inside an Align Object and alignment property is set to Alignment.topCenter. This ensures that List appears at the top, just below the AppBar.
Updated the Post (accepted answer) mentioned in the comments for a complete a demo.
As explained above:
#override
Widget build(BuildContext context) {
return new Scaffold(
key: key,
appBar: buildBar(context),
body: new Stack(
children: <Widget>[
new Container(
height: 300.0,
padding: EdgeInsets.all(10.0),
child: new DefaultTabController(length: 5, child: mainTabView),
),
displaySearchResults(),
],
));
}
Widget displaySearchResults() {
if (_IsSearching) {
return new Align(
alignment: Alignment.topCenter,
//heightFactor: 0.0,
child: searchList());
} else {
return new Align(alignment: Alignment.topCenter, child: new Container());
}
}
Complete demo
class SearchList extends StatefulWidget {
SearchList({Key key, this.name}) : super(key: key);
final String name;
#override
_SearchListState createState() => new _SearchListState();
}
class _SearchListState extends State<SearchList> {
Widget appBarTitle = new Text(
"",
style: new TextStyle(color: Colors.white),
);
Icon actionIcon = new Icon(
Icons.search,
color: Colors.white,
);
final key = new GlobalKey<ScaffoldState>();
final TextEditingController _searchQuery = new TextEditingController();
List<SearchResult> _list;
bool _IsSearching;
String _searchText = "";
String selectedSearchValue = "";
_SearchListState() {
_searchQuery.addListener(() {
if (_searchQuery.text.isEmpty) {
setState(() {
_IsSearching = false;
_searchText = "";
});
} else {
setState(() {
_IsSearching = true;
_searchText = _searchQuery.text;
});
}
});
}
#override
void initState() {
super.initState();
_IsSearching = false;
createSearchResultList();
}
void createSearchResultList() {
_list = <SearchResult>[
new SearchResult(name: 'Google'),
new SearchResult(name: 'IOS'),
new SearchResult(name: 'IOS2'),
new SearchResult(name: 'Android'),
new SearchResult(name: 'Dart'),
new SearchResult(name: 'Flutter'),
new SearchResult(name: 'Python'),
new SearchResult(name: 'React'),
new SearchResult(name: 'Xamarin'),
new SearchResult(name: 'Kotlin'),
new SearchResult(name: 'Java'),
new SearchResult(name: 'RxAndroid'),
];
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: key,
appBar: buildBar(context),
body: new Stack(
children: <Widget>[
new Container(
height: 300.0,
padding: EdgeInsets.all(10.0),
child: new Container(
child: ListView(
children: <Widget>[
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
],
),
),
),
displaySearchResults(),
],
));
}
Widget displaySearchResults() {
if (_IsSearching) {
return new Align(
alignment: Alignment.topCenter,
child: searchList());
} else {
return new Align(alignment: Alignment.topCenter, child: new Container());
}
}
ListView searchList() {
List<SearchResult> results = _buildSearchList();
return ListView.builder(
itemCount: _buildSearchList().isEmpty == null ? 0 : results.length,
itemBuilder: (context, int index) {
return Container(
decoration: new BoxDecoration(
color: Colors.grey[100],
border: new Border(
bottom: new BorderSide(
color: Colors.grey,
width: 0.5
)
)
),
child: ListTile(
onTap: (){},
title: Text(results.elementAt(index).name,
style: new TextStyle(fontSize: 18.0)),
),
);
},
);
}
List<SearchResult> _buildList() {
return _list.map((result) => new SearchResult(name: result.name)).toList();
}
List<SearchResult> _buildSearchList() {
if (_searchText.isEmpty) {
return _list.map((result) => new SearchResult(name: result.name)).toList();
} else {
List<SearchResult> _searchList = List();
for (int i = 0; i < _list.length; i++) {
SearchResult result = _list.elementAt(i);
if ((result.name).toLowerCase().contains(_searchText.toLowerCase())) {
_searchList.add(result);
}
}
return _searchList
.map((result) => new SearchResult(name: result.name))
.toList();
}
}
Widget buildBar(BuildContext context) {
return new AppBar(
centerTitle: true,
title: appBarTitle,
actions: <Widget>[
new IconButton(
icon: actionIcon,
onPressed: () {
_displayTextField();
},
),
// new IconButton(icon: new Icon(Icons.more), onPressed: _IsSearching ? _showDialog(context, _buildSearchList()) : _showDialog(context,_buildList()))
],
);
}
String selectedPopupRoute = "My Home";
final List<String> popupRoutes = <String>[
"My Home",
"Favorite Room 1",
"Favorite Room 2"
];
void _displayTextField() {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon = new Icon(
Icons.close,
color: Colors.white,
);
this.appBarTitle = new TextField(
autofocus: true,
controller: _searchQuery,
style: new TextStyle(
color: Colors.white,
),
);
_handleSearchStart();
} else {
_handleSearchEnd();
}
});
}
void _handleSearchStart() {
setState(() {
_IsSearching = true;
});
}
void _handleSearchEnd() {
setState(() {
this.actionIcon = new Icon(
Icons.search,
color: Colors.white,
);
this.appBarTitle = new Text(
"",
style: new TextStyle(color: Colors.white),
);
_IsSearching = false;
_searchQuery.clear();
});
}
}
It is actually very simple. you can refer above answers for details. Let's follow these steps:
Create a list of items we want to have in the autofill menu, lets name it autoList
Create one more emptyList named filteredList
Add all the values of autoList to filterList
void initState() {
filteredList.addAll(autoList);
}
Create a custom search bar widget with a TextField in it
we will be getting a 'value' i.e. the text entered from this Textfield: eg. TextFiled(onchange(value){})
Assuming that we have strings in our autoList, write:
filteredList.removeWhere((i) => i.contains(value.toString())==false);
The complete TextField widget will look like:
TextField(
onChanged: (value) {
setState(() {
filteredList.clear(); //for the next time that we search we want the list to be unfilterted
filteredList.addAll(autoList); //getting list to original state
//removing items that do not contain the entered Text
filteredList.removeWhere((i) => i.contains(value.toString())==false);
//following is just a bool parameter to keep track of lists
searched=!searched;
});
},
controller: editingController,
decoration: InputDecoration(
border: InputBorder.none,
labelText: "Search for the filtered list",
prefixIcon: Icon(Icons.search),
),
),
Now, along the search bar, we just have to display filteredList with ListViewBuilder. done :)
this plugin will helpful for you,loader_search_bar
Flutter widget integrating search field feature into app bar, allowing to receive query change callbacks and automatically load new data set into ListView. It replaces standard AppBar widget and needs to be placed underneath Scaffold element in the widget tree to work properly.
Getting started
To start using SearchBar insert it in place of an AppBar element in the Scaffold widget. Regardless of the use case, defaultBar named argument has to be specified, which basically is a widget that will be displayed whenever SearchBar is not in activated state:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: SearchBar(
defaultBar: AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: _openDrawer,
),
title: Text('Default app bar title'),
),
...
),
body: _body,
drawer: _drawer,
);
}
Optional attributes
searchHint - hint string being displayed until user inputs any text,
initialQuery - query value displayed for the first time in search field,
iconified - boolean value indicating way of representing non-activated SearchBar:
true if widget should be showed as an action item in defaultBar,
false if widget should be merged with defaultBar (only leading icon of the default widget and search input field are displayed in such case),
autofocus - boolean value determining if search text field should get focus whenever it becomes visible,
autoActive - ,
attrs - SearchBarAttrs class instance allowing to specify part of exact values used during widget building (e.g. search bar colors, text size, border radius),
controller - SearchBarController object that provides a way of interacing with current state of the widget,
searchItem - defining how to build and position search item widget in app bar,
overlayStyle - status bar overlay brightness applied when widget is activated.
Query callbacks
To get notified about user input specify onQueryChanged and/or onQuerySubmitted callback functions that receive current query string as an argument:
appBar: SearchBar(
...
onQueryChanged: (query) => _handleQueryChanged(context, query),
onQuerySubmitted: (query) => _handleQuerySubmitted(context, query),
),
QuerySetLoader
By passing QuerySetLoader object as an argument one can additionally benefit from search results being automatically built as ListView widget whenever search query changes:
appBar: SearchBar(
...
loader: QuerySetLoader<Item>(
querySetCall: _getItemListForQuery,
itemBuilder: _buildItemWidget,
loadOnEachChange: true,
animateChanges: true,
),
),
List<Item> _getItemListForQuery(String query) { ... }
Widget _buildItemWidget(Item item) { ... }
querySetCall - function transforming search query into list of items being then rendered in ListView (required),
itemBuilder - function creating Widget object for received item, called during ListView building for each element of the results set (required),
loadOnEachChange - boolean value indicating whether querySetCall should be triggered on each query change; if false query set is loaded once user submits query,
animateChanges - determines whether ListView's insert and remove operations should be animated.
SearchItem
Specifying this parameter allows to customize how search item should be built and positioned in app bar. It can be either action or menu widget. No matter which of these two is picked, two constructor arguments can be passed:
builder - function receiving current BuildContext and returning Widget for action or PopupMenuItem for menu item,
gravity - can be one of SearchItemGravity values: start, end or exactly. If no arguments are passed, SearchBar will create default item which is search action icon with start gravity.
SearchItem.action
appBar: SearchBar(
// ...
searchItem: SearchItem.action(
builder: (_) => Padding(
padding: EdgeInsets.all(12.0),
child: Icon(
Icons.find_in_page,
color: Colors.indigoAccent,
),
),
gravity: SearchItemGravity.exactly(1),
),
)
SearchItem.menu
appBar: SearchBar(
// ...
searchItem: SearchItem.menu(
builder: (_) => PopupMenuItem(
child: Text("Search 🔍"),
value: "search",
),
gravity: SearchItemGravity.end,
),
)
Also, bear in mind that SearchBar will prevent built item widget from receiving tap events and will begin search action rather than that.
hope it will help you.
import 'package:flutter/material.dart';
class SearchText extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Searchable Text"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
showSearch(
context: context,
delegate: DataSearch(),
);
})
],
),
drawer: Drawer(),
);
}
}
class DataSearch extends SearchDelegate<String> {
final cities = ['Ankara', 'İzmir', 'İstanbul', 'Samsun', 'Sakarya'];
var recentCities = ['Ankara'];
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = "";
})
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
close(context, null);
});
}
#override
Widget buildResults(BuildContext context) {
return Center(
child: Container(
width: 100,
height: 100,
child: Card(
color: Colors.red,
child: Center(child: Text(query)),
),
),
);
}
#override
Widget buildSuggestions(BuildContext context) {
final suggestionList = query.isEmpty
? recentCities
: cities.where((p) => p.startsWith(query)).toList();
return ListView.builder(
itemBuilder: (context, index) => ListTile(
onTap: () {
showResults(context);
},
leading: Icon(Icons.location_city),
title: RichText(
text: TextSpan(
text: suggestionList[index].substring(0, query.length),
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
children: [
TextSpan(
text: suggestionList[index].substring(query.length),
),
],
),
),
),
itemCount: suggestionList.length,
);
}
}