Унаследованный виджет перестраивает весь виджет Google Maps
Создание приложения с Flutter и до сих пор оно мне нравилось, но меня раздражает неприятная проблема.
У меня есть "основной" Stateful 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)); }
Затем, каждый раз, когда значение в строке поиска изменяется, он перестраивает весь виджет, и это очень неэффективно, с неэстетичным черным экраном при перестроении. Более того, он работает один раз в несколько раз: в большинстве случаев виджет даже не перестраивается, что делает результаты поиска неправильными.
Одна странная вещь заключается в том, что когда я запускаю приложение в режиме отладки и устанавливаю точку останова в методе "build" виджета карты, он работает как положено.
Что я хотел бы сделать, это когда список изменится в главном виджете: -Если мы находимся в виджете списка, перестройте -Если мы находимся на виджете карт 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,
),
),
);
}
}