Вызвать класс состояния из виджета во флаттере - PullRequest
0 голосов
/ 20 мая 2019

Я думаю, что у меня ошибочная архитектура, но я пытаюсь понять, как и почему.Я очень новичок, чтобы трепетать, поэтому терпите меня, пожалуйста.

У меня есть карта и ящик.Я загружаю список координат в ящик, и я хотел бы сделать что-то на карте, как только я нажму одну из этих координат.

Так что моя проблема в том, что я не знаю, как позвонитьгде должен быть чистый код И работает.Конечно, я мог бы просто раскрыть все для всех, но это не решило бы главную проблему: я понимаю

Моя карта нарисована там же, где и мой ящик, так что там я уже думаю, что я обманываю, но я думаю ничего страшного.Если честно, я даже не уверен, что эта часть действительно верна.

Рисуем карту:

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  var _map = MapView();
  var _stationService = StationService();

  ....

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: 4,
        child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(
              tabs: [
                Tab(icon: Icon(Icons.directions_car)),
                Tab(icon: Icon(Icons.map)),
                Tab(icon: Icon(Icons.directions_transit)),
                Tab(icon: Icon(Icons.directions_bike))
              ],
            ),
            title: Text('Floctta Plus'),
          ),
          drawer: _drawer(),
          body: TabBarView(
            physics: NeverScrollableScrollPhysics(),
            children: [
              IncrementView(),
              _map,
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike)
            ],
          ),
        ),
      ),
    );
  }

В моем коде ящика:

ListView.builder(
  itemCount: snapshot.data.length,
  itemBuilder: (context, position) {
    return ListTile(
       title: new Text(snapshot.data[position].titleFR),
       leading: new Icon(Icons.pin_drop),
       onTap: () {
         _map.GoToStation();     <<= Here, calling the MapView class
       },
    );
  },
),

ВсеКод MapView.dart

class MapView extends StatefulWidget {
  MapView({Key key}) : super(key: key);


  void GoToStation() {
    print("I'm reaching this point with success!");
  }

  @override
  _MapViewState createState() => _MapViewState();
}

class _MapViewState extends State<MapView> {
  CameraPosition _initialPosition =
      CameraPosition(target: LatLng(50.8267018, 4.3532732), zoom: 10.0);
  Completer<GoogleMapController> _controller = Completer();

  var _markers;

  void AddMarkers(List<Station> stations) {
    setState(() {
      _markers = new List.generate(
          stations.length,
              (index) => Marker(
            markerId: MarkerId(index.toString()),
            position: new LatLng(double.parse(stations[index].latitude),
                double.parse(stations[index].longitude)),
            infoWindow: InfoWindow(
              title: stations[index].titleFR,
              snippet: stations[index].city,
            ),
            icon: BitmapDescriptor.defaultMarker,
          ));
    });
  }

  void _onMapCreated(GoogleMapController controller) {
    _controller.complete(controller);
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        GoogleMap(
          onMapCreated: _onMapCreated,
          initialCameraPosition: _initialPosition,
          markers: _markers,
          myLocationEnabled: true,
        ),
      ],
    );
    // );
  }
}

Я думаю, что кода достаточно.

Мой вопрос:

Я сейчас в моем классе Mapview, и я хотел бы позвонить AddMarkers, который находится в моем классе State.Но я не могу.Я не совсем уверен, как мне поступить.

Должен ли я вызывать класс состояний прямо из ящика?Если так, то как?Должен ли я вызвать состояние из класса представления?Если так, то как?Должен ли я делать что-то еще полностью?Если да, то что?

1 Ответ

2 голосов
/ 20 мая 2019

Правильно!

Я предполагаю, что MapView и Drawer находятся в разных поддеревьях виджетов в Scaffold.Один из них - drawer, другой - где-то под body.

Насколько я понимаю, когда кто-то нажимает на ListTile в Drawer, MapView должен обновляться.

В общем,принцип, который вы хотите использовать в этом случае, заключается во введении стороннего объекта для посредничества между двумя компонентами.

AWidget <--> SState <--> MapView

Наиболее опрометчивым подходом было бы введение глобальной переменной (пожалуйста,никогда не делаю, просто упомянув об этом, чтобы вы поняли).Так что болтовня между ListTiles и вашим MapView должна происходить через этот объект-посредник.

Давайте назовем этот объект SState.(Двойной СС предназначен для дифференциации от Государственного класса Флаттера).Это Государство будет отвечать за отслеживание параметров построения карты.(Я на самом деле не знаком с пониманием вашего приложения, так что оно полностью составлено)

Итак, эта косвенность обработки действий будет означать:

tap on ListTile in drawer -> update SState object -> let Flutter know stuff happened and should update those widgets that are sensitive to that change (MapView)

Обновление объекта SStateпросто, это просто вызов функции / вызов установщика.Но как вы дадите Flutter знать, что он должен обновить MapView?И где вы должны разместить этот экземпляр объекта SState, чтобы оба могли получить его без глобальной переменной?

Чтобы ответить на это, существует несколько решений: scoped_model, InheritedWidget, Provider, BLoC pattern.Общим во всем этом является то, что они используют принцип, называемый lifting state up.По сути, это означает, что SState объект должен быть создан в дереве над ними обоими.

Пример провайдера:

class SState with ChangeNotifier {

  String _someMapParam;
  String get someMapParam => _someMapParam;
  set someMapParam(String val) {
    _someMapParam = val;
    if(hasListeners) notifyListeners();
  }
}

// [...] somewhere in your app

Widget build(BuildContext context) {
  return ChangeNotifierProvider<SState>(
    builder: (ctx) => new SState(),
    child: Scaffold(
      drawer: _drawer(),
      body: Consumer<SState>(
        builder: (_, sStateInstance, __) => Text(sStateInstance.someMapParam),
      ),
    ),
  );
}

Подробнее о провайдере: https://pub.dev/packages/provider

...