I'm trying to make fixed ScrollView with google map inside of FlexibleSpaceBar.
There's NestedScrollView with SliverAppBar and body content. Inside SliverAppBar is google map widget, app bar occupy the whole view of the device. There's also FloatingActionButton, which on press should scroll to the bottom of the view revealing body content of NestedScrollView and shifting map to app bar and vice versa. It technically works, there's a code if someone wants to see how it looks like:
#override
State<MapPage> createState() => MapPageState();
}
const LatLng _coordinates = LatLng(37.43296265331129, -122.08832357078792);
class MapPageState extends State<MapPage> with SingleTickerProviderStateMixin {
ScrollController _scrollController;
Completer<GoogleMapController> _controller = Completer();
double _scrollExtent;
bool _buttonState;
static final CameraPosition _kGooglePlex = CameraPosition(
target: _coordinates,
zoom: 14.4746,
);
#override
void initState() {
_buttonState = true;
_scrollController = ScrollController(initialScrollOffset: 0.0);
super.initState();
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
_scrollExtent = MediaQuery.of(context).size.height;
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.keyboard_arrow_down),
backgroundColor: Colors.white,
foregroundColor: Colors.red,
onPressed: (){
if(_buttonState){
_scrollController.animateTo(_scrollExtent, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
_buttonState = false;
}
else{
_scrollController.animateTo(-_scrollExtent, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
_buttonState = true;
}
},
),
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: MediaQuery.of(context).size.height,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text("Title",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: _kGooglePlex,
markers: _createMarker(),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
),
),
];
},
body: Container(
padding: EdgeInsets.all(24.0),
child: Text(
'Content'),
),
),
);
}
Set<Marker> _createMarker() {
return <Marker>[
Marker(
markerId: MarkerId("marker_1"),
position: _coordinates,
infoWindow: InfoWindow(title: 'Marker"'),
),
].toSet();
}
}
The important part is here:
NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: MediaQuery.of(context).size.height,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text("Title",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: _kGooglePlex,
markers: _createMarker(),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
),
),
];
},
There are 3 problems:
I dont understand how to prevent user from scrolling with gestures.
I want it to be controlled only by a scroll controller. I've tried to set physics of NestedScrollView to NeverScrollableScrollPhysics(), but it doesn't have any effect.
The map gesture detection inside scrollable widget behaving weirdly
You can't properly drag it and it seems to be only working with taps. In ListView, for example, it behaved fine with NeverScrollable physics.
Every time it's getting rebuilt in app bar it seems to cause some sort of memory leak and I'm getting next warning.
E/flutter (10644): #0 _AsyncCompleter.complete (dart:async/future_impl.dart:39:31)
E/flutter (10644): #1 MapPageState.build.<anonymous closure>.<anonymous closure> (package:flutter_app/map_page2.dart:83:35)
E/flutter (10644): #2 _GoogleMapState.onPlatformViewCreated (package:google_maps_flutter/src/google_map.dart:259:14)
E/flutter (10644): <asynchronous suspension>
E/flutter (10644): #3 AndroidViewController._create (package:flutter/src/services/platform_views.dart:599:15)
...
If anyone can help out with any of those or maybe know examples of such implementation I would highly appreciate it.
I crossed the same problem, and found the solution here.
background: GoogleMap(
gestureRecognizers: [
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
].toSet(),
Related
I'm trying to display some widgets on top of a Map, and the widgets are created onMapCreated. Now, the problem is that the widgets are formed but not displayed immediately. They are displayed after I press hot reload on the IDE. How can I display the widgets immediately the page is loaded?
Here's my map
SafeArea(
child: Stack(children: [
GoogleMap(
myLocationEnabled: true,
myLocationButtonEnabled: true,
initialCameraPosition: CameraPosition(
target:
LatLng(widget.position.latitude, widget.position.longitude),
zoom: 16,
),
onMapCreated: _onMapCreated,
markers: markers.toSet(),
),
SizedBox(
height: 120,
child: ListView(
scrollDirection: Axis.horizontal,
children: tiles,
),
),
]),
),
The _onMapCreated function is
void _onMapCreated(GoogleMapController controller) {
stream!.listen((List<DocumentSnapshot> documentList) {
_updateMarkers(documentList, controller);
});
setState(() {
mapController = controller;
});
}
I have a pair of widgets that generate a Google map and updates the user location but for some reason the padding seems to delay for a while, causing the MyLocation button to move into place instead of starting off in its correct position.
Is there any way to prevent this movement/delay in padding?
#override
Widget build(BuildContext context) {
return Scaffold(body: googleMapUI());
}
Widget googleMapUI() {
return Consumer<LocationProvider>(builder: (
consumerContext,
model,
child,
) {
if (model.locationPosition != null) {
return Column(
children: [
Expanded(
child: GoogleMap(
mapType: MapType.normal,
padding: const EdgeInsets.only(top: 40.0),
initialCameraPosition:
CameraPosition(target: model.locationPosition!, zoom: 18),
myLocationEnabled: true,
myLocationButtonEnabled: true,
onMapCreated: (GoogleMapController controller) async {
Provider.of<LocationProvider>(context, listen: false);
},
),
)
],
);
}
return Container(
child: const Center(
child: CircularProgressIndicator(),
),
);
});
}
I am suing Flutter plugin to show map in my app which is done issue is I have fixed long and latitude value and I need to show a custom image marker on that values. Its showing a simple red marker now but don't know how to show the image icon.
class _MapScreenState extends State<MapScreen> {
Completer<GoogleMapController> _controller = Completer();
#override
void initState() {
super.initState();
_onMapCreated();
}
final currentPostion = LatLng(24.916404, 67.130654);
final Map<String, Marker> _markers = {};
_onMapCreated() {
setState(() {
_markers.clear();
final marker = Marker(
markerId: MarkerId('asda'),
position: LatLng(24.916404, 67.130654),
infoWindow: InfoWindow(
title: 'dsaa',
snippet: 'sss',
),
);
_markers['saa'] = marker;
}
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
leading: GestureDetector(
onTap:(){
Navigator.pop(context);
},child: Icon(Icons.arrow_back)),
centerTitle: true,
flexibleSpace: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/nav.jpg'),
fit: BoxFit.cover,
),
),
),
backgroundColor: Colors.transparent,
title: Text('My Location', style: TextStyle(fontFamily: 'UbuntuBold'),),
actions: [
Padding(
padding: const EdgeInsets.only(right: 15),
child: Icon(
Icons.notifications_none,
size: 33,
),
)
]),
body: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: currentPostion,
zoom: 20,
),
markers: _markers.values.toSet(),
),
);
}
you can see i have added marker in code butnot able to change it to image.
GoogleMap widget contains property markers which is a Set<Marker>.
https://medium.com/#zeh.henrique92/google-maps-flutter-marker-circle-and-polygon-c71f4ea64498
I have animated pageview and listview that connected to each other. But I have an animation problem that caused by setState(i have tested to removed the setState and the animation works well).
import 'package:flutter/material.dart';
const double _listheight = 80;
class LoyaltyPage extends StatefulWidget {
#override
_LoyaltyPageState createState() => new _LoyaltyPageState();
}
class _LoyaltyPageState extends State<LoyaltyPage> {
PageController controller;
ScrollController listcontroller;
int selected = 0;
List<String> images=[
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
];
#override
initState() {
super.initState();
controller = new PageController(
initialPage: selected,
keepPage: false,
viewportFraction: 0.7,
);
listcontroller = new ScrollController();
}
#override
dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
title: Text(
'Loyalty',
style: TextStyle(color: Colors.black),
),
),
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: new Container(
child: new PageView.builder(
onPageChanged: (value) {
//ADDING THIS SET STATE CAUSE SELECTED COLOR WORKS BUT LAGGY ANIMATION
setState(() {
selected=value;
});
listcontroller.animateTo(value*_listheight,duration: Duration(milliseconds: 500),curve: Curves.ease);
},
itemCount: images.length,
controller: controller,
itemBuilder: (context, index) => builder(index)),
),
),
Expanded(
flex: 6,
child: new Container(
child: new ListView.builder(
controller: listcontroller,
itemCount: images.length,
itemBuilder: (context,index) => _listbuilder(index),
),
),
),
],
),
);
}
builder(int index) {
return new AnimatedBuilder(
animation: controller,
builder: (context, child) {
double value = 1.0;
if (controller.position.haveDimensions) {
value = controller.page - index;
value = (1 - (value.abs() * .4)).clamp(0.0, 1.0);
}
return new Center(
child: new SizedBox(
height: Curves.easeOut.transform(value) * 180,
width: Curves.easeOut.transform(value) * 270,
child: child,
),
);
},
child: new Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child:Container(
child: Image.network(images[index],fit: BoxFit.fill,)
),
),
);
}
_listbuilder(int index){
return Container(
height: _listheight,
child: Column(
children: <Widget>[
ListTileTheme(
selectedColor: Colors.blueAccent,
child: ListTile(
title: Text(images[index],maxLines: 1,overflow: TextOverflow.ellipsis,),
subtitle: Text('Level : Gold'),
leading: GestureDetector(
onTap: (){
//ADDING THIS SET STATE CAUSE SELECTED COLOR WORKS BUT LAGGY ANIMATION
setState(() {
selected=index;
});
controller.animateToPage(index,duration: Duration(milliseconds: 500),curve: Curves.ease);
},
child: Icon(
Icons.credit_card),
),
trailing: Icon(Icons.navigate_next),
selected: index==selected,//THIS IS THE SELECTED THAT I TRIED TO CHANGE
),
),
Divider(height: 1,color: Colors.black45,),
],
),
);
}
}
I use setState at 2 places where it's on pagechanged and on leading listtile of my app.And here is my app looks like.
the first one is the expected output of my app but it laggy,
the second one has smooth animation but the text color doesn't change.
Try running your application in Release or Profile Mode.
There are performance-issues with animations in debug-mode when only a CircularProgressIndicator() is spinning. This is due to the fact that in Debug Mode, Dart gets compiled JIT instead of AOT. Thus, the Drawing Engine would need to compile 60 times / second while drawing the UI during the animation to run as smoothly as in AOT mode. This would take up a lot of resources as one might guess.
As long as you don't call setState({}) on the AnimationController..addListener() you should be fine with your specific implementation. Please refer to this article for further details on Animations and setState({}): https://medium.com/flutter-community/flutter-laggy-animations-how-not-to-setstate-f2dd9873b8fc .
I took an application from the internet using flutter_google_maps plugin. To this I added a method called centermap. I have tried countless things to try and get the app to read centermap and relocate the google map to the coordinates. I have tried googling but can't find any examples of doing this in Flutter.
Can some help to get me going. In the orginal code I copied the raised button works.
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
GoogleMapController mapController;
void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Google Maps demo')),
body: MapsDemo(),
),
));
centerMap(mapController);
}
class MapsDemo extends StatefulWidget {
#override
State createState() => MapsDemoState();
}
class MapsDemoState extends State<MapsDemo> {
#override
Widget build(BuildContext context) {
return Padding (
padding: EdgeInsets.all(15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Center(
child: SizedBox(
width: 400.0,
height: 500.0,
child: GoogleMap(
onMapCreated: _onMapCreated,
),
),
),
RaisedButton(
child: const Text('Go to London'),
onPressed: mapController == null ? null : (){
mapController.animateCamera(CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 270.0,
target: LatLng(51.5160895, -0.1294527),
tilt: 30.0,
zoom: 17.0,
),
));
},
),
],
),
);
}
void _onMapCreated(GoogleMapController controller) {
setState(() {
mapController = controller;
});
}
}
void centerMap(GoogleMapController mapController) {
mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(37.4219999, -122.0862462), zoom: 20.0),
),
);
}
I should have kept trying a few more minutes
This seems to work
void _onMapCreated(GoogleMapController controller) {
setState(() {
mapController = controller;
});
centerMap(mapController);
}
}
void centerMap(GoogleMapController mapController) {
mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(37.4219999, -122.0862462), zoom: 20.0),
),
);
}