У меня проблемы с плагином флейта GoogleMap, когда я закрываю showModalBottomSheet, получаю правильный обратный вызов с правильным контекстом.
Когда создается моя карта, вызывается onMapCreated()
, а затем FAB_DrawerMarkers()
это запустить. Он получает список объектов из БД SQFlite через провайдера, преобразует их в маркеры и затем dr aws при первом запуске приложения. У меня есть FAB, который открывает showModalBottomSheet, который используется в качестве диалогового окна настроек (охватывающих половину экрана). Когда я коснусь спутникового или обычного, я могу изменить GoogleMap MapType
, и setState()
сделает так, чтобы он мгновенно покрасил aws карту. Я также могу изменить возвращенный список объектов, отфильтрованный и т. Д. c.
Когда я закрываю showModalBottomSheet (или даже когда выбор сделан), я хочу, чтобы Карта запрашивала БД и перерисовывала маркеры. Тем не менее, я получаю контекстную ошибку, когда поставщик не может быть найден. Я попытался showModalBottomSheet .whenComplete(() => {})
и .then((value) {})
, чтобы вызвать некоторую форму callback . Однако, когда я пытаюсь вызвать _FAB_DrawMarkers();
, я обнаруживаю, что виджет еще не смонтирован, и поэтому провайдер не может быть найден.
Я привел несколько примеров состояния потока переменных с print()
ниже. Поэтому я бы хотел:
- Чтобы понять, как правильно выполнять обратный вызов после внесения изменений в настройки, заставить карту запрашивать БД (через провайдера), используя правильный контекст или время.
- Если я могу сделать это без какого-либо обратного вызова (например, вызвать маркеры отрисовки при изменении настроек), это нормально.
- Я пытался держать вещи в порядке, помещая их в отдельные файлы дротиков, и хочу убедиться, что это сделано правильно.
MAIN.dart >> SectionMain.dart >> SectionSettings - (основное состояние приложения передается дочерним элементам для возможности вызова setState ())
Main.dart:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MyAppStateContainer>(
create: (context) => MyAppStateContainer(),
child: MaterialApp(
home: Builder(
builder: (context) => SectionMain(mainAppStateRef: this)
MyAppStateContainer.dart:
class MyAppStateContainer extends ChangeNotifier {
MyAppStateContainer();
MapType _mapType = MapType.terrain;
String _someValue = "ABC";
String get getSomeValue => _someValue;
}
SectionMain.dart:
SectionSettings sectionSettings = SectionSettings();
class SectionMain extends StatefulWidget {
const SectionMain({Key key, @required this.mainAppStateRef}) : super(key: key);
final State mainAppStateRef;
@override
_SectionMain createState() => _SectionMain();
}
class _SectionMain extends State<SectionMain> {
BuildContext thisMapPageContext;
final Set<Marker> _markers = {};
GoogleMapController mapController;
MapMarkers mapMarkers = MapMarkers();
...
void callbackTest() {
print("************************ callback again");
_FAB_DrawMarkers();
}
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
//Load custom marker images
mapMarkers.init(); //load custom marker images
//trigger draw markers
_FAB_DrawMarkers();
}
@override
Widget build(BuildContext context) {
this.thisMapPageContext = context;
return Scaffold(
appBar: AppBar(
title: TextMy Map'),
backgroundColor: Colors.green[700],
),
//Put in a stack widget so can layer other widgets on top of map widget
body: Stack(
children: <Widget>[
//Builder so we can get a CTX made, and then Provider.of() finds the Provider
Builder(
builder: (context) => GoogleMap(
mapType: Provider.of<MyAppStateContainer>(context).getMapType, <--- works fine
minMaxZoomPreference: MinMaxZoomPreference(5,8),
markers: _markers,
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 6, //11.0,
),
mapToolbarEnabled: false,
myLocationEnabled: true,
onTap: (LatLng location) {
setState(() {
...
});
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
alignment: Alignment.bottomCenter,
child:
Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
Builder(
builder: (context) =>
FloatingActionButton(
child: Icon(Icons.settings, size: 36.0),
backgroundColor: Colors.green,
onPressed: () {
sectionSettings.onSheetShowContents(widget.mainAppStateRef, context);
}),
),
...
Builder(
builder: (context) =>
FloatingActionButton(
onPressed: () {
_FAB_DrawMarkers();
},
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Colors.green,
child: const Icon(Icons.pin_drop, size: 36.0),
),
),
...
])),
),
],
),
);
}
void _FAB_DrawMarkers() async {
BuildContext getContext = this.thisMapPageContext;
print("############ PROBLEM HERE:");
print(getContext);
print(this.widget);
print(this);
print(this.mounted);
print("############");
final selectedValue = Provider.of<MyAppStateContainer>(getContext, listen:false).getSomeValue; <--- works first time
db_manager.DatabaseHelper helper = db_manager.DatabaseHelper.instance;
...
}
}
void settingsClosedCallBack() {
print("CallBack");
_SectionMain().callbackTest();
}
SectionSettings.dart:
class SectionSettings {
State MainState;
Future<dynamic> onSheetShowContents(State mainAppState, BuildContext context) {
MainState = mainAppState;
return showModalBottomSheet(
//showBottomSheet(
context: context,
builder: (context) {
return ListView(
padding: EdgeInsets.all(15.0),
children: <Widget>[
ListTile(
title: Text("Map Settings"),
selected: true,
),
...
ChoiceChip(
label: Text("Normal Map"),
selected: Provider.of<MyAppStateContainer>(context,listen: false).getMapType == MapType.terrain,
onSelected: (value) {
Provider.of<MyAppStateContainer>(context,listen: false).setMapType(MapType.terrain);
MainState.setState(() {
});
},
),
ChoiceChip(
label: Text("Satellite Map"),
selected: Provider.of<MyAppStateContainer>(context,listen: false).getMapType == MapType.satellite,
onSelected: (value) {
Provider.of<MyAppStateContainer>(context, listen: false).setMapType(MapType.satellite);
MainState.setState(() {
});
},
),
...
],
);
//}).whenComplete(() => {
}).then((value) {
//TODO Callback...
//TODO - 1 - Try giving the right context
//TODO - 2 - Try updating the map when items are changed, not during close. The widget seems to be unmounted
//callback to trigger redraw if changed/dirty
//print("*** ShowBottomSheetClosed")
//mainAppState.
//context
//mainAppState.setState(() { })
//MainState.setState(() { reloadMapMarkers(ctx); });
settingsClosedCallBack();
//mainAppState.setState(() { settingsClosedCallBack(); })
}
);
}
}
Отладочные выходы:
void _FAB_DrawMarkers() async {
BuildContext getContext = this.thisMapPageContext;
print("############ PROBLEM HERE:");
print(getContext);
print(this.widget);
print(this);
print(this.mounted);
print("############");
_FAB_DrawMarkers()
- Первый прогон, вызванный после onMapCreated()
:
I/flutter (11115): ############ CONTEXT:
I/flutter (11115): SectionMain(state: _SectionMain#d8dda)
I/flutter (11115): SectionMain
I/flutter (11115): _SectionMain#d8dda
I/flutter (11115): true
I/flutter (11115): ############
_FAB_DrawMarkers()
- Второй прогон, после попытки обратного вызова из закрытия BottomSheet:
I/flutter (11115): ############ CONTEXT:
I/flutter (11115): null
I/flutter (11115): null
I/flutter (11115): _SectionMain#2e084(lifecycle state: created, no widget, not mounted)
I/flutter (11115): false
I/flutter (11115): ############
Основная ошибка, которую я получаю это:
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'owner' was called on null.
E/flutter (11115): Receiver: null
E/flutter (11115): Tried calling: owner
E/flutter (11115): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
E/flutter (11115): #1 Provider.of (package:provider/src/provider.dart:193:15)
E/flutter (11115): #2 _SectionMain._FAB_DrawMarkers (package:flutter_app/section_main.dart:255:38)
E/flutter (11115): #3 _SectionMain.callbackTest (package:flutter_app/section_main.dart:60:5)
E/flutter (11115): #4 settingsClosedCallBack (package:flutter_app/section_main.dart:344:18)
E/flutter (11115): #5 SectionSettings.onSheetShowContents.<anonymous closure>
, где ошибка, конечно, указывает на строку поставщика: final selectedValue = Provider.of<MyAppStateContainer>(getContext, listen:false).getSomeValue;
Я надеялся, что whenCompleted()
или then()
из нижней таблицы будет гарантировать, что ма в виджете был смонтирован до вызова этих функций. Возможно, проблема в том, что я звоню им, когда лист закрывается, но до того, как основной виджет сможет перемонтировать.
Я довольно новичок во Флаттере, поэтому большое спасибо за совет, который вы можете предложить здесь , Я уверен, что это связано с тем, что я неправильно передаю контекст или не развиваюсь корректно декларативно во Flutter.
Большое спасибо, J