Обновить представление списка на основе нажатия кнопки

#flutter #dart #setstate #flutter-listview #statefulwidget

Вопрос:

У меня есть список категорий и товаров. Я хочу, чтобы это происходило, когда я нажимаю на категорию:

  1. Выбранная категория должна изменить цвет на оранжевый.
  2. Список продуктов должен быть обновлен с помощью продуктов выбранной категории.

Я попытался добавить 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. Не могли бы вы привести пример, пожалуйста?