Виджет перезагружается снова при нажатии на дочерний элемент/виджет в Flutter.

Вопрос или проблема

У меня есть состояние виджет, и я сталкиваюсь с странной проблемой: каждый раз, когда я нажимаю на любой элемент, такой как CustomDropDownButtonFormField, и открываю выпадающее меню, даже не выбрав ни один вариант в выпадающем меню, вызывается метод build(BuildContext context) и экран перезагружается. Это странно. Я также попробовал закомментировать setState везде в этом виджете, но проблема все еще существует. Я потратил много времени на это. Но я не могу решить эту проблему. Пожалуйста, посмотрите на мой код и дайте мне знать, что с ним не так.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:np_casse/app/customized_component/sliver_grid_delegate_fixed_cross_axis_count_and_fixed_height.dart';
import 'package:np_casse/componenents/custom.drop.down.button.form.field.field.dart';
import 'package:np_casse/core/models/category.catalog.model.dart';
import 'package:np_casse/core/models/product.catalog.model.dart';
import 'package:np_casse/core/models/user.app.institution.model.dart';
import 'package:np_casse/core/notifiers/authentication.notifier.dart';
import 'package:np_casse/core/notifiers/product.catalog.notifier.dart';
import 'package:np_casse/screens/shopScreen/widget/product.card.dart';
import 'package:provider/provider.dart';

class ProductThreeShopScreen extends StatefulWidget {
  const ProductThreeShopScreen({
    Key? key,
    required this.childCategoryCatalogModel,
  }) : super(key: key);
  final CategoryCatalogModel childCategoryCatalogModel;

  State<ProductThreeShopScreen> createState() =>
      __ProductThreeShopScreenState();
}

class __ProductThreeShopScreenState extends State<ProductThreeShopScreen> {
  double widgetWitdh = 320;
  double widgetHeight = 620;
  double widgetHeightHalf = 470;
  double gridMainAxisSpacing = 10;

  Timer? _timer;
  Icon icona = const Icon(Icons.search);
  TextEditingController nameDescSearchController = TextEditingController();
  bool viewOutOfAssortment = false;
  bool readImageData = true;
  bool readAlsoDeleted = false;
  int selectedCategory = 0;
  List<DropdownMenuItem<String>> availableCategory = [];

  String numberResult="10";
  List<DropdownMenuItem<String>> availableNumberResult = [
    DropdownMenuItem(child: Text("Tutti"), value: "All"),
    DropdownMenuItem(child: Text("10"), value: "10"),
    DropdownMenuItem(child: Text("25"), value: "25"),
    DropdownMenuItem(child: Text("50"), value: "50"),
  ];

  String orderBy = 'NameProduct';
  List<DropdownMenuItem<String>> availableOrderBy = [
    DropdownMenuItem(child: Text("Nome"), value: "NameProduct"),
    DropdownMenuItem(child: Text("Descrizione"), value: "DescriptionProduct"),
    DropdownMenuItem(
        child: Text("Ordine di visualizzazione"), value: "DisplayOrder"),
  ];
  Icon iconaNameDescSearch = const Icon(Icons.search);

  void onChangeCategory(value) {
    setState(() {
      selectedCategory = value!;
    });
  }

  void onChangeNumberResult(value) {
    setState(() {
      numberResult = value!;
    });
  }

  void onChangeOrderBy(value) {
    setState(() {
      orderBy = value!;
    });
  }

