Унаследованный виджет перестраивает весь виджет 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,
        ),
      ),
    );
  }

}

0 ответов

Другие вопросы по тегам