我正在使用Flutter创建一个使用AutoCompleteTextField autocomplete_textfield: ^1.7.3
的应用程序。我对此小部件有问题。在开始之前,让我解释一下我对这个小部件的理解。
它获取“建议”列表,并为建议列表中的每个条目构建列表项。当用户在文本字段中键入内容时,它会与项目过滤器匹配并显示类似的项目。
我遇到的问题是,此建议列表未从setState()中得到更新。我有一个数据库可从中获取数据,我尝试在覆盖的initState()处异步获取数据,但是即使记录了列表的长度,建议也没有出现。类似的问题here。我觉得,一旦构建了窗口小部件,此“建议”列表就永远不会像有状态窗口小部件那样刷新。我在Google上搜索了这些内容,并找到了FutureBuilder小部件的变通办法,并且此举得以完成(目前)。我在这里粘贴了我的有效代码。
import 'package:autocomplete_textfield/autocomplete_textfield.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
class AddKeptPlaceScreen extends StatefulWidget {
static const String route = "/add_kept_place";
@override
_AddKeptPlaceScreenState createState() => _AddKeptPlaceScreenState();
}
class _AddKeptPlaceScreenState extends State<AddKeptPlaceScreen> {
bool _hasAKeptPlace = false;
bool disableKeptPlaceSwitch = false;
int _keptPlaceKeptPlaceID;
List<KeptPlaceModal> listOfKeptPlaces = new List();
TextEditingController _keptPlaceKeptPlace = new TextEditingController();
GlobalKey<AutoCompleteTextFieldState<KeptPlaceModal>> keyK =
new GlobalKey<AutoCompleteTextFieldState<KeptPlaceModal>>();
void clearAllFields(KeptPlaceModal place) {
_keptPlaceKeptPlace.text = "";
setState(() {
this._keptPlaceKeptPlaceID = null;
});
}
Widget addKeptPlace(BuildContext context, String imagePath) {
return new Scaffold(
floatingActionButton: SpeedDial(
closeManually: false,
child: Icon(Icons.image),
children: [
SpeedDialChild(
backgroundColor: Colors.blueAccent,
child: Icon(Icons.add_photo_alternate),
label: Strings.fab_pick_picture,
onTap: () {
handlePictureButton(false);
}),
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
child: Row(
children: <Widget>[
AbsorbPointer(
child: Switch(
value: this._hasAKeptPlace,
activeColor: Colors.black,
onChanged: (bool valueChanged) {
setState(() {
_keptPlaceKeptPlace.text = "";
_keptPlaceKeptPlaceID = null;
this._hasAKeptPlace = valueChanged;
});
},
),
absorbing: disableKeptPlaceSwitch,
),
Flexible(
child: AbsorbPointer(
absorbing: !_hasAKeptPlace,
child: AutoCompleteTextField<KeptPlaceModal>(
clearOnSubmit: false,
controller: _keptPlaceKeptPlace,
onFocusChanged: (changed) {
setState(() {
this.disableKeptPlaceSwitch = changed;
});
validateAutoInputs(
this.listOfKeptPlaces, _keptPlaceKeptPlace);
},
textCapitalization: TextCapitalization.words,
decoration: InputDecoration(
labelText: "Kept Place",
labelStyle: TextStyle(color: Colors.pink),
border: OutlineInputBorder()),
style: TextStyle(color: Colors.black),
itemBuilder: (context, item) {
Note here ----------------> print(listOfKeptPlaces.length);
return AutoCompleteEntry(
entryImage: item.keptPlaceImage,
entryTitle: item.keptPlaceName,
);
},
suggestionsAmount: 4,
itemFilter: (item, query) {
return item.keptPlaceName
.toLowerCase()
.contains(query.toLowerCase());
},
itemSorter: (a, b) {
return a.keptPlaceName.compareTo(b.keptPlaceName);
},
itemSubmitted: (item) {
setState(() {
_keptPlaceKeptPlace.text = item.keptPlaceName;
_keptPlaceKeptPlaceID = item.keptPlaceID;
});
},
suggestions: this.listOfKeptPlaces,
key: keyK,
),
),
),
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 5.0),
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: MediaQuery.of(context).size.width - 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
RaisedButton(
onPressed: () {
// Create a "keptPlace" item and insert into db
createNewKeptPlace(keptPlace).then((v) {
clearAllFields(keptPlace);
print("Done");
}).catchError((error) {
print(error);
});
} else {
print("Empty");
}
},
color: Colors.green,
child: Text(
"Save",
style: TextStyle(color: Colors.white),
),
),
Padding(
padding: EdgeInsets.all(5.0),
),
RaisedButton(
onPressed: () {
clearAllFields(null);
},
color: Colors.pinkAccent,
child: Text(
"Clear",
style: TextStyle(color: Colors.white),
),
),
],
)),
)
],
),
));
}
@override
Widget build(BuildContext context) {
// Future method to fetch data from database and load state variables. But
// as we are using FutureBuilder, make sure not to call setState as it will
// cause infinite loop of widget building
Future<bool> loadKeptPlacesFromDB() async {
List<KeptPlaceModal> _keptPlaceList = await getKeptPlaceList();
this.listOfKeptPlaces = _keptPlaceList;
return true;
}
Widget addKeptPlaceView() {
return new Scaffold(
appBar: new AppBar(
title: Text("Add Kept Place"),
),
body: new Container(
child: new Center(
child: addKeptPlace(context, null),
),
),
);
}
return new FutureBuilder(
future: loadKeptPlacesFromDB(),
builder: (BuildContext buildContext, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return addKeptPlaceView();
} else {
return Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
});
}
}
现在,我想动态更新“建议”列表;从同一个窗口。那就是在用户单击“保存”按钮时将一个项目添加到列表中。因此,当他再次输入而又不导航时,新项目应出现在建议列表中。我尝试了很多事情;
对于上述每个方法,我在AutoCompleteTextField构建器方法中放置了一个print(),以查看listOfKeptPlaces
中有多少个项目,并且似乎增加了,并且该项目在那里(类型和所有内容都相同),但是新的该项目未显示在建议列表中。这与我之前使我使用FutureBuilder而不是使用initState()方法填充列表的问题类似。
我在这里使用状态正确吗?我的代码或小部件中是否存在错误?感谢您提前提出的提示:)