  @override
  void initState() {
    nameDescSearchController.text = "";
    viewOutOfAssortment = false;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    AuthenticationNotifier authenticationNotifier =
        Provider.of<AuthenticationNotifier>(context);

    // ProjectNotifier projectNotifier = Provider.of<ProjectNotifier>(context);
    // StoreNotifier storeNotifier = Provider.of<StoreNotifier>(context);
    UserAppInstitutionModel cUserAppInstitutionModel =
        authenticationNotifier.getSelectedUserAppInstitution();
    // bool canAddProduct = authenticationNotifier.canUserAddItem();

    return SafeArea(
        child: Scaffold(
      backgroundColor: Theme.of(context).colorScheme.background,
      // drawer: const CustomDrawerWidget(),
      appBar: AppBar(
        centerTitle: true,
        title: Text(widget.childCategoryCatalogModel.nameCategory,
            style: Theme.of(context).textTheme.headlineLarge),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                    flex: 1,
                    child: CustomDropDownButtonFormField(
                      enabled: true,
                      actualValue: numberResult,
                      labelText: 'Mostra numero risultati',
                      listOfValue: availableNumberResult,
                      onItemChanged: (value) {
                        onChangeNumberResult(value);
                      },
                    )),
                Expanded(
                    flex: 2,
                    child: CustomDropDownButtonFormField(
                      enabled: true,
                      actualValue: orderBy,
                      labelText: 'Ordinamento',
                      listOfValue: availableOrderBy,
                      onItemChanged: (value) {
                        onChangeOrderBy(value);
                      },
                    )),
                Expanded(
                  flex: 1,
                  child: CheckboxListTile(
                    side: const BorderSide(color: Colors.blueGrey),
                    checkColor: Colors.blueAccent,
                    checkboxShape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(15)),
                    activeColor: Colors.blueAccent,

                    controlAffinity: ListTileControlAffinity.leading,
                    value: viewOutOfAssortment,
                    onChanged: (bool? value) {
                      setState(() {
                        viewOutOfAssortment = value!;
                      });
                    },
                    title: Text(
                      'Visualizza fuori assortimento',
                      style: Theme.of(context)
                          .textTheme
                          .labelMedium!
                          .copyWith(color: Colors.blueGrey),
                    ),
                    // subtitle: const Text(""),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: CheckboxListTile(
                    side: const BorderSide(color: Colors.blueGrey),
                    checkColor: Colors.blueAccent,
                    checkboxShape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(15)),
                    activeColor: Colors.blueAccent,

                    controlAffinity: ListTileControlAffinity.leading,
                    value: readImageData,
                    onChanged: (bool? value) {
                      setState(() {
                        readImageData = value!;
                      });
                    },
                    title: Text(
                      'Visualizza immagine',
                      style: Theme.of(context)
                          .textTheme
                          .labelMedium!
                          .copyWith(color: Colors.blueGrey),
                    ),
                    // subtitle: const Text(""),
                  ),
                ),
                Expanded(
                  flex: 2,
                  child: TextFormField(
                    style: Theme.of(context)
                        .textTheme
                        .labelLarge!
                        .copyWith(color: Colors.blueGrey),
                    onChanged: (String value) {
                      if (_timer?.isActive ?? false) {
                        _timer!.cancel();
                      }
                      _timer = Timer(const Duration(milliseconds: 1000), () {
                        setState(() {
                          iconaNameDescSearch = const Icon(Icons.cancel);
                          if (value.isEmpty) {
                            iconaNameDescSearch = const Icon(Icons.search);
                          }
                        });
                      });
                    },
                    controller: nameDescSearchController,
                    decoration: InputDecoration(
                      labelText: "Ricerca per nome, descrizione o barcode",
                      labelStyle: Theme.of(context)
                          .textTheme
                          .labelMedium!
                          .copyWith(color: Colors.blueGrey),
                      hintText: "Ricerca per nome, descrizione o barcode",
                      hintStyle: Theme.of(context)
                          .textTheme
                          .labelLarge!
                          .copyWith(
                              color:
                                  Theme.of(context).hintColor.withOpacity(0.3)),
                      suffixIcon: IconButton(
                        icon: iconaNameDescSearch,
                        onPressed: () {
                          setState(() {
                            if (iconaNameDescSearch.icon == Icons.search) {
                              iconaNameDescSearch = const Icon(Icons.cancel);
                            } else {
                              iconaNameDescSearch = const Icon(Icons.search);
                              nameDescSearchController.text = "";
                            }
                          });
                        },
                      ),
                      floatingLabelBehavior: FloatingLabelBehavior.always,
                      border: const OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(10.0)),
                        borderSide: BorderSide(color: Colors.grey, width: 1.0),
                      ),
                      enabledBorder: const OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(10.0)),
                        borderSide: BorderSide(color: Colors.grey, width: 1.0),
                      ),
                      focusedBorder: const OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(10.0)),
                        borderSide: BorderSide(color: Colors.blue, width: 1.0),
                      ),
                      errorBorder: const OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(10.0)),
                        borderSide: BorderSide(color: Colors.red, width: 1.0),
                      ),
                      focusedErrorBorder: const OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(10.0)),
                        borderSide: BorderSide(
                            color: Colors.deepOrangeAccent, width: 1.0),
                      ),
                    ),
                  ),
                ),
              ],
            ),
            SizedBox(
              height: MediaQuery.of(context).size.height,
              width: MediaQuery.of(context).size.width,
              child: Consumer<ProductCatalogNotifier>(
                builder: (context, productCatalogNotifier, _) {
                  return SizedBox(
                    height: MediaQuery.of(context).size.height * 0.8,
                    width: MediaQuery.of(context).size.width,
                    child: FutureBuilder(
                      future: productCatalogNotifier.getProducts(
                          context: context,
                          token: authenticationNotifier.token,
                          idUserAppInstitution:
                              cUserAppInstitutionModel.idUserAppInstitution,
                          idCategory:
                              widget.childCategoryCatalogModel.idCategory,
                          readAlsoDeleted: false,
                          numberResult: numberResult,
                          nameDescSearch: nameDescSearchController.text,
                          orderBy: orderBy,
                          readImageData: readImageData,
                          shoWVariant: true,
                          viewOutOfAssortment: viewOutOfAssortment),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState ==
                            ConnectionState.waiting) {
                          return const Center(
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                Center(
                                    child: SizedBox(
                                        width: 100,
                                        height: 100,
                                        child: CircularProgressIndicator(
                                          strokeWidth: 5,
                                          color: Colors.redAccent,
                                        ))),
                              ],
                            ),
                          );
                        } else if (!snapshot.hasData) {
                          return const Center(
                            child: Text(
                              'No data...',
                              style: TextStyle(
                                color: Colors.redAccent,
                              ),
                            ),
                          );
                        } else {
                          var tSnapshot =
                              snapshot.data as List<ProductCatalogModel>;
                          var t = tSnapshot
                              .any((element) => element.imageData.isNotEmpty);
                          bool areAllWithNoImage = !t;
                          double cHeight = 0;
                          if (areAllWithNoImage) {
                            cHeight = widgetHeightHalf;
                          } else {
                            cHeight = widgetHeight;
                          }
                          return GridView.builder(
                              gridDelegate:
                                  SliverGridDelegateWithFixedCrossAxisCountAndFixedHeight(
                                crossAxisCount:
                                    (MediaQuery.of(context).size.width) ~/
                                        widgetWitdh,
                                crossAxisSpacing: 10,
                                mainAxisSpacing: gridMainAxisSpacing,
                                height: cHeight,
                              ),
                              physics: const ScrollPhysics(),
                              shrinkWrap: true,
                              itemCount: tSnapshot.length,
                              // scrollDirection: Axis.horizontal,
                              itemBuilder: (context, index) {
                                ProductCatalogModel productCatalog =
                                    tSnapshot[index];
                                return ProductCard(
                                    productCatalog: productCatalog,
                                    areAllWithNoImage: areAllWithNoImage);
                              });
                        }
                      },
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    ));
  }
}

