Flutter google_maps_flutter package blocking alertDialog - google-maps

I am currently using the google_maps_flutter package, and I am placing it within a stack and overlaying it with a button. When the button is pressed, an alert should pop up.
The problem I am currently having is that it works upon first load, but if I exit the app, putting it in the background, and I reenter the app, the alertDialog is no longer showing up. It exists on the screen because I am unable to move the map, and I have to click the area where the button would normally be, but it is not visible.
Any ideas on what's going on?
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Stack(
children: <Widget>[
new Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: new Opacity(
opacity: opacity,
child: new GoogleMap(
onMapCreated: initializeMap,
options: GoogleMapOptions(
//trackCameraPosition: true,
compassEnabled: true,
myLocationEnabled: true,
),
),
),
),
new Align(
alignment: new Alignment(0, 1),
child: new GestureDetector(
onTap: () {
alert();
},
child: new Image.asset(
'assets/test.png',
height: 150.0,
fit: BoxFit.cover,
),
),
),
],
),
),
),
}
alert() {
return showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return new CupertinoAlertDialog(
title: new Text("hello"),
content: new Text("hello"),
actions: <Widget>[
CupertinoDialogAction(
isDefaultAction: true,
child: Text("Ok"),
onPressed: () {
Navigator.pop(context);
}
),
CupertinoDialogAction(
child: Text("Cancel"),
onPressed: () {
Navigator.pop(context);
}
)
],
);
},
);
}

Keep in mind google_maps_flutter is a developer preview at version 0.0.3. Give it some time!

Related

Call function in onTap method directly or using fat operator in Dart/Flutter?

I have created a function in my app which makes a custom widget according to my need. Here is the code for the function
Widget customCircularButton(
{String title, String subTitle, String img, void Function() onTap}) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: onTap,
child: CircleAvatar(
radius: screenWidth(context) / 6,
backgroundColor: pColor,
child: CircleAvatar(
radius: screenWidth(context) / 6 - 2,
backgroundColor: Colors.white,
backgroundImage: Image.asset(img).image,
),
),
),
Container(
margin: EdgeInsets.all(5),
child: Text(
title,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
),
Container(
margin: EdgeInsets.all(5),
width: screenWidth(context) / 3,
child: Text(
subTitle,
style: TextStyle(fontWeight: FontWeight.w300, fontSize: 12),
textAlign: TextAlign.center,
))
],
);
}
So to call this function into my widget i call it like this
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
customCircularButton(
title: 'Donate',
subTitle: 'Donate/Buy food for needy',
img: 'assets/images/donate-food.png',
onTap: donateDiaolg,
),
customCircularButton(
title: 'Become Volunteer',
subTitle: 'Distribute food to the needy',
img: 'assets/images/become-volunteer.png',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BecomeVolunteer()));
}),
],
),
Here donateDialog is another function which opens up a dialog.
void donateDiaolg() {
showDialog(..implementation..);}
So my doubt here is that should is use fat operator to call the donateDialog function or just call it like there in above code.
My basic question is should i use this
onTap: () => donateDialog(),
or this
onTap: donateDialog(),
or is it okay to do like this
onTap: donateDialog
It would be better if someone explains me these three function call.
It depends on function signature. for example onTap property needs function with no argument. if your custom function is same you can use your function name directly like onTap: myFunc. if your function's signature take 1 or more arguments you should pass empty function to onTap and call your function through it.

Flutter: exclude one object from scrolling within a listview

