我正在尝试从REST API加载数据并将其加载到下拉列表中。下面是实现我的下拉列表和从外部加载数据的代码。
String _selectedLocation;
FutureBuilder _dropDownMenu() {
List<String> categoryList = new List<String>();
return FutureBuilder<List<ProductCategory>>(
future: DataFetch().fetchCategoryList(
AppNavigation.getAPIUrl() + "productCategory/getAllProductCategories",
ProductCategory),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
if (snapshot.hasData) {
for (int i = 0; i < snapshot.data.length; i++) {
categoryList.add(snapshot.data[i].categoryName);
}
return DropdownButton(
hint: Text('Please choose'), // Not necessary for Option 1
value: _selectedLocation,
onChanged: (newValue) {
setState(() {
_selectedLocation = newValue;
});
},
items: categoryList.map((data) {
return DropdownMenuItem(
child: new Text(data),
value: data,
);
}).toList(),
);
} else {
return CircularProgressIndicator();
}
},
);
}
下面是上述方法的使用方式
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 200.0,
centerTitle: true,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
background: Image.asset(
"assets/images/create_sell_ad_background_2_dark2.jpg",
fit: BoxFit.fill),
title: Text("I want to sell.."),
),
actions: <Widget>[
FlatButton(
child: Text(
"Save",
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
),
onPressed: () {/* ... */},
),
]),
new SliverPadding(
padding: new EdgeInsets.all(16.0),
sliver: new SliverList(
delegate: new SliverChildListDelegate([
Container(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildInputLabel("Select the Category"),
_dropDownMenu()
],
),
),
在上面的代码中,我得到了数据,那部分就好了。我也可以将它们加载到下拉列表中。问题是,只要我从下拉菜单中选择一项,就会触发以下错误。
I/flutter ( 8467): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 8467): The following assertion was thrown building FutureBuilder<List<ProductCategory>>(dirty, state:
I/flutter ( 8467): _FutureBuilderState<List<ProductCategory>>#176ad):
I/flutter ( 8467): 'package:flutter/src/material/dropdown.dart': Failed assertion: line 560 pos 15: 'items == null ||
I/flutter ( 8467): items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) => item.value ==
I/flutter ( 8467): value).length == 1': is not true.
I/flutter ( 8467): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 8467): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 8467): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 8467): https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter ( 8467): When the exception was thrown, this was the stack:
I/flutter ( 8467): #2 new DropdownButton
I/flutter ( 8467): #3 _CreateSellingAdvertisementState._dropDownMenu.<anonymous closure>
I/flutter ( 8467): #4 _FutureBuilderState.build (package:flutter/src/widgets/async.dart)
I/flutter ( 8467): #5 StatefulElement.build
I/flutter ( 8467): #6 ComponentElement.performRebuild
I/flutter ( 8467): #7 Element.rebuild
I/flutter ( 8467): #8 BuildOwner.buildScope
I/flutter ( 8467): #9 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame
I/flutter ( 8467): #10 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback
I/flutter ( 8467): #11 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback
I/flutter ( 8467): #12 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame
I/flutter ( 8467): #13 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame
I/flutter ( 8467): #17 _invoke (dart:ui/hooks.dart:209:10)
I/flutter ( 8467): #18 _drawFrame (dart:ui/hooks.dart:168:3)
I/flutter ( 8467): (elided 5 frames from class _AssertionError and package dart:async)
I/flutter ( 8467):
我还注意到有时候,下拉列表中填充有重复的值,例如数据加载部分已经进行了两次。
我该如何解决这个问题?
答案 0 :(得分:0)
我真的很想在编写此答案之前测试您的代码,但我不能,因为获取数据请求网址,所以我决定在黑暗中拍摄。
我对您的源代码进行了一些更改,以避免多个Web请求获得下拉选项,因为您正在使用setState重建窗口小部件树,并且您的Web请求位于您的build方法中,这不是一个好主意。来源中有一些评论,因此...如果不起作用,请随时分享结果。
//This is the state of your class
/// Is a good practice initialize the selection value.
/// I'am doing this after dataFetch is completed.
String _selectedLocation;
/// we make the future object part of the state to avoid data fetching
/// from web every time that build method is called·
Future< List<ProductCategory> > _future;
/// and now we store the category list as cache in widget state
List<String> _categoryList;
initState(){
// in initState we trigger the network call for load the dropdown menu options.
// this is part of trying to avoid recall data fetching from network every time
// that we need rebuild the widget.
_future = DataFetch().fetchCategoryList(
AppNavigation.getAPIUrl() + "productCategory/getAllProductCategories",
ProductCategory);
}
Widget _dropDownMenu() {
// if we haven't load the options from network yet... we await the future
// completes to create dropdown menu.
if (_categoryList == null) {
return FutureBuilder<List<ProductCatefory>>(
future: _future,
builder: (context, snapshot) {
if (snapshot.hasError)
print(snapshot.error);
else if (snapshot.hasData) {
for (int i = 0; i < snapshot.data.length; i++)
_categoryList.add(snapshot.data[i].categoryName);
// I put this line just to grant that the initial option of
//dropdown menu has some valid value. You can erase if not needed.
_selectedLocation = snapshot.data[0].categoryName);
return _createDropDownMenu();
}
else return CircularProgressIndicator();
},);
}
// other way if we already load the options data we just create the dropdown menu
// we just populate the dropdown menu options with _categoryList values.
return _createDropDownMenu();
}
Widget _createDropDownMenu(){
return DropdownButton<String>(
hint: Text('Please choose'), // Not necessary for Option 1
value: _selectedLocation,
onChanged: (newValue) {
setState(() {
_selectedLocation = newValue;
});
},
items: _categoryList.map((data) {
return DropdownMenuItem<String>(
child: new Text(data),
value: data,
);
}).toList(),
);
}