I put the FormBuilderDropdown inside FutureBuilder so when I press on it will rebuild infinitely and will not enter the FormBuilderDropdown if anyone can give me a proper solution
and this is my code
FutureBuilder(
future: widget.fetchBrands(yearValue),
builder:
(BuildContext context, AsyncSnapshot snapshot) {
var snap = snapshot.data;
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
if (yearValue == null) {
return Container(
child: Text(
"Select the manufacture year to Select Brand",
style: TextStyle(
color: Colors.red[800],
fontSize: 16,
),
overflow: TextOverflow.visible,
),
);
}
return Center(
child: new Text('Error'),
);
}
snap.forEach((key, brand) {
brands.add(brand['make']);
});
brands = brands.toSet().toList();
brands.sort();
return
FormBuilderDropdown(
key: ValueKey("Brands"),
name: "Brands",
items: brands
.map(
(e) => DropdownMenuItem<String>(
child: Text(e),
value: e,
),
)
.toList(),
validator: FormBuilderValidators.compose(
[
FormBuilderValidators.required(
context,
)
],
),
decoration: InputDecoration(
labelText: 'Brands',
labelStyle:
Theme.of(context).textTheme.display1,
),
hint: Text('Select Brand'),
allowClear: true,
onChanged: (value) {
setState(() {
brandValue = value;
carValue = null;
carsName.clear();
});
},
);
},
),
before the new version of FormBuilder, this works well
I do temp solution that after I get the data I remove the future and build a new one without future but this solution not good enough because it takes two tap to open the dropdown
future: widget.fetchBrands(yearValue),
This means, every time build is called, you will start over and fetch the brands. You only want to fetch them once, yo you should only call the method once.
You want this line in initState where it runs only once:
brandsFuture = widget.fetchBrands(yearValue);
and then use that one future in your build method:
future: brandsFuture,
obviously you need to declare brandsFuture as a field in your state class.
Related
I am trying to use the SwitchTileList to show all my categories and toggle them on/off however it seems to either not change state/toggle or it will toggle all of them together.
At the moment the code below the showdefault items are on as should be and the rest are off, however it will not toggle any of them at the moment.
return FutureBuilder(
future: amenityCategories,
builder:
(BuildContext context, AsyncSnapshot<AmenityCategories> snapshot) {
if (snapshot.hasData) {
return ListView(
padding: EdgeInsets.zero,
children: [
SizedBox(
height: 85.0,
child: DrawerHeader(
child: Text(
'Show/Hide Map Pins',
style: new TextStyle(fontSize: 18.0, color: Colors.white),
),
decoration: const BoxDecoration(
color: Colors.green,
),
),
),
SizedBox(
height: double.maxFinite,
child: ListView.builder(
itemCount: snapshot.data!.categories.length,
itemBuilder: (context, index) {
bool toggle = false;
if (snapshot.data!.categories[index].showbydefault == 1) {
toggle = true;
}
return SwitchListTile(
title: Text(
snapshot.data!.categories[index].categoryname),
value: toggle,
onChanged: (bool val) {
if (val != toggle) {
setState(() {
toggle = !toggle;
});
}
});
},
),
),
],
);
}
return Container();
});
}
You must use a separate variable for each individual ListTile. Give your category an additional variable isActive and work with it.
onChanged: (bool val) {
if (val != snapshot.data!.categories[index].isActive) {
setState(() {
snapshot.data!.categories[index].isActive = !snapshot.data!.categories[index].isActive;
});
}
I have a Category TextField with a controller. The controller value is updated onChange of a Product Dropdown. What I expect is upon onChange the value of the categoryField should be updated. However, I can only see the update on the TextField once I hover on it.
Category TextField
var productCategory = Column(
children: [
TextField(
controller: categoryController,
enabled: false,
focusNode: categoryFocusNode,
),
const Padding(
padding: EdgeInsets.all(5.0), child: SizedBox(width: 200)),
],
);
Product Dropdown onChange
void onChange<Product>(prod) {
BlocProvider.of<InvoiceCubit>(context).updateProduct(prod);
categoryController.text = prod.category.categoryName.toString();
}
I have finally figured it out. Since I am using a rxdart stream, I created both the Product dropdown and Category on the StreamBuilder. Then I created the categoryController within the builder itself with text value from the product category. Below is my code:
var productDropdownField = StreamBuilder<Product>(
stream: BlocProvider.of<InvoiceCubit>(context).productStream,
builder: (context, snapshot) {
final categoryController = TextEditingController();
categoryController.text = snapshot.hasData
? snapshot.data!.category.categoryName
: "";
var productCategory = Column(
children: [
CustomTextField(
labelText: "Category",
controller: categoryController,
enabled: false,
),
const Padding(
padding: EdgeInsets.all(10.0),
child: SizedBox(width: 200)),
],
);
return StreamBuilder<Object>(
stream:
BlocProvider.of<InvoiceCubit>(context).priceStream,
builder: (context, snapshot) {
return Column(
children: [
BlocBuilder<ProductsCubit, ProductsState>(
builder: (context, state) {
if (state is ProductsLoaded) {
List<DropdownMenuItem<Product>>
dropDownItems = state.products
.map((e) => DropdownMenuItem<Product>(
value: e,
child: Text(
e.productName,
style: const TextStyle(
color: Colors.black,
fontWeight:
FontWeight.w900),
),
))
.toList();
if (invoiceItem == null &&
prodSelected == false) {
onChange<Product>(state.products.first);
prodSelected = true;
}
return CustomDropdown<Product>(
labelText: "Product",
value:
BlocProvider.of<InvoiceCubit>(context)
.getProduct(),
items: dropDownItems,
context: context,
onChanged: onChange,
);
}
return const SizedBox(
width: 100, child: Text("Error"));
},
),
productCategory,
],
);
});
});
categoryController won't be updated via onChange but will be updated based on the snapshot.data
I am getting a data from local database using floor in flutter.
And I want to display the list of doctors from the db I am using DropDownButton to do this
This is my code
DropdownButton(
icon: const Icon(
Icons.arrow_drop_down,
),
hint: const Text('Select a doctor'),
items: FutureBuilder<List<Doctor>>(
future: findAllDoctor(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return snapshot.data
.map((doctor) => DropdownMenuItem(
child: Text(doctor.name),
value: doctor,
))
.toList();
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
}
), ,
onChanged: (val) {
print(val);
},
),
And this is how I get the data
Future<List<Doctor>> findAllDoctor() async {
return await database.doctorDao.findAllDoctor();
}
I am getting this error
The argument type 'FutureBuilder<List<Doctor>>' can't be assigned to the parameter type 'List<DropdownMenuItem<Object>>?'.
What I need here is to display the doctors as list in a form to choose from.
Your FutureBuilder was placed in wrong place, check my edited code:
FutureBuilder<List<Doctor>>(
builder: (context, snapshot) {
if (snapshot.hasData) {
return DropdownButton<String>(
icon: const Icon(
Icons.arrow_drop_down,
),
hint: const Text('Select a doctor'),
items: snapshot.data
?.map((doctor) => DropdownMenuItem(
child: Text(doctor.name),
value: doctor.name,
))
.toList(),
onChanged: (val) {
print(val);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const CircularProgressIndicator();
},
);
I'm making an app using Flutter and I want to add text for my DropDownMenu like Select car make or something like that.
I could not find a tutorial so was hoping someone could help.
Here is my code:
new FormField(builder: (FormFieldState state) {
return InputDecorator(
decoration: InputDecoration(
),
child:FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
final album = snapshot.data;
final results = album.results;
return DropdownButtonHideUnderline(
child: DropdownButton<Results>(
isExpanded: true,
items: results.map((result) {
return DropdownMenuItem<Results>(
value: result,
child: Text('${result.modelName}'),
);
}).toList(),
onChanged: (album ) {
// selected album
setState(() {
_selected = album;
});
},
value: _selected,
));
} else
return CircularProgressIndicator();
}),
);
}),
I tried to set hinttext, but it does not work.
Here is how you can add a hint to your DropdownButton:
hint: Container(
width: 150,
child: Text(
"Select car",
style: TextStyle(color: Colors.grey),
textAlign: TextAlign.end,
),
),
my working with DropdownButton and i facing a problem my value is not taken
is show me null
my code is below
SizedBox(
height: 60.0,
child: new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("Category").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return new Text("Please wait");
var length = snapshot.data.documents.length;
DocumentSnapshot ds = snapshot.data.documents[length - 1];
return new DropdownButton(
items: snapshot.data.documents.map((
DocumentSnapshot document) {
return DropdownMenuItem(
child: new Text(document.data["name"]));
}).toList(),
value: category,
onChanged: (value) {
print(value);
},
hint: new Text("Category"),
style: TextStyle(color: Colors.black),
);
}
),
),
You should read more about StatefulWidget, here you have documentation: https://flutter.io/tutorials/interactive/
To fix your issue just update your category variable and refresh the state.
UPDATE
looks like you forget the value for the item also.
SizedBox(
height: 60.0,
child: new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("Category").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return new Text("Please wait");
var length = snapshot.data.documents.length;
DocumentSnapshot ds = snapshot.data.documents[length - 1];
return new DropdownButton(
items: snapshot.data.documents.map((
DocumentSnapshot document) {
return DropdownMenuItem(
value: document.data["name"],
child: new Text(document.data["name"]));
}).toList(),
value: category,
onChanged: (value) {
print(value);
setState(() {
category = value;
});
},
hint: new Text("Category"),
style: TextStyle(color: Colors.black),
);
}
),
),