使用 Flutter 构建应用程序并喜欢它,但一个烦人的问题困扰着我。
我有一个“主”状态小部件,其中包含属性中的商店列表、搜索栏、一个“子”小部件,它可以是地图或列表,我们可以通过单击按钮在视图之间切换。有一个 Inherited 小部件包裹在主小部件内,因此我们可以获得主小部件的状态,从而获得列表。
我在搜索栏上有一个监听器:
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 地图小部件上:触发一个函数,该函数将擦除所有标记并重新添加它们给定新列表,无需重建整个小部件。
我确信有办法做到这一点,但我已经尝试了几天,但我找不到办法。也许有人可以帮助我?非常感谢 !:)
编辑:这是我的代码:
继承的小部件
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,
),
),
);
}
}