我有一个有状态的小部件,每当我单击任何子项(例如 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);
});
}
},
),
);
},
),
),
],
),
),
));
}
}
您面临的问题可能是由于与 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),
],
),
);
},
);
}
}