I have a listview of objects that contains basically a form with textfields, a google maps object and then another form of checkboxes.
What I would like to do is have the google maps object not part of the scroll physics of the listview as a whole, because then you cannot actually move the map around at all.
Some code:
Constructor inside the build widget:
Flexible(
child: StreamBuilder(
stream: Firestore.instance.collection('users').document(uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) =>
_buildListRows(context, snapshot.data),
);
} else {
return LoadingAnimation();
}
},
),
),
The _buildListRows Widget, simplified:
Widget _buildListRows(BuildContext context, DocumentSnapshot document) {
return Container(
child: Form(
child: Column(
children: <Widget>[
Container(
child: TextFormField(),
),
Container(
child: SizedBox(
child: GoogleMap(),
),
),
Expanded(
Container(
ListView(
physics: ScrollPhysics(),
children: keys.map(key) {
return Checkbox(value: key);
}
),
),
],
),
),
);
}
So a quick recap, I need to be able to scroll the objects above and below the map, to move the whole screen up and down, but on the map itself I need to be able to control the map, so that touch actions apply to map functionality instead.
I have read a lot about this and tried some of the suggestions like having a singlechildscrollview and then listviews underneath that parent but it seems anything else I try just completely breaks the app and nothing will display at all.

GoogleMap's onCameraIdle stops firing when in a SingleChildScrollView

I have a design where a SingleChildScrollView has a GoogleMap on the first 50% on the screen, and a listing of items in the lower 50%. The user can scroll the entire view up to look at all of the listings. However, at times the Map stops firing the onCameraIdle if the users scrolls the page, and it just won't start firing it again.
onCameraMove and onTap works just fine. It is just onCameraIdle that won't fire.
SingleChildScrollView(
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
height: screenSize.height / 2,
child: GoogleMap(
key: Key("GMap"),
mapType: MapType.normal,
markers: Set<Marker>.of(markers.values),
gestureRecognizers: Set()
..add(Factory<PanGestureRecognizer>(
() => PanGestureRecognizer()))
..add(
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer()),
)
..add(
Factory<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer()),
)
..add(
Factory<ScaleGestureRecognizer>(
() => ScaleGestureRecognizer()),
),
initialCameraPosition: CameraPosition(
target: LatLng(14.551620, 121.053329), zoom: 14.5),
onMapCreated: (GoogleMapController controller) {
if (!_controller.isCompleted) {
_controller.complete(controller);
_lastCameraPosition = CameraPosition(
target: LatLng(14.551620, 121.053329), zoom: 14.5);
}
},
myLocationEnabled: true,
myLocationButtonEnabled: true,
onCameraIdle: () {
print("547: onCameraIdle");
_fetchOffers();
},
onCameraMove: (value) {
print("552: onCameraMove");
_lastCameraPosition = value;
},
onTap: (value) {
// Load items for current view if deselecting a marker
print('556: Tapped outside');
},
),
),
Positioned(
top: 50,
right: 20,
child: Container(
height: 30,
decoration: BoxDecoration(
border: Border.all(
color: _userBalance > 0
? globals.themeColor4
: globals.themeColor2,
width: 2),
boxShadow: [
BoxShadow(
blurRadius: 10.0,
color: Colors.black.withOpacity(.5),
offset: Offset(3.0, 4.0),
),
],
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10.0))),
child: Center(
child: Padding(
padding: EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Text(
"Balance: \u{20B1} ${_userBalance.toStringAsFixed(0)}",
style: TextStyle(
color: Colors.black,
fontSize: 14,
),
),
),
),
),
),
],
),
AnimatedContainer(
color: Colors.white,
// Use the properties stored in the State class.
width: double.infinity,
height: _loaderHeight,
// Define how long the animation should take.
duration: Duration(seconds: 1),
// Provide an optional curve to make the animation feel smoother.
curve: Curves.fastOutSlowIn,
child: Center(
child: Text(
"Loading, please wait",
style: TextStyle(color: Colors.grey),
),
),
),
Container(
color: Colors.white,
child: _offers == null
? Container(
child: Padding(
padding: EdgeInsets.all(30),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(MdiIcons.foodAppleOutline,
size: 60, color: globals.themeColor4),
Padding(padding: EdgeInsets.only(right: 20)),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Fetching offers",
style: TextStyle(
color: globals.themeColor4,
fontWeight: FontWeight.bold)),
Padding(padding: EdgeInsets.only(top: 5)),
Text(
"We are fetching offers for you, please hold on...",
style: TextStyle(color: Colors.grey)),
],
),
),
],
),
),
)
: Column(children: _offers),
),
],
),
),
Has anyone encountered this before and have a solution to it?
I replicated a simple version of your sample, but I wasn’t able to reproduce an issue with onCameraIdle not firing.
Now, based off of my sample, there were some behaviors that you could have misinterpreted as not working, but is actually the scrollview’s behavior taking over (since this is all inside a scrollview):
Sometimes a downward gesture on the map would pull on the scrollview instead of the map.
And an upward gesture would scroll the scrollview instead of interacting with the map.
But without any further details into the rest of your code, or a mcve that can easily reproduce your issue, it’s hard to say what’s really going on.
However, as Patrick Kelly mentioned, it’s also possible that the lack of a KeepAlive might have eventually led to the temporary disposing of your maps widget. Which is why ListView was suggested because this feature is built into it.
On the other hand, you can also implement AutomaticKeepAliveClientMixin for a similar effect, as seen over at https://stackoverflow.com/a/51738269/6668797 (but beware of the warning for the widget disposing).
Anyways, here’s my sample, and I had to make an educated guess on what your _fetchOffers() is:
class _MyHomePageState extends State<MyHomePage> {
// testing
int fetchCount = 0;
List<Widget> _offers;
_fetchOffers() {
fetchCount++;
// simulate varying data
var rng = new Random();
int start = rng.nextInt(10);
int end = start + 3 + rng.nextInt(30);
// build sample list
List<Widget> list = new List();
for (int i = start; i < end; i++) {
list.add(Text('offer$i', style: new TextStyle(fontSize: 30.0)));
}
// assuming you are using setState()
setState(() {
_offers = list;
});
}
// from google maps sample
Completer<GoogleMapController> _controller = Completer();
static final CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height / 2,
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: _kGooglePlex,
gestureRecognizers: Set()
..add(Factory<PanGestureRecognizer>(() => PanGestureRecognizer()))
..add(Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer()))
..add(Factory<HorizontalDragGestureRecognizer>(() => HorizontalDragGestureRecognizer()))
..add(Factory<ScaleGestureRecognizer>(() => ScaleGestureRecognizer())),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
onCameraIdle: () {
_fetchOffers();
},
),
),
]
),
Container(
child: _offers == null
? Container(child: Text("Fetching offers"))
: Column(children: _offers)
),
],
),
),
floatingActionButton: FloatingActionButton(
// for logging _fetchOffers activity
child: Text(fetchCount.toString())
),
);
}
}
onCameraIdle fired every time for me, and I could visually confirm it with the changing offers data as well as the fetchCount log.
You could use a ListView or a CustomScrollView with KeepAlive
This prevents Widgets from being thrown out when scrolled out of view.
I would alternatively recommend digging into the ScrollController class

