On some devices, the maps display fine. However, some times, when I scroll to the very bottom and scroll up slowly, the map overlap App Bar instead of start disappearing from my view (as the other widgets).
I've tested it in more than 4 different devices and simulators, and nothing happens. However it does happen on my friend's smartphone, I don't know why. Have anyone ever saw this bug? [screenshot of the bug][1]
children: <Widget>[
Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width,
child: GoogleMap(
onMapCreated: (mapController) {
_mapController = mapController;
try {
_addMarker();
} catch (e) {
print(e.toString());
_scaffoldKey.currentState.showSnackBar(
buildSnackBar('Request location error'));
}
},
markers: _markers,
mapType: MapType.normal,
scrollGesturesEnabled: false,
initialCameraPosition: CameraPosition(target: LatLng(_lead.latitude, _lead.longitude)),
)),
Column(
children: <Widget>[
Table(
columnWidths: {0: FlexColumnWidth(1), 1: FlexColumnWidth(1)},
children: [
_buildTableRow('Installation Address:',
_lead.installationAddressLineOne,
color: COLOR_LIGHT_GREY.withAlpha(150))
],
),
addDivider(),
Text(
'Roof Location',
style:
TextStyle(fontWeight: FontWeight.w600, color: Colors.red),
),
],
),
],
),
Container(
child: ButtonTheme(
height: 32,
minWidth: double.infinity,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
buttonColor: Colors.indigo,
padding: EdgeInsets.only(left: 10, right: 10),
child: RaisedButton(
child: Text(
'Route to lead',
style: TextStyle(color: Colors.white),
),
onPressed: () {
final url =
'https://www.google.com/maps/dir/?api=1&destination=${_lead.latitude},${_lead.longitude}';
launch(url);
},
),
),
)
],
);
[1]: https://i.stack.imgur.com/lAmL9.png
Related
I need help with the Flexible widget.
Widget build(BuildContext context) {
return Padding(
padding : EdgeInsets.all(20),
child: RawMaterialButton(
onPressed: onPressed,
elevation: 4.0,
fillColor: Theme.of(context).primaryColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: PreferredSize(
preferredSize: Size.fromHeight(30.0),
child: AppBar(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)),
automaticallyImplyLeading: false,
centerTitle: true,
title: Text(title),
)
),
body: Column(
children: [
Flexible(
child: Image.network(
link,
fit:BoxFit.fill
),
),
Padding(
padding: EdgeInsets.fromLTRB(0,10,0,10),
child: Divider(),
),
Text(
type,
overflow: TextOverflow.fade,
style: TextStyle(
wordSpacing: 1.0,
fontStyle: FontStyle.italic
),
),
Text(
"Prezzo: "+price.toString()+"€",
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
}
the method works because the image follows the size of the window, but if I make it too small the image disappears and gives me this error.
I have made several attempts but have not found a solution to the problem.
EDIT:
basically my panel looks like this but if I try to make it too small it gives me this error.
I am really confused as per what should I do, I am pretty bad with the layout of flutter.
I want to create boxes in the listview according to the number of strings (addresses) I have in another class - what is the best practice or approach for that?
Here Is the Image of the APP
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'AddData.dart';
import 'package:donation_yoga/services/json_service.dart';
//import 'package:provider/provider.dart';
class Map extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MapState();
}
class _MapState extends State<Map> {
/* Getting Live Location */
Completer<GoogleMapController> _controllerGoogleMap = Completer();
GoogleMapController newGoogleMapController;
Position currentPosition;
var geoLocator = Geolocator();
void locatePosition() async {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
currentPosition = position;
LatLng latLngPos = LatLng(position.latitude, position.longitude);
CameraPosition cameraPosition =
CameraPosition(target: latLngPos, zoom: 15.0);
newGoogleMapController
.animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
}
static final CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
myLayoutWidget(),
_buildContainer(),
_createForm(context),
],
),
);
}
Widget myLayoutWidget() {
return Container(
child: GoogleMap(
mapType: MapType.normal,
myLocationButtonEnabled: true,
initialCameraPosition: _kGooglePlex,
myLocationEnabled: true,
zoomGesturesEnabled: true,
onMapCreated: (GoogleMapController controller) {
_controllerGoogleMap.complete(controller);
newGoogleMapController = controller;
locatePosition();
},
),
);
}
}
Widget _buildContainer() {
return Align(
alignment: Alignment.bottomLeft,
child: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
height: 150.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
SizedBox(width: 10.0),
Padding(
padding: const EdgeInsets.all(8.0),
child: _boxes(
"https://lh5.googleusercontent.com/p/AF1QipO3VPL9m-b355xWeg4MXmOQTauFAEkavSluTtJU=w225-h160-k-no",
40.738380,
-73.988426,
Centres.first),
),
SizedBox(width: 10.0),
Padding(
padding: const EdgeInsets.all(8.0),
child: _boxes(
"https://lh5.googleusercontent.com/p/AF1QipMKRN-1zTYMUVPrH-CcKzfTo6Nai7wdL7D8PMkt=w340-h160-k-no",
40.761421,
-73.981667,
Centres.second),
),
SizedBox(width: 10.0),
Padding(
padding: const EdgeInsets.all(8.0),
child: _boxes(
"https://images.unsplash.com/photo-1504940892017-d23b9053d5d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
40.732128,
-73.999619,
Centres.third),
),
],
),
),
);
}
Widget _boxes(String _image, double lat, double long, String restaurantName) {
return GestureDetector(
onTap: () {},
child: Container(
child: new FittedBox(
child: Material(
color: Colors.white,
elevation: 14.0,
borderRadius: BorderRadius.circular(24.0),
shadowColor: Color(0x802196F3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
width: 180,
height: 200,
child: ClipRRect(
borderRadius: new BorderRadius.circular(24.0),
child: Image(
fit: BoxFit.fill,
image: NetworkImage(_image),
),
),
),
Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: myDetailsContainer1(restaurantName),
),
),
],
)),
),
),
);
}
Widget myDetailsContainer1(String restaurantName) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Container(
child: Text(
restaurantName,
style: TextStyle(
color: Color(0xff6200ee),
fontSize: 24.0,
fontWeight: FontWeight.bold),
)),
),
SizedBox(height: 5.0),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
child: Text(
"4.1",
style: TextStyle(
color: Colors.black54,
fontSize: 18.0,
),
)),
Container(
child: Icon(
FontAwesomeIcons.solidStar,
color: Colors.amber,
size: 15.0,
),
),
Container(
child: Icon(
FontAwesomeIcons.solidStar,
color: Colors.amber,
size: 15.0,
),
),
Container(
child: Icon(
FontAwesomeIcons.solidStar,
color: Colors.amber,
size: 15.0,
),
),
Container(
child: Icon(
FontAwesomeIcons.solidStar,
color: Colors.amber,
size: 15.0,
),
),
Container(
child: Icon(
FontAwesomeIcons.solidStarHalf,
color: Colors.amber,
size: 15.0,
),
),
Container(
child: Text(
"(946)",
style: TextStyle(
color: Colors.black54,
fontSize: 18.0,
),
)),
],
)),
SizedBox(height: 5.0),
Container(
child: Text(
"This is ",
style: TextStyle(
color: Colors.black54,
fontSize: 18.0,
),
)),
SizedBox(height: 5.0),
Container(
child: Text(
"Closed \u00B7 Opens 17:00 Thu",
style: TextStyle(
color: Colors.black54, fontSize: 18.0, fontWeight: FontWeight.bold),
)),
],
);
}
Widget _createForm(BuildContext context) {
return Align(
alignment: Alignment(-0.2, -1.0),
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddData()),
);
},
child: Text("Create")),
);
}
Here is my Other Class.
class Centres {
static const String first = 'This is our first location';
static const String second = 'This is our Second location';
static const String third = 'This is our Third locations';
}
The data in this class is gonna come from server.
You can use a ListView.builder to create a list of your boxes, just provide an item count that means how many widgets it will create and use the index to access each value you want to take from the lists of values.
I know i did not use you Centres class, unless you really need it to be a class, i find it easier to use a list with the values you need
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final List<String> entries = <String>['A', 'B', 'C'];
final List<String> centres = [
'first location',
'second location',
'third location'
];
final List<double> latitudes = [40.732128, 40.732128, 40.732128];
final List<double> longitudes = [-73.999619, -73.999619, -73.999619];
return ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return _boxes(
entries[index],
latitudes[index],
longitudes[index],
centres[index],
);
});
}
}
You can use both ListView.builder() and PageView.builder(). in ListView you can stay in between two boxes but PageView only stays on one box. both have the itemCount and itemBuilder.
I am quite new to Flutter and Dart development.
I am trying to display a list of all of the items in a JSON List that match a particular criteria, with the criteria being a type of alcohol in this case. I have created an IF statement at the top of what the ListBuilder returns, but it only returns the first item in the list when it matches the criteria. For example, I have three objects where the category is "Rum", but it still only returns the first one. Any help or advice would be greatly appreciated! Thank you!
class CocktailCategoryList extends StatefulWidget {
#override
_CocktailListState createState() => _CocktailListState();
}
class _CocktailListState extends State<CocktailCategoryList> {
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Container(
height: 55,
width: 55,
child: FittedBox(
child: Container(
height: 60,
width: 60,
child: FloatingActionButton(
backgroundColor: Color.fromRGBO(86, 99, 255, 100),
onPressed: () {
},
child: Icon(
Icons.shuffle,
color: Colors.white,
size: 35,
),
),
),
),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.miniCenterDocked,
bottomNavigationBar: BottomAppBar(
elevation: 20,
shape: CircularNotchedRectangle(),
child: Container(
height: 55,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
iconSize: 30,
padding: EdgeInsets.only(left: 28),
icon: Icon(
Icons.home,
),
onPressed: () {
Navigator.pop(context);
},
),
IconButton(
iconSize: 30,
padding: EdgeInsets.only(right: 28),
icon: Icon(
Icons.list,
color: Colors.purple[700],
),
onPressed: () {},
),
IconButton(
iconSize: 30,
padding: EdgeInsets.only(left: 28),
icon: Icon(Icons.person),
onPressed: () {},
),
IconButton(
iconSize: 30,
padding: EdgeInsets.only(right: 28),
icon: Icon(Icons.search),
onPressed: () {},
),
],
),
),
),
appBar: AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(
color: Colors.black,
),
centerTitle: true,
title: Text(
"Cocktails",
style: TextStyle(color: Colors.black),
),
),
body: Center(
child: FutureBuilder(
builder: (context, snapshot) {
var cocktailData = json.decode(snapshot.data.toString());
return ListView.builder(
itemCount: cocktailData == null ? 0 : cocktailData.length,
itemBuilder: (BuildContext context, int index) {
if (cocktailData[index]['category'] == "Rum"){
Column(
children: [
SafeArea(
minimum: EdgeInsets.fromLTRB(15, 0, 15, 0),
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CocktailPage(
cocktailData[index]['name'],
cocktailData[index]['subtitle'],
cocktailData[index]['category'],
cocktailData[index]['instructions'],
cocktailData[index]['image'],
cocktailData[index]['ingredients'],
))),
child: Container(
width: MediaQuery.of(context).size.width,
child: Wrap(children: [
Image(
image: NetworkImage(cocktailData[index]['image']),
),
]),
),
),
),
GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CocktailPage(
cocktailData[index]['name'],
cocktailData[index]['subtitle'],
cocktailData[index]['category'],
cocktailData[index]['instructions'],
cocktailData[index]['image'],
cocktailData[index]['ingredients'],
))),
child: ListTile(
visualDensity:
VisualDensity(horizontal: 0, vertical: -1),
title: Row(
children: [
Text(
cocktailData[index]['name'],
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(
width: 5,
),
CategoryTag(
cocktailData[index]['category']) //Button Here
],
),
subtitle: Text(cocktailData[index]['subtitle']),
),
),
SizedBox(height: 10)
],
);
}else {
SizedBox();
}
}
);
},
future:
DefaultAssetBundle.of(context).loadString('assets/testJson.json'),
),
),
);
}
}
One thing I noticed is that you are not returning the Column widget in your ListView.builder() inside of the if statement. The anonymous builder function that you provide to the ListView.builder() should always return a widget.
I have created a dialogue box in my flutter app and i want it to be reusable. So each time i call it i pass in two arguments, the text to be displayed to the user and the function to be executed if they confirm the action. here is the code
The dialogue widget
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, left: 20.0, right: 20.0),
child: Text(
//The text argument is passed here
widget.text,
style: TextStyle(color: Colors.grey[600], fontSize: 16.0),
),
)),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: ButtonTheme(
height: 35.0,
minWidth: 110.0,
child: RaisedButton(
color: Colors.black,
shape: buttonShapeDeco,
splashColor: Colors.black.withAlpha(40),
child: Text(
widget.button,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.grey,
fontWeight: FontWeight.bold,
fontSize: 13.0),
),
onPressed: ()async{
//I expect the passed function to be executed here after the user confirms the action
await widget.action;
}
},
)),
),
Padding(
padding: const EdgeInsets.only(
left: 20.0, right: 10.0, top: 10.0, bottom: 10.0),
child: ButtonTheme(
height: 35.0,
minWidth: 110.0,
child: RaisedButton(
color: Colors.black,
shape: buttonShapeDeco,
splashColor: Colors.white.withAlpha(40),
child: Text(
'Cancel',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.grey,
fontWeight: FontWeight.bold,
fontSize: 13.0),
),
onPressed: () {
setState(() {
Navigator.pop(context);
});
},
))
),
],
))
],
)),
In my instance 'm trying to sign out a user only after they press the confirm button to logout.
Here is the code
onTap: ()async{
showDialog(
context: context,
builder: (_) => Dialogue(text: 'Are you sure you want to logout?',button: 'Logout',action: _auth.signOut(),pop:true),
);
},
The problem is the function is executed as soon as the dialogue box is opened, before the user can confirm the action.
How can i get around this?
change the action from _auth.signOut() to ()=>_auth.signOut()
onTap: ()async{
showDialog(
context: context,
builder: (_) => Dialogue(text: 'Are you sure you want to logout?',button: 'Logout',action: ()=>_auth.signOut(),pop:true),
);
}
In your Dialogue widget you need to change the type of action parameter to Future<dynamic> Function(). And then pass _auth.signOut function to it.
Dialogue Widget
class Dialogue extends StateFulWidget {
Future<dynamic> Function() action;
//other code
}
Pass the signOut function to it
onTap: ()async{
showDialog(
context: context,
builder: (_) => Dialogue(text: 'Are you sure you want to logout?', button: 'Logout', action: _auth.signOut, pop:true),
);
}
I have a SliverAppBar that I want to be hidden when the user scrolls down on the page. The problem is that I have a Google Map widget that is causing the app bar to move as well, when I only want it to move only when my touch is off the Google Map. Is there a way to prevent this?
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.transparent,
elevation: 5.0,
pinned: false,
snap: false,
floating: false,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: Image.asset(
'assets/events/city.jpeg',
fit: BoxFit.cover,
),
),
),
SliverFillRemaining(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
height: 200,
width: double.infinity,
child: GoogleMap(
initialCameraPosition:
CameraPosition(target: LatLng(50.0, 50.0)),
onMapCreated: (controller) {
setState(() {
_googleMapController = controller;
});
},
),
),
)
],
),
),
)
],
),
));
}
I found that adding a gestureRecognizer property to GoogleMap and passing in a Factory of type VerticalDragRecognizer allowed it to prevent the SliverAppBar from scrolling. It also works on any kind of scrolling you want for your app. See this for more info about it around the 50 minute mark.
SliverFillRemaining(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
height: 200,
width: double.infinity,
child: GoogleMap(
gestureRecognizers: Set()..add(Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer()
)),
scrollGesturesEngabled: true,
initialCameraPosition:
CameraPosition(target: LatLng(50.0, 50.0)),
onMapCreated: (controller) {
setState(() {
_googleMapController = controller;
});
},
),
),
)
],
),
),
)