到目前为止,使用Flutter构建应用程序并喜欢它,但是一个烦人的问题困扰着我。
我有一个“主要” Stateful Widget,带有属性的商店列表,搜索栏,一个“子” Widget(可以是地图或列表),我们可以通过单击按钮在视图之间切换。在主窗口小部件中包装了一个继承的窗口小部件,因此我们可以获取主窗口小部件的状态,从而获得列表的状态。
我在搜索栏上有一个侦听器:
void updateNameFilter(){
setState(() {
FiltersService.nameSearch=_searchTextController.text;
});}
它与列表小部件(具有FutureBuilder)一起完美平滑地工作,我们可以在其中过滤列表。
child: TruckList(snapshot.data.where((Truck t)=>FiltersService.shouldDisplay(t)).toList(), _handleRefresh),
但是,对于地图小部件,我们使用Google Maps插件。在创建地图时,我们在地图上为应该显示的列表的每个项目添加一个标记:
void _onMapCreated(GoogleMapController controller) async {
mapController = controller;
//Get the list of trucks from parent widget
List<Truck> list= await TrucksMainWidget.of(context).trucksList;
//Iterate on all trucks to add them on the map
list.where((Truck t) => FiltersService.shouldDisplay(t)).forEach((Truck t) => _addTruckMarker(t)); }
然后,每次搜索栏中的值更改时,它都会重建整个窗口小部件,并且效率很低,在重建时会出现不美观的黑屏。而且,它几次只能工作一次:在大多数情况下,小部件甚至都没有重建,从而使搜索结果错误。
一个奇怪的事情是,当我以调试模式启动该应用程序并在地图小部件的“构建”方法上设置一个断点时,它会按预期运行。
我想做的是,当主窗口小部件中的列表更改时: -如果我们在列表小部件上,请重建 -如果我们在Google Maps窗口小部件上:触发一个函数,该函数将删除所有标记并在给定新列表的情况下重新添加它们,而无需重建整个窗口小部件。
我肯定有办法做到这一点,但是我已经尝试了几天,但找不到方法。也许有人可以帮助我?非常感谢 ! :)
编辑:这是我的代码:
继承的窗口小部件
class _TrucksInherited extends InheritedWidget {
_TrucksInherited({
Key key,
@required Widget child,
@required this.data,
}) : super(key: key, child: child);
final _TrucksMainWidgetState data;
@override
bool updateShouldNotify(_TrucksInherited oldWidget) {
return true;
}
}
class TrucksMainWidget extends StatefulWidget {
final Widget child;
TrucksMainWidget({this.child});
@override
_TrucksMainWidgetState createState() => _TrucksMainWidgetState();
static _TrucksMainWidgetState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_TrucksInherited)
as _TrucksInherited)
.data;
}
}
class _TrucksMainWidgetState extends State<TrucksMainWidget> {
final Icon _mapIcon = new Icon(
FontAwesomeIcons.map,
color: Color(0xFF666666),
);
final Icon _listIcon = new Icon(
FontAwesomeIcons.thLarge,
color: Color(0xFF666666)
);
Widget _trucksListWidget;
Widget _trucksMapWidget;
TextEditingController _searchTextController;
Icon _switchIcon;
Widget _widgetView;
Future<List<Truck>> _trucksList;
@override
void initState() {
super.initState();
_trucksList = TruckService.getAll();
_trucksListWidget = new TrucksListWidget();
_trucksMapWidget = new TrucksMapWidget();
_searchTextController = new TextEditingController();
_searchTextController.addListener(updateNameFilter);
_switchIcon = _mapIcon;
_widgetView = _trucksListWidget;
}
Future<List<Truck>> get trucksList => _trucksList;
@override
void dispose(){
_searchTextController.removeListener(updateNameFilter);
_searchTextController.dispose();
super.dispose();
}
void updateNameFilter(){
setState(() {
FiltersService.nameSearch=_searchTextController.text;
});
}
void refresh() {
setState(() {
_trucksList = TruckService.getAll();
});
}
void _onSwitchView() {
setState(() {
if (_switchIcon.icon == FontAwesomeIcons.map) {
_switchIcon = _listIcon;
_widgetView = _trucksMapWidget;
} else {
_switchIcon = _mapIcon;
_widgetView = _trucksListWidget;
}
});
}
_openFiltersDialog() async {
bool shouldUpdate = await showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return FiltersAlertBox();
},
);
if (shouldUpdate != null && shouldUpdate) {
setState(() {});
}
}
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
backgroundColor: Colors.white,
title: Padding(
padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
child: Directionality(
textDirection: Directionality.of(context),
child: TextField(
style: new TextStyle(color: Colors.black, fontSize: 16.0),
decoration: InputDecoration(
hintText: 'Recherche',
hintStyle: new TextStyle(color: Color(0xFF999999)),
fillColor: Color(0xFFF6F6F6),
filled: true,
border: InputBorder.none,
prefixIcon: Icon(Icons.search, color: Color(0xFF999999))),
autofocus: false,
controller: _searchTextController,
)),
),
actions: <Widget>[
IconButton(
icon: Icon(
FontAwesomeIcons.slidersH,
color: Color(0xFF666666),
),
onPressed: _openFiltersDialog,
),
IconButton(
icon: _switchIcon,
onPressed: _onSwitchView,
)
],
),
body: _TrucksInherited(
data: this,
child: _widgetView,
));
}
}
列表小部件(确定)
class TrucksListWidget extends StatefulWidget {
TrucksListWidget();
_TrucksListWidgetState createState() => new _TrucksListWidgetState();
}
class _TrucksListWidgetState extends State<TrucksListWidget>{
Future<List<Truck>> _handleRefresh() async{
final state = TrucksMainWidget.of(context);
state.refresh();
return state.trucksList;
}
Widget build(BuildContext context){
final state = TrucksMainWidget.of(context);
return FutureBuilder<List<Truck>>(
future: state.trucksList,
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.hasData) {
return RefreshIndicator(
onRefresh: _handleRefresh,
child: TruckList(snapshot.data.where((Truck t)=>FiltersService.shouldDisplay(t)).toList(), _handleRefresh),
);
}else if(snapshot.hasError){
return Center(
child: Text('${snapshot.error}'),
);
}
return Center(
child: CircularProgressIndicator(),
);
}
);
}
}
地图小部件(KO)
class TrucksMapWidget extends StatefulWidget {
_TrucksMapWidgetState createState() => new _TrucksMapWidgetState();
}
class _TrucksMapWidgetState extends State<TrucksMapWidget> {
Future<Position> userPosition;
GoogleMapController mapController;
Map<Marker, Truck> allMarkers = {};
StreamSubscription subscription;
void initState(){
super.initState();
initPosition();
subscription = LocationService.ctrl.stream.listen((Future<Position> p)=>userPosition=p); //Listening LocationService's stream for a change of the user position
}
void dispose(){
super.dispose();
subscription.cancel();
}
void initPosition() async{
userPosition = LocationService.getUserPosition();
}
void _onMapCreated(GoogleMapController controller) async {
mapController = controller;
//Get the list of trucks from parent widget
List<Truck> list= await TrucksMainWidget.of(context).trucksList;
//Iterate on all trucks to add them on the map
list.where((Truck t) => FiltersService.shouldDisplay(t)).forEach((Truck t) => _addTruckMarker(t));
}
void _addTruckMarker(Truck truck) async {
Marker marker = await mapController.addMarker(MarkerOptions(
icon: BitmapDescriptor.fromAsset(
truck.situation.isOpen ? 'assets/images/open_marker.png' : 'assets/images/close_marker.png',
),
infoWindowText: InfoWindowText(truck.name, truck.situation.address),
position: LatLng(truck.situation.position.lat,
truck.situation.position.long),
consumeTapEvents: true,
));
allMarkers[marker] = truck; //Linking the marker to its truck in the Map
}
Widget build(BuildContext context) {
return FutureBuilder(
future:userPosition,
builder: (context, snapshot){
if(snapshot.connectionState==ConnectionState.done){
return _getMap(snapshot.data);
}else{
return Center(
child : CircularProgressIndicator(),
);
}
},
);
}
Widget _getMap(Position pos){
return GoogleMap(
onMapCreated: _onMapCreated,
options: GoogleMapOptions(
myLocationEnabled: true,
cameraPosition: CameraPosition(
target: LatLng(pos.lat, pos.long), //Starting position at user
zoom: 8.0,
),
),
);
}
}