I want to shuffle list called "Travel". So, user will see random members of widget every time.
However, when i do final _list = Travel.generateMembersMain().shuffle(); I came error on this line on widget: itemCount: _list.length, and var travel = _list[index];
This expression has a type of 'void' so its value can't be used.
I will be so happy if someone can help.
List Class:
class Travel { String name;
String location;
String url;
String placedetails_start;
String placedetails_end;
String places_url;
Travel(this.name,
this.location,
this.url,
this.placedetails_start,
this.placedetails_end,
this.places_url,);
static List<Travel> generateMembersMain() {
return [
Travel("Place 1", "XXX", "XXXX",
"XXXXX", "XXXXXX", "XXXXXXX"),
Travel("Place 2", "XXX", "XXXX",
"XXXXX", "XXXXXX", "XXXXXXX"),
]; }
Widget:
class Travelblog extends StatelessWidget {
final _list = Travel.generateMembersMain();
final _pageCtrl = PageController(viewportFraction: 0.9);
#override
Widget build(BuildContext context) {
return PageView.builder(
controller: _pageCtrl,
itemCount: _list.length,
itemBuilder: (context, index) {
var travel = _list[index];
return GestureDetector(
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return DetailPage(
travel: travel,
);
}));
},
child: Stack(
children: [
Padding(
padding:
const EdgeInsets.only(top: 10, right: 20, bottom: 30),
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Image.asset(travel.url,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover),
),
),
Positioned(
bottom: 50,
left: 15,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Material(
color: Colors.transparent,
child: Text(
travel.location,
style:
TextStyle(color: Colors.white, fontSize: 20),
)),
Material(
color: Colors.transparent,
child: Text(
travel.location,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 35),
)),
],
),
),
Positioned(
bottom: 0,
right: 30,
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.deepOrange,
borderRadius: BorderRadius.circular(30)),
child: Icon(
Icons.arrow_forward,
color: Colors.white,
size: 30,
),
),
)
],
));
});
}
}
change generateMembersMain to
static List<Travel> generateMembersMain() {
var travels = [
Travel("Place 1", "XXX", "XXXX", "XXXXX", "XXXXXX", "XXXXXXX"),
Travel("Place 2", "XXX", "XXXX", "XXXXX", "XXXXXX", "XXXXXXX"),
];
travels.shuffle();
return travels;
}
Hope helpful to you.
I found the answer, sorry for bothering here.
I write to shuffle() inside of widget:
Widget build(BuildContext context) {
_list.shuffle(); //Here
return PageView.builder(
controller: _pageCtrl,
itemCount: _list.length,
itemBuilder: (context, index) {
var travel = _list[index];
I generally keep in mind these two simple rules when I look for a suitable List method:
Some List methods return a value, however, some do not.
Also, some
of them change the list, but some do not.
For example, the return type of shuffle is void. And it changes the list also.
void shuffle(
[Random? random]
)
Another example:
The return type is bool and it also changes the list (removes the item)
bool remove(
Object? value
)
Another one: firstWhere
Returns the first instance/Element(E below) searched for, however, it has no effect on the list.
E firstWhere(
bool test(E element),
{E orElse()?}
)
Related
The bounty expires in 6 days. Answers to this question are eligible for a +50 reputation bounty.
wuuyungwuu is looking for an answer from a reputable source.
I am trying to select multiple components in this Wrap.toList() but every first index I select doesn't change its colour indicating that it is selected. It is selected in the list but it doesn't show.
See the 4 components I have selected.
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
shrinkWrap: true,
children: cC.allCommodityList.map((order) {
return InkWell(
onTap: () {
setState(() {
selectedItems.contains(order)
? selectedItems.remove(order)
: selectedItems.add(order);
commodityName = order.commodityName;
commodityid = order.commodityID;
// }
});
},
child: Card(
child: Column(
children: [
Expanded(
child: selectedItems.contains(order)
? SvgPicture.asset(
'assets/toiletpaper.svg',
color: Color.fromRGBO(0, 76, 32, 1),
)
: SvgPicture.asset(
'assets/toiletpaper.svg',
)),
selectedItems.contains(order)
? TopBorderNoTap(
listColor: [
Color.fromRGBO(229, 229, 229, 1),
Color.fromRGBO(0, 76, 32, 1),
],
text: order.commodityName.toString(),
color: Colors.white,
textColor: Colors.white)
: TopBorderNoTap(
listColor: [
Color.fromRGBO(229, 229, 229, 1),
Colors.white
],
text: order.commodityName.toString(),
textColor: Colors.black,
)
],
)),
);
}).toList(),
))),
This is my model class, not the full code but it just returns from json and to json
CommodityModel({
this.commodityID,
this.commodityName,
this.commodityImage,
});
CommodityModel.fromJson(Map<String, dynamic> json) {
commodityID = json['commodityID'];
commodityName = json['commodityName'];
commodityImage = json['commodityImage'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['commodityID'] = commodityID;
data['commodityName'] = commodityName;
data['commodityImage'] = commodityImage;
You can try this approach to select & d-select model list item.
class MyNewWidget extends StatefulWidget {
const MyNewWidget({super.key});
#override
State<MyNewWidget> createState() => _MyNewWidgetState();
}
class _MyNewWidgetState extends State<MyNewWidget> {
final List<CommodityModel> allCommodityList = [
CommodityModel(
commodityID: 1,
commodityName: "Toilet Paper",
commodityImage: "commodityImage"),
CommodityModel(
commodityID: 2,
commodityName: "Paper Towels",
commodityImage: "commodityImage"),
CommodityModel(
commodityID: 3,
commodityName: "Hand shop",
commodityImage: "commodityImage"),
CommodityModel(
commodityID: 4,
commodityName: "Air freshner",
commodityImage: "commodityImage")
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
),
itemCount: allCommodityList.length,
itemBuilder: (BuildContext ctx, index) {
final order = allCommodityList[index];
return Container(
alignment: Alignment.center,
child: InkWell(
onTap: () {
setState(() {
order.isSelected = !order.isSelected;
});
},
child: Card(
child: Column(
children: [
Expanded(
child: SvgPicture.asset(
'assets/toiletpaper.svg',
color: order.isSelected
? const Color.fromRGBO(0, 76, 32, 1)
: null,
)),
Row(
children: [
Expanded(
child: Container(
color: order.isSelected
? const Color.fromRGBO(0, 76, 32, 1)
: null,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Text(
order.commodityName ?? "",
style: TextStyle(
color: order.isSelected
? Colors.white
: Colors.black),
)),
),
),
),
],
)
],
)),
),
);
}),
),
);
}
}
class CommodityModel {
int? commodityID;
String? commodityName;
String? commodityImage;
bool isSelected =
false; // Add key for selection handle. You can also handle with single orderID Array
CommodityModel({this.commodityID, this.commodityName, this.commodityImage});
CommodityModel.fromJson(Map<String, dynamic> json) {
commodityID = json['commodityID'];
commodityName = json['commodityName'];
commodityImage = json['commodityImage'];
isSelected = json['isSelected'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['commodityID'] = commodityID;
data['commodityName'] = commodityName;
data['commodityImage'] = commodityImage;
data['isSelected'] = isSelected;
return data;
}
}
I'm working on a shopping app for a school project, and I'm trying to get my product images and details to show up on the various product detail screens. I used some sample images from flutter's Shrine sample app as I followed their tutorial. The problem I'm facing now is an endless Listview scroll that looks like this, and when you keep scrolling, it repeats the image and details endlessly:
What should I do to avoid this? Sorry, I am really new to coding so I am not to sure how to go about fixing this problem...Below I have included a few dart files of my current code which might be helpful. Thank you to anyone who is willing to help, it is very much appreciated!
Product detail screen dart file:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:MyShoppingApp/provider/CartProvider.dart';
import 'package:MyShoppingApp/db/cart_database.dart';
import 'package:MyShoppingApp/model/cart.dart';
import 'model/products_repository.dart';
import '../model/cart.dart';
class ProductDetailsPage extends StatelessWidget {
static const routeName = '/user-products';
ProductDetailsPage({Key? key}) : super(key: key); //const
DBHelper dbHelper = DBHelper();
#override
Widget build(BuildContext context) {
//get particular productId using the ModalRoute class
final productId = ModalRoute.of(context)!.settings.arguments as String;
print(productId);
//use Provider package to find out ID by accessing method declared in Product()
final loadedProduct = ProductsRepository().findById(productId);
//List<bool> clicked = List.generate(10, (index) => false, growable: true);
final cart = Provider.of<CartProvider>(context);
void saveData(int index) {
dbHelper
.insert(
CartItem(
id: index,
title: loadedProduct.name,
price: loadedProduct.price.toDouble(),
quantity: ValueNotifier(1),
image: loadedProduct.image,
),
)
.then((value) {
cart.addTotalPrice(loadedProduct.price.toDouble());
cart.addCounter();
print('Product Added to cart');
}).onError((error, stackTrace) {
print(error.toString());
});
}
return Scaffold(
backgroundColor: Colors.orange[50],
appBar: AppBar(
backgroundColor: Colors.deepOrange[900],
title: const Text("Product details "),
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios_outlined,
color: Colors.black,
semanticLabel: 'back to home',
),
onPressed: () {
Navigator.pop(context);
},
),
),
//body:
body: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 8.0),
shrinkWrap: true,
itemCount: loadedProduct.length,
itemBuilder: (context, index) {
return Card(
//SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(
height: 300,
width: double.infinity,
child: Image.asset(
loadedProduct.image,
fit: BoxFit.cover,
),
),
const SizedBox(height: 10),
Text(
'\$${loadedProduct.price}',
style: const TextStyle(
color: Colors.grey,
fontSize: 20,
),
),
const SizedBox(
height: 10,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.blueGrey.shade900),
onPressed: () {
saveData(Random().nextInt(1000));
},
child: const Text('Add to Cart')),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
width: double.infinity,
child: Text(
loadedProduct.description,
textAlign: TextAlign.center,
softWrap: true,
),
),
],
),
);
})
);
}
}
Product Repository Dart file:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:MyShoppingApp/db/cart_database.dart';
//add product data
import 'package:MyShoppingApp/model/product.dart';
//to get all products at once or any particular product by its ID
//product class that uses mixins with ChangeNotifier
class ProductsRepository with ChangeNotifier {
DBHelper dbHelper = DBHelper();
static List<Product> loadProducts(Category category) {
//linked list storing objects of type Product
var allProducts = <Product>[
Product(
category: Category.accessories,
id: "0",
isFeatured: true,
name: 'Vagabond sack',
price: 120,
details: "Nice fancy shirt",
description: "Comfortable and minimalistic",
image: "packages/shrine_images/0-0.jpg",
),
Product(
category: Category.accessories,
id: "1",
isFeatured: true,
name: 'Stella sunglasses',
price: 58,
details: "",
description: "",
image: "packages/shrine_images/1-0.jpg",
),
Product(
category: Category.accessories,
id: "2",
isFeatured: false,
name: 'Whitney belt',
price: 35,
details: "",
description: "",
image: "packages/shrine_images/2-0.jpg",
),
Product(
category: Category.accessories,
id: "3",
isFeatured: true,
name: 'Garden strand',
price: 98,
details: "",
description: "",
image: "packages/shrine_images/3-0.jpg",
),
Product(
category: Category.accessories,
id: "4",
isFeatured: false,
name: 'Strut earrings',
price: 34,
details: "",
description: "",
image: "packages/shrine_images/4-0.jpg",
),//removed other products to save space
];
if (category == Category.all) {
return allProducts;
} else {
return allProducts.where((Product p) {
return p.category == category;
}).toList();
}
}
//to get particular products by ID
Product findById(String id) {
var x = loadProducts(Category.all).firstWhere((prod) => prod.id == id);
print("findById successful");
print(x);
return x;
}
void addProduct() {
// _items.add(value);
notifyListeners();
}
}
Thank you everyone! Also, this problem stemmed from an earlier error in another post that a user was helping me to work through, but I didn't want to trouble them too much so I am reposting it here. Thank you #eamirho3einππ
Try this:
class ProductDetailsPage extends StatefulWidget {
static const routeName = '/user-products';
ProductDetailsPage({Key? key}) : super(key: key);
#override
State<ProductDetailsPage> createState() => _ProductDetailsPageState();
}
class _ProductDetailsPageState extends State<ProductDetailsPage> {
//const
DBHelper dbHelper = DBHelper();
List<Product> loadedProduct = []; // <----update this
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
//get particular productId using the ModalRoute class
final productId = ModalRoute.of(context)!.settings.arguments as String;
print(productId);
//use Provider package to find out ID by accessing method declared in Product()
setState(() {
loadedProduct = ProductsRepository().findById(productId);
});
});
}
#override
Widget build(BuildContext context) {
//List<bool> clicked = List.generate(10, (index) => false, growable: true);
final cart = Provider.of<CartProvider>(context);
void saveData(int index) {
dbHelper
.insert(
CartItem(
id: index,
title: loadedProduct.name,
price: loadedProduct.price.toDouble(),
quantity: ValueNotifier(1),
image: loadedProduct.image,
),
)
.then((value) {
cart.addTotalPrice(loadedProduct.price.toDouble());
cart.addCounter();
print('Product Added to cart');
}).onError((error, stackTrace) {
print(error.toString());
});
}
return Scaffold(
backgroundColor: Colors.orange[50],
appBar: AppBar(
backgroundColor: Colors.deepOrange[900],
title: const Text("Product details "),
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios_outlined,
color: Colors.black,
semanticLabel: 'back to home',
),
onPressed: () {
Navigator.pop(context);
},
),
),
//body:
body: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 8.0),
shrinkWrap: true,
itemCount: loadedProduct.length,
itemBuilder: (context, index) {
return Card(
//SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(
height: 300,
width: double.infinity,
child: Image.asset(
loadedProduct.image,
fit: BoxFit.cover,
),
),
const SizedBox(height: 10),
Text(
'\$${loadedProduct.price}',
style: const TextStyle(
color: Colors.grey,
fontSize: 20,
),
),
const SizedBox(
height: 10,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.blueGrey.shade900),
onPressed: () {
saveData(Random().nextInt(1000));
},
child: const Text('Add to Cart')),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
width: double.infinity,
child: Text(
loadedProduct.description,
textAlign: TextAlign.center,
softWrap: true,
),
),
],
),
);
})
);
}
}
Hello I see some weird thing there.
ProductsRepository().findById(productId) return a single Product. Then
loadedProduct is a Product. Later on the ListView.builder on the itemCount parameter you used loadedProduct.length (loadedProduct in this point is used as a list of element). I dont know if the Product object have a .length method nor what returns that. But i think there is the problem. When you set itemCount: loadedProduct.length you are saying than the list will have x elements and later on the itemBuilder you are only using the data from loadedProduct to create the same widget in the list, x times.
Assuming that loadedProduct.length = 10, you will have a list view with the same Element 10 times
The next example use the loadedProduct to fill the products list. The product list will be used as the data on the ListView.builder. Then the list will show 3 time the same widget.
final products = [
loadedProduct,
loadedProduct,
loadedProduct
];
return Scaffold(
backgroundColor: Colors.orange[50],
appBar: AppBar(
backgroundColor: Colors.deepOrange[900],
title: const Text("Product details "),
leading: IconButton(
icon: const Icon(
Icons.arrow_back_ios_outlined,
color: Colors.black,
semanticLabel: 'back to home',
),
onPressed: () {
Navigator.pop(context);
},
),
),
//body:
body: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 8.0),
shrinkWrap: true,
itemCount: products.length,
itemBuilder: (context, index) {
final targetProduct = products[index];
return Card(
//SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(
height: 300,
width: double.infinity,
child: Image.asset(
targetProduct.image,
fit: BoxFit.cover,
),
),
const SizedBox(height: 10),
Text(
'\$${targetProduct.price}',
style: const TextStyle(
color: Colors.grey,
fontSize: 20,
),
),
const SizedBox(
height: 10,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.blueGrey.shade900),
onPressed: () {
saveData(Random().nextInt(1000));
},
child: const Text('Add to Cart')),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
width: double.infinity,
child: Text(
targetProduct.description,
textAlign: TextAlign.center,
softWrap: true,
),
),
],
),
);
})
);
Just think what do you want to show on the list and use the right data. And review why the Product object have a .length method and what it return.
if you want to stop scroll listview add
NeverScrollableScrollPhysics
physics: const NeverScrollableScrollPhysics()
Inside ListView widget, use
I'm having trouble displaying a filtered list in my widget. It works up to the point of printing the filtered data in the list as per the query that is passed but not when that exact data needs to be displayed. I believe I will have to update the list with the filtered data every time I type in a query but I just cannot figure out how and where I need to use setState to update that. My code and the outputs are as follows:
Initially, the entire list gets rendered but the moment I type in a query string, the list is supposed to get modified with only the data that matched the query. This is not something that's happening at the moment. The list tends to remain as it is.
However, when I print the filtered data, it seems to work just fine(_searchResult printed in the searchData method below).
[
{product_id: 8, restaurant_name: Mocambo, restaurant_id: 6, product_name: Kaju Paneer, product_description: Tasty yummy paneer gravy dish, product_image: /public/assets/product/lgml5L03-19-41.jpg, product_selling_price: 320},
{product_id: 5, restaurant_name: City Club, restaurant_id: 1, product_name: Palak Paneer, product_description: Tasty silky gravy with goodness of palak, product_image: /public/assets/product/C6pGz101-42-17.jpg, product_selling_price: 180},
{product_id: 4, restaurant_name: City Club, restaurant_id: 1, product_name: Shahi Paneer, product_description: Tasty Paneer main course dish, product_image: /public/assets/product/vgI1dR01-29-18.jpg, product_selling_price: 240}
]
The code:
The method that filters. (Please note that the filtering is performed after the data is fetched from the server. For my convenience, I decided to convert it into a list)
class PopularDishesProvider with ChangeNotifier {
Map<String, dynamic> _dishes = {};
final List<dynamic> _searchDish = [];
List<dynamic> _searchResult = [];
List<dynamic> get searchDish {
return [..._searchDish];
}
List<dynamic> get searchResult {
return [..._searchResult];
}
Future<void> searchData(String query) async {
final url = Uri.parse(baseUrl + 'api/all_products');
final response = await http.get(url);
PopularDishes popularDishes = popularDishesFromJson(response.body); //This method converts the response into Dart model
_dishes = popularDishes.toJson();
_dishes['data'].forEach((value) => _searchDish.add(value));
_searchResult = _searchDish.where((element) {
final name = element['product_name'].toLowerCase();
final searchQuery = query.toLowerCase();
return name.contains(searchQuery);
}).toList();
print(_searchResult);
notifyListeners();
}
}
The widget where this is supposed to be rendered:
class SearchState extends State<Search> {
final _controller = TextEditingController();
bool value = true;
String query = '';
List<dynamic> search = [];
PopularDishesProvider popular = PopularDishesProvider();
#override
void initState() { //This is to make the API Call for the first time
// TODO: implement initState
Provider.of<PopularDishesProvider>(context, listen: false)
.searchData('');
});
super.initState();
}
#override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
final width = MediaQuery.of(context).size.width;
final textScale = MediaQuery.of(context).textScaleFactor * 1.2;
final searchProvider = Provider.of<PopularDishesProvider>(context).searchResult;
PopularDishesProvider popular = PopularDishesProvider();
// TODO: implement build
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
elevation: 5,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
// backgroundColor: Colors.green,
titleSpacing: 0,
toolbarHeight: 100,
title: Column(
children: [
Container(
width: double.infinity,
height: 40,
.......
.......
.......
),
Stack(
children: [
Container(
height: 60,
width: double.infinity,
// color: Colors.red,
padding: const EdgeInsets.only(top: 8, left: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(
Icons.search,
size: 30,
color: Colors.grey,
),
Expanded(
child: Center(
child: Container(
margin:
const EdgeInsets.only(bottom: 6, right: 4),
padding: const EdgeInsets.only(left: 6),
height: 45,
width: width * 0.7,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(14)),
border:
Border.all(color: Colors.grey, width: 2)),
child: Row(
children: [
Flexible(
flex: 9,
fit: FlexFit.tight,
child: Center(
child: TextField(
controller: _controller,
onChanged: (value) async {
setState(() {
query = value;
});
await popular.searchData(value);
},
autofocus: true,
cursorColor: Colors.grey,
style: const TextStyle(
color: Colors.grey, fontSize: 18),
decoration: const InputDecoration(
border: InputBorder.none,
hintText:
'Search By Restaurant or Food',
hintStyle:
TextStyle(color: Colors.grey),
),
),
)),
Flexible(
flex: 1,
fit: FlexFit.tight,
child: InkWell(
onTap: () => Navigator.of(context).pop(),
child: const Icon(Icons.close,
color: Colors.grey),
),
)
],
),
),
),
),
],
),
),
],
)
],
)),
body: Column(
children: [
Expanded(
child: Container(
width: double.infinity,
color: Colors.red,
child: ListView.builder(
itemBuilder: (context, index) => ListTile(
title: Text(searchProvider [index]['product_name'])),
itemCount: searchProvider.length,
),
)
)
],
),
);
}
}
Can someone please help out?
class Header extends StatefulWidget {
#override
_HeaderState createState() => _HeaderState();
Future<List> getSearch() async {
List<String> searchList;
final List<DocumentSnapshot> documents =
(await FirebaseFirestore.instance.collection('movies').get()).docs;
searchList = documents
.map((documentSnapshot) => documentSnapshot['movieName'] as String)
.toList();
print(searchList); //This does print the data from the database.
return searchList;
}
}
The above code fetches data from FirebaseFirestore and also the print statement prints the list fetched.
class _HeaderState extends State<Header> {
final MenuController _controller = Get.put(MenuController());
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: kDarkBlackColor,
child: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(kDefaultPadding),
constraints: BoxConstraints(maxWidth: kMaxWidth),
child: Column(
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
borderRadius:
new BorderRadius.all(Radius.circular(10.0)),
shape: BoxShape.rectangle,
border: Border.all(color: Colors.white)),
child: IconButton(
icon: Icon(
Icons.menu,
color: Colors.white,
),
onPressed: () {
_controller.openOrCloseDrawer();
},
),
),
Container(
width: MediaQuery.of(context).size.width / 7,
child: Image.asset('assets/images/logo.png')),
Spacer(),
Container(
color: Colors.white,
width: MediaQuery.of(context).size.width / 5,
child: TypeAheadField(
hideOnEmpty: true,
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context)
.style
.copyWith(fontStyle: FontStyle.italic),
decoration: InputDecoration(
border: OutlineInputBorder())),
suggestionsCallback: (pattern) async {
return CitiesService.getSuggestions(pattern);
},
transitionBuilder:
(context, suggestionsBox, controller) {
return suggestionsBox;
},
itemBuilder: (context, suggestion) {
return ListTile(
title: Text(suggestion),
);
},
onSuggestionSelected: (suggestion) {},
)),
Spacer(),
Socal(),
//Spacer(),
],
),
],
),
)
],
),
),
);
}
}
class CitiesService {
static List<String> search = Header().getSearch() as List<String>; //This is not adding data to list
static List<String> getSuggestions(String query) {
print(search); //This does not print any thing.
List<String> matches = List();
matches.addAll(search);
matches.retainWhere((s) => s.toLowerCase().contains(query.toLowerCase()));
return matches;
}
}
I am trying to store the data fetched in getSearch() into search List so that I can use it to provide suggestions but the list is empty. I don't know if this is the correct way to convert future into list of strings. Help would be really appreciated. Also, if there is another way to implement search from FirebaseFirestore, please do let me know.
Thanks in advance.
The problem here is that getSearch is an async function, which mean that it return intially a Future while is awaiting the async part of the function to be fulfilled and continue to execute, so in order to capture that you should be using a future.then() notation, doing something like this while calling getSearch will fix the issue:
Header().getSearch().then((value) {
static List<String> search = value as List<String>;
...
});
I am trying to create a page that lists a number of questions. For each question I will have different answers. At the minute, whenever I select an answer, the same option on the other questions are selected at the same time. How can I avoid this and make each question its own entity? Here is my code:
class QuestionScreen extends StatefulWidget {
#override
_QuestionScreenState createState() => _QuestionScreenState();
}
class _QuestionScreenState extends State<QuestionScreen> {
List<bool> _isChecked;
final Map<String, Map> questions = {"milk comes from what animal":
{"horse": false, "monkey": false, "frog": false, "cow": true},
"what colour is the sea?":
{"red": false, "green": false, "blue": true, "yellow": false}};
#override
void initState() {
super.initState();
_isChecked = List<bool>.filled(questions.values.length, false);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Text("questions page"),
),
Expanded(
child: new ListView.builder(
itemCount: questions.keys.length,
itemBuilder: (BuildContext ctxt, int questionTitleIndex) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Container(
height: MediaQuery.of(context).size.height * 0.45,
decoration: BoxDecoration(
color: OurTheme().ourCanvasColor,
borderRadius: BorderRadius.circular(25),
),
child: Column(
children: [
Text(
questions.keys.toList()[questionTitleIndex],
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.w800),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
shrinkWrap: true,
itemCount: questions.values
.toList()[questionTitleIndex]
.keys
.length,
itemBuilder:
(BuildContext ctxt, int questionAnswersIndex) {
return Container(
decoration: BoxDecoration(border: Border.all()),
child: CheckboxListTile(
title: Text(
"${questionAnswersIndex + 1}. ${questions.values.toList()[questionTitleIndex].keys.toList()[questionAnswersIndex]}",
style: TextStyle(
color: Colors.white, fontSize: 16),
),
value: _isChecked[questionAnswersIndex],
controlAffinity:
ListTileControlAffinity.platform,
onChanged: (bool value) {
setState(
() {
_isChecked[questionAnswersIndex] =
value;
},
);
},
activeColor: OurTheme().ourCanvasColor,
checkColor: Colors.white,
),
);
},
),
)
],
),
),
);
},
),
)
],
),
);
}
}
I see a few problems here.
First since your need to maintain the answer for each question in your _isChecked. It would make more sense to make it a Map<String, String> instead of a List<bool>.
Inside it, the key will be the question title and the value will be the selected option title.
So, inside your initState, you will initiate it liket this.
Map<String, String> _isChecked = {}; // Initializing with empty map
#override
void initState() {
super.initState();
widget.questions.keys.forEach((key) {
// For each question we first set the answer as "". means nothing selected.
_isChecked[key] = "";
// We then loop over the options of that question to see if any option was already true and set it as the initial answer.
for (MapEntry entry in widget.questions[key]!.entries) {
if (entry.value) _isChecked[key] = entry.key;
}
});
}
After this, you just have to change the places in your code where you were using the _isChecked variable.
Here is the link to the full working code. Just replace all your code with the code in the link.
Result.