#flutter #setstate
#flutter #setstate
Вопрос:
У меня есть нижняя таблица с кучей листов списка, один из которых — «добавить в избранное». основная идея заключается в том, что когда пользователь нажимает на эту плитку списка, ее значок и текст меняются с icon: heart_outline amp; text: add to favorite
на icon: heart_filled amp; text: remove from favorite
, изменение контролируется bool isFavorite
даже когда я использую setState и виджет с отслеживанием состояния, пользовательский интерфейс не меняется, пока я не закрою нижнюю таблицу и не открою ее снова
это код, который я придумал:
class _ListPageState extends State<ListPage> {
bool isFavorite = false;
DatabaseHelper databaseHelper;
@override
void initState() {
databaseHelper = DatabaseHelper.databaseHelper;
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 6.0),
child: FutureBuilder<List<Word>>(
future: databaseHelper.getAllWords(),
builder: (BuildContext context, AsyncSnapshot<List<Word>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
reverse: false,
controller: _scrollController,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
int objId = snapshot.data[index].wordId;
String germanW = snapshot.data[index].ger;
String englishW = snapshot.data[index].eng;
String favState = snapshot.data[index].isFav;
Word currenWord = new Word(wordId: objId,eng: englishW, ger: germanW, isFav: favState);
setState(() {
currenWord.isFav == 'true'
? isFavorite = true
: isFavorite = false;
//here I open the bottomsheet
trriggerBottomsheet(context, currenWord);
});
},
child: AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 200),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: listChild(snapshot.data[index].eng, snapshot.data[index].ger),
),
)),
);
},
);
}
return Center(child: CircularProgressIndicator(),);
},
),
);
}
//this is the bottomsheet itself
void trriggerBottomsheet(context, Word wrd) {
showModalBottomSheet(
context: context,
builder: (BuildContext buildCtx) {
return Container(
child: Wrap(
children: <Widget>[
// ... (irrelevant code)
new ListTile(
leading: isFavorite == true
? Icon(Icons.favorite, color: Colors.red[300],)
: Icon(Icons.favorite_border),
title: isFavorite == true
? Text('remove from favorites')
: Text('add to favorites'),
onTap: () {
isFavorite == true
? databaseHelper.removeFromFavorite(wrd.wordId)
: databaseHelper.addToFavorite(wrd.wordId);
setState(() {
isFavorite = !isFavorite;
});
},
),
],
),
);
});
}
}
Комментарии:
1. Я не думаю, что это хорошая идея — сохранять функцию запуска внутри setState. Также, насколько я знаю, setState во flutter является синхронным, поэтому эта проблема не должна возникать. Дайте мне знать, если перемещение триггера из setState не поможет.
2. @ArshShaikh ничего не произошло, все осталось как есть, и, как вы упомянули, этого не должно было происходить, но это каким-то образом! Я не вижу изменений, пока не закрою его и не открою снова!!
3. Сразу после setState попробуйте напечатать значение isFavourite. Проверьте, не меняется ли состояние или виджет не отображается повторно.
4. @ArshShaikh похоже, что виджет не перерисовывается, я просто проверил bool, как вы сказали, и он нормально меняется
5. Я не знаю, сильно ли это поможет, но я опубликую ответ, объясняющий как можно больше.
Ответ №1:
Проблема с кодом заключается в том, что для всего списка есть только одна переменная состояния.
Скорее у вас должен быть виджет для элемента списка, у которого есть собственное состояние.
Например:
class OrderListItem extends StatefulWidget {
final OrderItem orderItem;
const OrderListItem(this.orderItem);
@override
_OrderListItemState createState() => _OrderListItemState();
}
class _OrderListItemState extends State<OrderListItem> {
var _showMore = false;
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(10),
child: Column(
children: <Widget>[
ListTile(
title: Text('$${widget.orderItem.amount}'),
subtitle: Text(DateFormat().format(widget.orderItem.orderDate)),
trailing: IconButton(
icon: Icon(_showMore ? Icons.expand_less : Icons.expand_more),
onPressed: () {
setState(() {
_showMore = !_showMore;
});
},
),
),
if (_showMore) Divider(),
if (_showMore)
Container(
height: min(widget.orderItem.products.length * 20.0 10, 120),
margin: const EdgeInsets.symmetric(
vertical: 5,
horizontal: 15,
),
// irrelevant code
],
),
);
}
}
Здесь _showMore — это переменная состояния, используемая для расширения ListTile (не имеет отношения только к контексту)
Это виджет OderListItem, поэтому теперь, когда я пишу ListView.builder
ListView.builder(
itemBuilder: (ctx, index) =>
OrderListItem(ordersData.orders[index]),
itemCount: ordersData.orders.length,
),
Каждый элемент является виджетом с отслеживанием состояния.
Поэтому, когда я нажимаю кнопку ShowMore на любом элементе Order, это изменит только его собственное состояние и не повлияет ни на какие другие элементы.
Реализуйте свой код таким образом, он должен работать.
Комментарии:
1. извините за поздний ответ, я попробовал то, что вы сделали, и следовал той же логике, но, к сожалению, это не сработало
2. Ну ладно. Но я не вижу никакой другой проблемы. Извините, я не могу больше помочь😓