How to hook up data from local json to achieve search with autocomplete text in list?

I am trying to implement input search feature wherein typing a search text will display suggested text and user can select relevant text from list and hit search button to proceed to corresponding screen. The suggested text is in local json and I added it under under assets/ folder and in pubspec.yaml.
The search textfield is:
The code for above is:
new TextField(
style: new TextStyle(
color: Colors.white,
fontSize: 16.0),
cursorColor: Colors.green,
decoration: new InputDecoration(
suffixIcon: Container(
width: 85.0,
height: 60.0,
color: Colors.green,
child: new IconButton(
icon: new Image.asset('assets/search_icon_ivory.png',color: Colors.white, height: 18.0,),
onPressed: () {
},
),
),
fillColor: Colors.black,
contentPadding: new EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'What Do You Need Help With?',
hintStyle: new TextStyle(
color: Colors.white
)
)
)
The local json data sample is:
I want to achieve above using autocomplete_textfield package which I've installed and imported and referring this example.
I would like to know how to get started with this and integrate parsing from local json, hook that data using autocomplete_textfield package to achieve my goal. I haven't done parsing json in flutter yet so looking for guidance on how to do that.
The end result I am looking for is like this:
***************** Edit **************
I am now able to parse data from local json and display it in a listView using a demo app. For it, I created a new model class `services.dart' as below:
class Categories {
String serviceCategory;
String servCategoryDesc;
int id;
String autocompleteterm;
String category;
String desc;
Categories({
this.serviceCategory,
this.servCategoryDesc,
this.id,
this.autocompleteterm,
this.category,
this.desc
});
factory Categories.fromJson(Map<String, dynamic> parsedJson) {
return Categories(
serviceCategory:parsedJson['serviceCategory'] as String,
servCategoryDesc: parsedJson['serviceCategoryDesc'] as String,
id: parsedJson['serviceCategoryId'],
autocompleteterm: parsedJson['autocompleteTerm'] as String,
category: parsedJson['category'] as String,
desc: parsedJson['description'] as String
);
}
}
Used builder function to retrieve and display value in listview as below:
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Load local JSON file"),
),
body: new Container(
child: new Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: new FutureBuilder(
future: DefaultAssetBundle
.of(context)
.loadString('assets/services.json'),
builder: (context, snapshot) {
// Decode the JSON
Map data = json.decode(snapshot.data
.toString());
print(data);
final List<Categories> items = (data['data'] as List).map((i) => new Categories.fromJson(i)).toList();
for (final item in items) {
print(item.category);
return new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Text('Service Category: ' + items[index].category),
new Text('Category' + items[index].categoryDesc),
new Text('Auto complete term' + items[index].autocompleteterm),
new Text('Desc' + items[index].desc)
],
),
);
},
);
}
}
)
)
)
);
}
}
In my target app, added required code that uses autocomplete_textfield package that shows a static list of suggestions as of now :
#override
Widget build(BuildContext context) {
textField = new AutoCompleteTextField<String>
(style: new TextStyle(
color: Colors.white,
fontSize: 16.0),
decoration: new InputDecoration(
suffixIcon: Container(
width: 85.0,
height: 60.0,
color: Colors.green,
child: new IconButton(
icon: new Image.asset('assets/search_icon_ivory.png',color: Colors.white,
height: 18.0,),
onPressed: (){},
),
),
fillColor: Colors.black,
contentPadding: new EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0),
filled: true,
hintText: 'What Do You Need Help With ?',
hintStyle: new TextStyle(
color: Colors.white
)
),
submitOnSuggestionTap: true,
clearOnSubmit: true,
textChanged: (item){
currentText = item;
},
textSubmitted: (item) {
setState(() {
currentText = item;
});
},
key: key,
suggestions: suggestions,
itemBuilder: (context, item) {
return new Padding(
padding: EdgeInsets.all(8.0), child: new Text(item));
},
itemSorter: (a, b) {
return a.compareTo(b);
},
itemFilter: (item, query) {
return item.toLowerCase().startsWith(query.toLowerCase());
});
Column body = new Column(children: [
new GestureDetector(
child: new ListTile(
title: textField,
onTap: () {
setState(() {
if (currentText != "") {
added.add(currentText);
textField.clear();
currentText = "";
}
});
}
)
)
]
);
body.children.addAll(added.map((item) {
return new ListTile(
title: new Text(item)
);
}));
return Scaffold(
resizeToAvoidBottomPadding: false,
backgroundColor: Color(0xFF13212C),
appBar: AppBar(
title: Text(''),
),
drawer: appDrawer(),
body: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Column(
children: <Widget>[
textField
Above code shows in UI as below:
I now would like to know how to hook the builder function retrieving json data in my target app, so that instead of static list of strings, the dropdown would show suggestions from json (as posted in my original question's screenshot).
If you found doing this manually it too much, this is actually a flutter package that does this. There are two examples on the package site too.
Do be warned, this is currently a bug in the package (I have raised a PR to fix it but the package owner has been too busy to review any PR recently). Depending on how you use it, the bug may not affect you.

many json loading in the same screen, how to await one and continue?

I have a screen with multiples json request, but it is loading all in the same time, i want to when finish one start the another until the end.
new CustomScrollView(
cacheExtent: height * 5,
slivers: [
new SliverList(
delegate: new SliverChildListDelegate(
[
new RelatorioVendPeriodoAPeriodo(),
new RelatorioDiasDaSemanas(),
new RelatorioVendasTotalidasPorPeriodo(),
new RelatorioDasVendasTotsProdutos(),
new RelatorioMensals(),
]
)
)
]
),
this classes loads a json request, how to divide and await one to start other?
Here one of this classes return method:
Widget build(BuildContext context) {
double height = (MediaQuery.of(context).size.height) - kRadialReactionRadius - kToolbarHeight;
return new Container(
height: height,
padding: new EdgeInsets.only(left: 5.0, right: 5.0),
child: new Card(
elevation: 10.0,
child:
new Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Column(crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new ListTile(
trailing:
_buildCounterButton(),
title: new Center(child: new Text('FATURAMENTO MENSAL', style: new TextStyle(fontWeight: FontWeight.bold))),
subtitle: new Icon(Icons.info),
onTap: _showDialog,
),
new FutureBuilder<List<RelatorioMensal>>(
future: fetchRelatorioMensal(new http.Client(), "", '$dateFinal1Sem'),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? new porVendaMensal(listVendasMensais: snapshot.data,)
: new Center(child: new CircularProgressIndicator());
}
),
],),]
),
),
);
}