Пользовательский интерфейс Flutter не обновляется мгновенно даже с помощью setState (){}

#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. Ну ладно. Но я не вижу никакой другой проблемы. Извините, я не могу больше помочь😓