在颤动中单击子项/小部件时,小部件会再次重新加载

问题描述 投票:0回答:1

我有一个有状态的小部件,每当我单击任何子项(例如 CustomDropDownButtonFormField 及其打开的下拉菜单)时,即使在选择下拉菜单中的任何选项其调用 Widget 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 dart future flutter-futurebuilder statefulwidget
1个回答
0
投票

您面临的问题可能是由于与 CustomDropDownButtonFormField 交互时构建方法运行过于频繁。发生这种情况是因为 Flutter 在状态更改时重建小部件,尤其是下拉列表或文本字段。

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

  final CategoryCatalogModel childCategoryCatalogModel;

  @override
  State<ProductThreeShopScreen> createState() => _ProductThreeShopScreenState();
}

class _ProductThreeShopScreenState extends State<ProductThreeShopScreen> {
  final TextEditingController nameDescSearchController = TextEditingController();
  Timer? _timer;
  bool viewOutOfAssortment = false;
  bool readImageData = true;
  String numberResult = '10';
  String orderBy = 'NameProduct';
  Icon searchIcon = const Icon(Icons.search);

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

  @override
  void dispose() {
    _timer?.cancel();
    nameDescSearchController.dispose();
    super.dispose();
  }

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

    return SafeArea(
      child: Scaffold(
        backgroundColor: Theme.of(context).colorScheme.background,
        appBar: AppBar(
          centerTitle: true,
          title: Text(
            widget.childCategoryCatalogModel.nameCategory,
            style: Theme.of(context).textTheme.headlineLarge,
          ),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              Row(
                children: [
                  _buildDropdown(
                    label: 'Mostra numero risultati',
                    value: numberResult,
                    items: ['All', '10', '25', '50'],
                    onChanged: (value) => setState(() => numberResult = value),
                  ),
                  _buildDropdown(
                    label: 'Ordinamento',
                    value: orderBy,
                    items: ['NameProduct', 'DescriptionProduct', 'DisplayOrder'],
                    onChanged: (value) => setState(() => orderBy = value),
                  ),
                  _buildCheckbox('Fuori assortimento', viewOutOfAssortment, (value) => setState(() => viewOutOfAssortment = value)),
                  _buildCheckbox('Visualizza immagine', readImageData, (value) => setState(() => readImageData = value)),
                ],
              ),
              _buildSearchField(),
              SizedBox(
                height: MediaQuery.of(context).size.height,
                width: MediaQuery.of(context).size.width,
                child: Consumer<ProductCatalogNotifier>(
                  builder: (context, productCatalogNotifier, _) {
                    return FutureBuilder<List<ProductCatalogModel>>(
                      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,
                        viewOutOfAssortment: viewOutOfAssortment,
                      ),
                      builder: (context, snapshot) {
                        if (snapshot.connectionState == ConnectionState.waiting) {
                          return const Center(child: CircularProgressIndicator());
                        } else if (!snapshot.hasData) {
                          return const Center(child: Text('No data available'));
                        } else {
                          final productList = snapshot.data!;
                          return _buildProductGrid(productList);
                        }
                      },
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildDropdown({
    required String label,
    required String value,
    required List<String> items,
    required Function(String?) onChanged,
  }) {
    return Expanded(
      child: DropdownButtonFormField<String>(
        value: value,
        decoration: InputDecoration(labelText: label),
        items: items.map((e) => DropdownMenuItem(value: e, child: Text(e))).toList(),
        onChanged: onChanged,
      ),
    );
  }

  Widget _buildCheckbox(String title, bool value, Function(bool?) onChanged) {
    return Expanded(
      child: CheckboxListTile(
        title: Text(title),
        value: value,
        onChanged: onChanged,
      ),
    );
  }

  Widget _buildSearchField() {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: TextFormField(
        controller: nameDescSearchController,
        decoration: InputDecoration(
          labelText: 'Ricerca per nome, descrizione o barcode',
          suffixIcon: IconButton(
            icon: searchIcon,
            onPressed: () {
              setState(() {
                if (searchIcon.icon == Icons.search) {
                  searchIcon = const Icon(Icons.cancel);
                } else {
                  searchIcon = const Icon(Icons.search);
                  nameDescSearchController.clear();
                }
              });
            },
          ),
        ),
        onChanged: (value) {
          _timer?.cancel();
          _timer = Timer(const Duration(seconds: 1), () {
            setState(() {
              searchIcon = value.isEmpty ? const Icon(Icons.search) : const Icon(Icons.cancel);
            });
          });
        },
      ),
    );
  }

  Widget _buildProductGrid(List<ProductCatalogModel> productList) {
    return GridView.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        crossAxisSpacing: 10,
        mainAxisSpacing: 10,
      ),
      itemCount: productList.length,
      itemBuilder: (context, index) {
        final product = productList[index];
        return Card(
          child: Column(
            children: [
              // Display product details, including image if available
              Text(product.nameProduct),
            ],
          ),
        );
      },
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.