I want to update dropdown items with setState when data received from api. Dropdown items not update even though widget is rebuilt. I can only get updated dropdown items after unfocus and focus. Please point me out if something wrong in my code..
Dropdown
import 'dart:async';
import 'package:eas/app/core/theme/colors.dart';
import 'package:eas/app/core/theme/theme.dart';
import 'package:eas/app/core/widgets/form_field_input.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
class AppAsyncDropdown extends StatefulWidget {
final double width;
final String hint;
final bool loading;
final List<DropdownMenuItem> items;
final Function(String search) onChanged;
const AppAsyncDropdown({
Key? key,
required this.width,
required this.hint,
required this.loading,
required this.items,
required this.onChanged,
}) : super(key: key);
@override
State<AppAsyncDropdown> createState() => _AppAsyncDropdownState();
}
class _AppAsyncDropdownState extends State<AppAsyncDropdown> {
Timer? _timer;
void debounceSearch(String search) {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(
const Duration(milliseconds: 400), (() => widget.onChanged(search)));
}
@override
void dispose() {
if (_timer != null) {
_timer!.cancel();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
print('why.. ${widget.loading} - ${widget.items}');
final items = [
DropdownMenuItem(
enabled: false,
child: FormFieldInput(
controller: TextEditingController(),
width: widget.width,
hintText: 'search product',
onChanged: (value) => debounceSearch(value),
),
),
];
if (widget.loading) {
items.add(
DropdownMenuItem(
enabled: false,
child: SizedBox(
width: widget.width,
child: Center(
child: Lottie.asset(
'assets/animation/search_loading.json',
width: 150,
)),
),
),
);
}
if (widget.items.isNotEmpty) {
items.addAll(widget.items);
}
return SizedBox(
width: widget.width,
child: DropdownButtonFormField<dynamic>(
isExpanded: true,
decoration: InputDecoration(
hintText: widget.hint,
hintStyle: AppTheme.of(context).subtitle4,
border: const UnderlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(color: AppColors.primary),
),
),
items: items,
onChanged: (dynamic e) {
print('hii $e');
},
),
);
}
}
I added debounce to prevent requesting on every keystroke.And by debugging with print and some ui changes, it is verified that states are updated..
Script That Update State
import 'dart:async';
import 'package:eas/app/core/layout/desktop/content_desktop.dart';
import 'package:eas/app/core/layout/desktop/header_desktop.dart';
import 'package:eas/app/core/layout/desktop/layout_desktop.dart';
import 'package:eas/app/core/theme/colors.dart';
import 'package:eas/app/core/theme/miscs.dart';
import 'package:eas/app/core/theme/sizedBoxes.dart';
import 'package:eas/app/core/theme/sizes.dart';
import 'package:eas/app/core/theme/theme.dart';
import 'package:eas/app/core/widgets/button.dart';
import 'package:eas/app/core/widgets/dropdown.dart';
import 'package:eas/app/core/widgets/dynamic_list.dart';
import 'package:eas/app/routes/app_pages.dart';
import 'package:flutter/material.dart';
class TransferStockForm extends StatefulWidget {
const TransferStockForm({Key? key}) : super(key: key);
@override
State<TransferStockForm> createState() => _TransferStockFormState();
}
class _TransferStockFormState extends State<TransferStockForm> {
Timer? _timer;
List<DropdownMenuItem> _branchWarehouses = [];
bool _isLoading = false;
void updateBranchWarehouse(String search) {
print('searched - $search');
setState(() {
_isLoading = true;
_branchWarehouses = [];
});
_timer = Timer(
const Duration(seconds: 2),
() {
setState(() {
_isLoading = false;
_branchWarehouses = const [
DropdownMenuItem(
value: '1',
child: Text('one'),
),
DropdownMenuItem(
value: '2',
child: Text('two'),
),
DropdownMenuItem(
value: '3',
child: Text('three'),
),
];
});
},
);
}
@override
void dispose() {
if (_timer != null) {
_timer!.cancel();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: AppDesktopLayout(
header: const AppDesktopHeader(
title: "Transfer Stock",
pageName: AppRoutes.transferStockForm,
isFavourable: true,
),
currentNavi: AppRoutes.home,
content: AppDesktopContent(
child: Container(
padding: const EdgeInsets.all(AppSizes.md),
child: Container(
padding: const EdgeInsets.all(AppSizes.md),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(
Radius.circular(AppSizes.sm),
),
boxShadow: [
AppThemeMiscs.shadow2,
],
),
child: LayoutBuilder(builder: (context, constraint) {
return Row(
children: [
SizedBox(
width: constraint.maxWidth * 0.35,
height: constraint.maxHeight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'To',
style: AppTheme.of(context).headline3,
),
AppSizeBoxes.sm,
AppAsyncDropdown(
width: constraint.maxWidth * 0.35,
hint: 'select branch or warehouse to transfer to',
loading: _isLoading,
items: _branchWarehouses,
onChanged: (String search) =>
updateBranchWarehouse(search),
),
AppSizeBoxes.md,
Button(onPressed: () {}, text: 'Transfer'),
],
),
),
const Expanded(
child: DynamicList(
type: 'stock',
)),
],
);
}),
),
),
)),
);
}
}
faked api response with timer