Ответ или решение

Проблема, с которой вы сталкиваетесь в вашем приложении Flutter, заключается в том, что при нажатии на элемент в выпадающем списке происходит перерисовка вашего виджета, главная функция build(BuildContext context) вызывается повторно, даже если вы не изменили состояние родительского виджета или не вызвали setState(). Давайте рассмотрим возможные причины этой проблемы и предложим решения.

Возможные причины проблемы

  1. Изменения состояния в родительском виджете: Если родительский виджет вашего ProductThreeShopScreen (например, Provider) имеет состояние, которое изменяется, это может вызывать повторный вызов build() для всех дочерних виджетов. Убедитесь, что родительский виджет не обновляет состояние или не вызывает setState() на каждом взаимодействии с дочерними виджетами.

  2. Использование Provider: При использовании Provider важно убедиться, что только необходимые части дерева виджетов перерисовываются. Если ваш ProductCatalogNotifier обновляет состояние, когда вы выбираете элемент в выпадающем списке, это также может привести к перерисовке.

  3. Функция коллбэка: Методы, такие как onItemChanged в ваших выпадающих списках, вызывают setState(), что может также привести к перерисовкам. Если изменения в состоянии не влияют на UI, рассмотрите возможность пересмотра подхода.

Решения

  1. Изолируйте состояние: Попробуйте переместить управление состоянием из вашего ProductThreeShopScreen в отдельный виджет. Это позволит вам лучше контролировать, какие части UI должны перерисовываться при изменении состояния.

  2. Используйте Consumer или Selector из пакета provider: Это позволяет вашему Widget получать обновления только тогда, когда состояние, к которому он подписан, изменяется. Например, вместо использования Provider.of<AuthenticationNotifier>(context) используйте Consumer<AuthenticationNotifier>.

  3. Проверьте логику обновления в ваших коллбэках:

    • Убедитесь, что вы вызываете setState() только в случае, если данные действительно изменились. Например:
      void onChangeNumberResult(value) {
       if (numberResult != value) {
           setState(() {
               numberResult = value!;
           });
       }
      }
  4. Рассмотрите альтернативные подходы к выпадающим спискам: Попробуйте использовать виджет DropdownButton вместо отдельного выпадающего виджета, который может непредсказуемо вызывать setState().

  5. Оптимизация FutureBuilder:

    • Убедитесь, что FutureBuilder обновляется только при необходимости. Например, вместо того чтобы вызывать getProducts каждый раз, когда происходит обновление состояния, вы можете вызывать его только при изменении необходимых параметров.
  6. Методы управления состоянием: Если ваша логика становится слишком сложной для управления с помощью состояния виджетов, подумайте о других подходах управления состоянием, таких как BLoC, Redux или Riverpod, которые могут помочь лучше изолировать состояние и его изменения, минимизируя повторные рендеры.

Заключение

При разработке приложений на Flutter, управление состоянием и избегание ненужных повторных рендеров — ключевые аспекты, которые могут повлиять на производительность и взаимодействие пользователя. Применяя вышеупомянутые практики, вы сможете уменьшить вероятность перерисовки вашего виджета и повысить стабильность вашего приложения. Если у вас возникнут дополнительные вопросы, оставайтесь на связи, и мы будем рады помочь вам в решении любых затруднений.

Оцените материал
Добавить комментарий

Капча загружается...