So I have this 2 classes
class MapsModel {
MapsModel._();
...
static void Function(VoidCallback) setState;
static GoogleMap googleMap = new GoogleMap(
markers: markers,
onMapCreated: (GoogleMapController controller) {
setState((){
log("Marker added");
markers.add(
Marker(
markerId: MarkerId("<MARKER_ID>"),
position: LatLng(45.726697, 25.521418)
)
);
});
},
);
}
and
class MapPage extends StatefulWidget {
MapPage({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() => MapPageState();
}
class MapPageState extends State<MapPage> {
#override
Widget build(BuildContext context) {
MapsModel.setState = (VoidCallback callback){
setState(() {
callback();
});
};
return Scaffold(
body: MapsModel.googleMap
);
}
}
So what I want to do is to update the map markers from the first class, using a custom setState function.
The problem is that even the message: Marker Added is shown, the map does not add the marker. Also when I change to another page and back, the marker is added.
Related
When i add value to an obs list in GetX controller inside a function, it shows the length that the data is successfully added. But when i call the variable in another function, the list is still empty.
class ExampleController extends GetxController {
var dataList = <dynamic>[].obs;
void setImages(items) {
dataList.addAll(items);
log(dataList.length.toString()); // shows the data length after added items
}
void onButtonPressed() {
log(dataList.length.toString()); // shows 0 length
}
}
I put the controller like this ..
void main {
runApp(const myApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Get.put(ExampleController());
return GetMaterialApp(
title: 'GetX Example',
debugShowCheckedModeBanner: false,
home: const SplashScreen(),
);
}
}
And this is how i instantiate the controller..
class DataPage extends StatefulWidget {
const DataPage({Key? key}) : super(key: key);
#override
State<DataPage> createState() => _DataPageState();
}
class _DataPageState extends State<DataPage> {
final _exampleController = Get.find<ExampleController>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: CustomButtonIcon(
label: 'Get Data',
onPressed: () => _exampleController.onButtonPressed(),
icon: Icons.arrow_back_rounded,
),
)),
);
}
}
You have declared the setImages() function . But never called .
So the List obs variable in Controller is empty.
To work as expected.
Before the onButtonPressed() function call the setImages() function
void setImages(items) {
dataList.addAll(items);
log(dataList.length.toString()); // shows the data length after added items
update(); // use this
}
void onButtonPressed() {
Future.delayed(const Duration(milliseconds: 500), () {
log(dataList.length.toString()); // shows 0 length
});
}
I'm working with the google map widget, when I render the map for the first time with static markers it displays all the markers, but when I get the data from API and try to add new markers to the map, it won't display them.
below the code that i'm using :
class Map extends StatefulWidget {
final Position initialPosition;
final List<Marker> allSitesMarkers;
final List<Site> placesSite;
final List<String> mapMarkersCategories;
const Map(this.initialPosition, this.allSitesMarkers, this.placesSite,
this.mapMarkersCategories,
{Key key})
: super(key: key);
#override
State<StatefulWidget> createState() => MapState();
}
class MapState extends State<Map> {
Completer<GoogleMapController> _controller = Completer();
String currentCategoryFilter;
bool filtred = false;
Set<Marker> _markers = Set();
List<Marker> markersToShow;
#override
void initState() {
super.initState();
refreshMarkers();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: (widget.placesSite.isNotEmpty)
? Stack(
children: [
GoogleMap(
initialCameraPosition: const CameraPosition(
target: LatLng(36.798, 10.1717), zoom: 15),
mapType: MapType.normal,
myLocationEnabled: true,
markers: _markers,
onMapCreated: (GoogleMapController controller) {
controller.setMapStyle(
'[ //... ]');
_controller.complete(controller);
},
),
_chips()
],
)
: const NoListMap(),
);
}
Widget _chips() {
//..
}
filterSites(String category) {
setState(() {
_markers.clear();
filtred = true;
markersToShow = widget.allSitesMarkers
.where((element) => element.infoWindow.title == category)
.toList();
});
refreshMarkers();
}
refreshMarkers() {
if (filtred) {
setState(() {
_markers.clear();
_markers.addAll(markersToShow);
});
} else {
setState(() {
_markers.clear();
_markers.addAll(widget.allSitesMarkers);
});
}
}
}
I'm using:
Flutter 2.10.2
Dart 2.16.1
google_maps_flutter : 2.0.9
Your _markers is a Set, and in setState you use clear and addAll to empty it and to add new items to the set. But the value of _markers does not change when you do so, therefore it will not trigger a rebuild.
This happens because _markers holds a reference to the Set (like lists, objects etc.).
You need to assign a new value to _markers to trigger a rebuild, for example:
setState(() {
_markers = markersToShow.toSet();
});
This will actually change the reference kept in _markers.
I would like to call a fonction when my ExpandablePanel is expanded, with ExpansionTile I do this with onExpansionChanged but here I don't want to use ExpansionTile,
Doesn't anyone have a solution ?
Thanks.
Use an ExpandableControllerand an ExpandableNotifier:
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => _MyWidget();
}
class _MyWidget extends State<MyWidget> {
final ExpandableController expandableController = ExpandableController();
void onExpandableControllerStateChanged() {
if (expandableController.expanded) {
// Do your stuff when panel got expanded here
} else {
// Do your stuff when panel got collapsed here
}
}
#override
void initState() {
super.initState();
expandableController.addListener(onExpandableControllerStateChanged);
}
#override
void dispose() {
expandableController.removeListener(onExpandableControllerStateChanged);
super.dispose();
}
#override
Widget build(BuildContext context) {
return ExpandableNotifier(
controller: expandableController,
child: ExpandablePanel(
header: HeaderWidget(),
collapsed: CollapsedWidget(),
expanded: ExpandedWidget(),
),
);
}
}
You can put the ExpansionPanel inside an ExpansionPanelList and inside it will have a property called expansionCallback
You can wrap ExpansionPanel with ExpansionPanelList, so that you can access to a callback function named expansionCallback. Take a look at the snippet below:
ExpansionPanelList(
animationDuration: const Duration(milliseconds:1000),
children: [
ExpansionPanel(), //..your expansionPanel here
],
expansionCallback: (int item, bool status) {
//status is what you're looking for
},
),
I'm using VoidCallback to call setState from child widget. But the callback function is not executing at all.
Below is my code in essence.
Child Widget:
class TextFormMentorHashtag extends StatefulWidget {
const TextFormMentorHashtag({Key? key, required this.callback}) : super(key: key);
final VoidCallback callback;
#override
_TextFormMentorHashtagState createState() =>
_TextFormMentorHashtagState();
}
class _TextFormMentorHashtagState extends State<TextFormMentorHashtag> {
#override
Widget build(BuildContext context) {
...
TextFormField(
...
onChanged: (value) {
if(_formKey.currentState!.validate()) {
...
print('call callback');
widget.callback;
}
}
)
}
}
parent widget:
class ApplyMentorPage extends StatefulWidget {
const ApplyMentorPage({Key? key}) : super(key: key);
#override
State<ApplyMentorPage> createState() => _ApplyMentorPageState();
}
class _ApplyMentorPageState extends State<ApplyMentorPage> {
...
bool _disabled = true;
#override
Widget build(BuildContext context) {
void callback() {
print('callback called');
setState(() {
_disabled = !_mentorProvider.check();
});
}
return Scaffold(
...
child: Column(
children: [
TextFormMentorHashtag(callback: callback),
...
// _disabled turns submit button on and off in the later code
When I run code and press child widget terminal prints 'call callback' but no 'callback called'. What am I missing?
You didn't call callback in your _TextFormMentorHashtagState. You should call it like widget.callback() or widget.callback.invoke().
Full code:
class _TextFormMentorHashtagState extends State<TextFormMentorHashtag> {
#override
Widget build(BuildContext context) {
...
TextFormField(
...
onChanged: (value) {
if(_formKey.currentState!.validate()) {
...
print('call callback');
// or widget.callback()
widget.callback.invoke();
}
}
)
}
}
If your using TextFormField it has also ontap on it
TextFormField(
onTap: () {
print("am called");
}
)
so you will is maybe like this
TextFormField(
onTap: widget.calback
)
I am newbie in flutter, I am trying to set my location as the center point of the map that the app draw on the screen
I am using statefulwidget as the root of my app and add this code :
class MyMap extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyMapState();
}
}
class MyMapState extends State<MyMap> {
GoogleMapController googleMapController;
LocationData currentLocation;
LocationData distinationLocation;
Location location;
#override
void initState() {
location = Location();
setInitSourceAndDestination();
super.initState();
}
setInitSourceAndDestination() async {
currentLocation = await location.getLocation();
}
#override
Widget build(BuildContext context) {
CameraPosition initialCameraPosition = CameraPosition(
target: LatLng(currentLocation.latitude, currentLocation.longitude),);
return GoogleMap(
initialCameraPosition: initialCameraPosition,
onMapCreated: (GoogleMapController controller) => googleMapController = controller,
mapType: MapType.normal,
tiltGesturesEnabled: false,
compassEnabled: true,
myLocationEnabled: true,
);
}
}
But there are a problem I can not solve:
the map is drawn in the screen before currentLocation is set
I tried setState and the problem has not been solved.
How can I make the app draw the map after setInitSourceAndDestination method finish excuting?
What make me confused is that the code at this form the build method will be excuted before setInitSourceAndDestination method finished, but if I add setState and change the currentLocation value inside it to rebuild the screen I noticed that setState executed before build function but it still dont show my location
Add a condition to check the state. If the state is null, then draw a loading indicator. If not null, draw the maps
it can be look like this :
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
class MapScreen extends StatefulWidget {
#override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
double lat;
double long;
LatLng _pickedLoc;
Future _getLocation() async {
LocationData location = await Location().getLocation();
setState(() {
lat = location.latitude;
long = location.longitude;
});
}
_selectLocation(LatLng location) {
_pickedLoc = location;
}
#override
void initState() {
super.initState();
_getLocation();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: lat == null || long == null
? Center(
child: CircularProgressIndicator(),
)
: GoogleMap(
initialCameraPosition: CameraPosition(
zoom: 16,
target: LatLng(lat, long),
),
onTap: _selectLocation,
onCameraMove: (object) => {debugPrint(object.target.toString())},
markers: {
Marker(
markerId: MarkerId("m1"),
position: _pickedLoc,
)
},
),
);
}
}