#flutter #dart #setstate #flutter-listview #statefulwidget
Вопрос:
У меня есть список категорий и товаров. Я хочу, чтобы это происходило, когда я нажимаю на категорию:
- Выбранная категория должна изменить цвет на оранжевый.
- Список продуктов должен быть обновлен с помощью продуктов выбранной категории.
Я попытался добавить setState и попытаться изменить цвет и представление списка, но это не сработало. Есть какие-нибудь идеи, что мне нужно исправить?
Future fetchProducts() async {
return await Provider.of<Products>(context, listen: false)
.fetchAndSetProducts('title', false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
backgroundColor: Colors.grey[100],
elevation: 0,
brightness: Brightness.light,
leading: Icon(null),
actions: <Widget>[
IconButton(
onPressed: () {},
icon: Icon(
Icons.shopping_basket,
color: Colors.grey[800],
),
)
],
),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: FutureBuilder(
future: _screenFuture,
// ignore: missing_return
builder: (context, snap) {
if (snap.error != null amp;amp;
!snap.error
.toString()
.contains('NoSuchMethodError')) {
return Center(child: Text('Something went wrong!'));
} else if (snap.hasData) {
var categoriesData = Provider.of<Categories>(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
FadeAnimation(
1,
Text(
'Food Delivery',
style: TextStyle(
color: Colors.grey[80],
fontWeight: FontWeight.bold,
fontSize: 30),
)),
SizedBox(
height: 20,
),
Container(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categoriesData.items.length,
itemBuilder: (ctx, i) => FadeAnimation(
i.toDouble(),
makeCategory(
isActive: i.toDouble() == 0
? true
: false,
title: categoriesData.items
.toList()[i]
.title)))),
SizedBox(
height: 10,
),
],
);
} else if (snap.connectionState ==
ConnectionState.waiting) {
//return Container();
return Center(child: Spinner());
}
})),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: FutureBuilder(
future: _productScreenFuture,
// ignore: missing_return
builder: (context, snap) {
if (snap.error != null amp;amp;
!snap.error
.toString()
.contains('NoSuchMethodError')) {
return Center(child: Text('Something went wrong!'));
} else if (snap.hasData) {
productsData = Provider.of<Products>(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FadeAnimation(
1,
Text(
'Food Delivery',
style: TextStyle(
color: Colors.grey[80],
fontWeight: FontWeight.bold,
fontSize: 30),
)),
SizedBox(
height: 20,
),
SizedBox(
height: 300,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: productsData.items.length,
itemBuilder: (ctx, i) => FadeAnimation(
1.4,
makeItem(
image: 'assets/images/one.jpg',
title: productsData.items[i].title,
price:
productsData.items[i].price)))),
SizedBox(
height: 10,
),
],
);
} else if (snap.connectionState ==
ConnectionState.waiting) {
//return Container();
return Center(child: Spinner());
}
})),
SizedBox(
height: 30,
)
],
),
),
);
}
Widget makeCategory({isActive, title}) {
return AspectRatio(
aspectRatio: isActive ? 3 : 2.5 / 1,
child: GestureDetector(
onTap: () {
print(title " clicked");
setState(() {
isActive = true;
productsData = Provider.of<Products>(context, listen: false)
.findBycategoryName(title);
print(productsData.first.title); // << data is available
});
},
child: Container(
margin: EdgeInsets.only(right: 10),
decoration: BoxDecoration(
color: isActive ? Colors.yellow[700] : Colors.white,
borderRadius: BorderRadius.circular(50),
),
child: Align(
child: Text(
title,
style: TextStyle(
color: isActive ? Colors.white : Colors.grey[500],
fontSize: 18,
fontWeight: isActive ? FontWeight.bold : FontWeight.w100),
),
),
),
));
}
Widget makeItem({image, String title, double price}) {
return AspectRatio(
aspectRatio: 1 / 1.5,
child: GestureDetector(
child: Container(
margin: EdgeInsets.only(right: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: AssetImage(image),
fit: BoxFit.cover,
)),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(begin: Alignment.bottomCenter, stops: [
.2,
.9
], colors: [
Colors.black.withOpacity(.9),
Colors.black.withOpacity(.3),
])),
child: //Expanded(
Padding(
padding: EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Align(
alignment: Alignment.topRight,
child: Icon(
Icons.favorite,
color: Colors.white,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"Tsh. $price",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
Text(
title,
style: TextStyle(color: Colors.white, fontSize: 20),
)
],
)
],
),
),
),
),
),
);
}
}
Комментарии:
1. вместо isActive = true; попробуйте isActive = !isActive ;
2. Создайте отдельный виджет с отслеживанием состояния для вашего элемента makeItem и элемента makeCategoryItem
3. Людям нужен весь код, чтобы понять весь контекст. Я считаю, что предоставил достаточно кода, чтобы кто-то мог запустить его локально. В прошлом я публиковал много вопросов с неполным кодом, и людям это не нравилось. Будьте добры, будьте милы.
4. @TechnicalWorld, он уже находится в виджете с отслеживанием состояния. Однако я попытался создать отдельный виджет с отслеживанием состояния в соответствии с вашим предложением, но это не сработало.
5. @Беньямин, это тоже не сработало.
Ответ №1:
На самом деле, когда вы вызываете setState (), ваш isActive снова изменяется на false из-за этого кода:
makeCategory(
isActive: i.toDouble() == 0
? true
: false,
ПОПРОБУЙТЕ ЭТОТ КОД:
bool currentCategory = 0;
Future fetchProducts() async {
return await Provider.of<Products>(context, listen: false)
.fetchAndSetProducts('title', false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
backgroundColor: Colors.grey[100],
elevation: 0,
brightness: Brightness.light,
leading: Icon(null),
actions: <Widget>[
IconButton(
onPressed: () {},
icon: Icon(
Icons.shopping_basket,
color: Colors.grey[800],
),
)
],
),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: FutureBuilder(
future: _screenFuture,
// ignore: missing_return
builder: (context, snap) {
if (snap.error != null amp;amp;
!snap.error
.toString()
.contains('NoSuchMethodError')) {
return Center(child: Text('Something went wrong!'));
} else if (snap.hasData) {
var categoriesData = Provider.of<Categories>(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
FadeAnimation(
1,
Text(
'Food Delivery',
style: TextStyle(
color: Colors.grey[80],
fontWeight: FontWeight.bold,
fontSize: 30),
)),
SizedBox(
height: 20,
),
Container(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categoriesData.items.length,
itemBuilder: (ctx, i) => FadeAnimation(
i.toDouble(),
makeCategory(
isActive: i == currentCategory
? true
: false,
position: i,
title: categoriesData.items
.toList()[i]
.title)))),
SizedBox(
height: 10,
),
],
);
} else if (snap.connectionState ==
ConnectionState.waiting) {
//return Container();
return Center(child: Spinner());
}
})),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: FutureBuilder(
future: _productScreenFuture,
// ignore: missing_return
builder: (context, snap) {
if (snap.error != null amp;amp;
!snap.error
.toString()
.contains('NoSuchMethodError')) {
return Center(child: Text('Something went wrong!'));
} else if (snap.hasData) {
productsData = Provider.of<Products>(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FadeAnimation(
1,
Text(
'Food Delivery',
style: TextStyle(
color: Colors.grey[80],
fontWeight: FontWeight.bold,
fontSize: 30),
)),
SizedBox(
height: 20,
),
SizedBox(
height: 300,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: productsData.items.length,
itemBuilder: (ctx, i) => FadeAnimation(
1.4,
makeItem(
image: 'assets/images/one.jpg',
title: productsData.items[i].title,
price:
productsData.items[i].price)))),
SizedBox(
height: 10,
),
],
);
} else if (snap.connectionState ==
ConnectionState.waiting) {
//return Container();
return Center(child: Spinner());
}
})),
SizedBox(
height: 30,
)
],
),
),
);
}
Widget makeCategory({isActive, title, position}) {
return AspectRatio(
aspectRatio: isActive ? 3 : 2.5 / 1,
child: GestureDetector(
onTap: () {
print(title " clicked");
setState(() {
currentCategory = position;
productsData = Provider.of<Products>(context, listen: false)
.findBycategoryName(title);
print(productsData.first.title); // << data is available
});
},
child: Container(
margin: EdgeInsets.only(right: 10),
decoration: BoxDecoration(
color: isActive ? Colors.yellow[700] : Colors.white,
borderRadius: BorderRadius.circular(50),
),
child: Align(
child: Text(
title,
style: TextStyle(
color: isActive ? Colors.white : Colors.grey[500],
fontSize: 18,
fontWeight: isActive ? FontWeight.bold : FontWeight.w100),
),
),
),
));
}
Widget makeItem({image, String title, double price}) {
return AspectRatio(
aspectRatio: 1 / 1.5,
child: GestureDetector(
child: Container(
margin: EdgeInsets.only(right: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: AssetImage(image),
fit: BoxFit.cover,
)),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(begin: Alignment.bottomCenter, stops: [
.2,
.9
], colors: [
Colors.black.withOpacity(.9),
Colors.black.withOpacity(.3),
])),
child: //Expanded(
Padding(
padding: EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Align(
alignment: Alignment.topRight,
child: Icon(
Icons.favorite,
color: Colors.white,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"Tsh. $price",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
Text(
title,
style: TextStyle(color: Colors.white, fontSize: 20),
)
],
)
],
),
),
),
),
),
);
}
}
Ответ №2:
значение isActive
не является переменной, и когда вы меняете его в setState, ничего не происходит. попробуйте использовать int? index
переменную для сохранения выбранного индекса категории
Комментарии:
1. Не могли бы вы привести пример, пожалуйста?