using the flutter plugin image_picker 0.4.4 I'm getting displaying an image from camera or gallery with the following
Widget _previewImageBkg() {
return FutureBuilder<File>(
future: _imageFileBkg,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) {
print('_previewImage.........check callback for this image. .>>>>>');
final File file = snapshot.data;
myBgURL = uploadFile('user#image.com_B.jpg', file);
return Image.file(snapshot.data);
} else if (snapshot.error != null) {
return const Text(
'Error picking image.',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
)
the image does display fine if you put in a Container
return Scaffold(
appBar: AppBar(
title: Text('Set Profile Images'),
),
body: Center(
child: new Container(
width: screen.width,
height: 250.0,
child: _previewImageBkg()),
),
However I would like to display it in a BoxDecoration(image: ...)
child: new Container(
width: screenSize.width,
height: 275.0,
decoration: new BoxDecoration(
image: new DecorationImage(
image: (_previewImageBkg()== null ? new ExactAssetImage('images/nav_header_bg.png') : _previewImageBkg()),
fit: BoxFit.cover,
),
),
),
/flutter (29415): type 'FutureBuilder<File>' is not a subtype of type 'ImageProvider<dynamic>'
How do I cast this File to Image ?
The greater objective to get the - fit: BoxFit.cover - property of the BoxDecoration.
Use FileImage like this, to build your decorated Container - the associated Future must return a File:
#override
Widget build(BuildContext context) {
return new FutureBuilder<File>(
future: _imageFileBkg,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container(); // or some other placeholder
return new Container(
width: screenSize.width,
height: 275.0,
decoration: new BoxDecoration(
image: new DecorationImage(
image: new FileImage(snapshot.data),
fit: BoxFit.cover,
)),
);
},
);
}
Related
I m making an app which display a row of images ( All of them using loop / index from 0-4). The code :
for (int i = 0; i <= 3; i++)
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Container(
width: 300,
height: 300,
color: Colors.accents[i],
child: Image.asset(
"assets/farCryImages/$i.jpg",
alignment: Alignment.center,
fit: BoxFit.cover,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 5,
),
And i wanted to show the user a popup of their name ( i.e : first picture : a , second picture : b etc..). But i can't seem to find a way to do it and I need help.
I tried this but it wont work :
for (int i = 0; i <= 3; i++)
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 0, 0),
child: GestureDetector(
onLongPress: () => showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text("$i"),
),
),
I wanted to change the $i into specific name for specific photos.
Full code :
child: Row(
children: [
for (int i = 0; i <= 3; i++)
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 0, 0),
child: GestureDetector(
onLongPress: () => showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text("$i"),
),
),
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Container(
width: 300,
height: 300,
color: Colors.accents[i],
child: Image.asset(
"assets/farCryImages/$i.jpg",
alignment: Alignment.center,
fit: BoxFit.cover,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 5,
),
),
)
],
),
Any help is appreciated !!!
I am not sure about your question. However, if you want to loop through different names for images, you should use a string list for their names:
List<String> imageName = [
"first image",
"second image",
// and so on
]
Then, call them in your loop:
onLongPress: () => showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(imageName[i]),
),
),
Also, for better solution, I suggest to use a class for each image:
class ImageItem{
String name;
String path;
// and other parameters like id
// and a constructor
}
Then create a list of this class and use them in your loop.
First of all, Create a Stateful Widget class for the objects of interest like so:
class CardObject extends StatefulWidget {
final String title;
final String image;
final Color color;
const CardObject(this.title, this.image, this.color);
#override
_CardObjectState createState() => _CardObjectState();
}
class _CardObjectState extends State<CardObject> {
#override
Widget build(BuildContext context) {
return Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 0, 0),
child: GestureDetector(
onLongPress: () => showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(widget.title),
),
),
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Container(
width: 300,
height: 300,
color: widget.color,
child: Image.asset(
"assets/farCryImages/${widget.image}",
alignment: Alignment.center,
fit: BoxFit.cover,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 5,
),
),
),
);
}
}
Then create a list to hold the CardObjects:
//List of CardObjects
final List<CardObject> cardObjects = [
CardObject('title 1', 'image1.jpg', Colors.red),
CardObject('title 2', 'image2.jpg', Colors.blue),
CardObject('title 3', 'image3.jpg', Colors.orange),
CardObject('title 4', 'image4.jpg', Colors.grey),
];
Display your objects like this:
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: cardObjects,
),
);
}
Quite a bit has changed since the inception of google_maps_flutter, this means that the process for removing individual markers has also changed.
What I've found on older queries for this question, removing markers on version ~ 0.0.1, is this:
mapController.markers.forEach((marker){
mapController.removeMarker(marker);
});
This doesn't work for me as I get the errors:
The getter 'markers' isn't defined for the class 'GoogleMapController'.
and
The method 'removeMarker' isn't defined for the class 'GoogleMapController'.
This is what I use to add a marker:
void _addMarker(LatLng latlang, String title) {
var _width = MediaQuery.of(context).size.width;
var _height = MediaQuery.of(context).size.height;
double _topHeight = 55;
if (_height == 896.0 && _width == 414.0) {
_topHeight = 83;
} else if (_height == 812.0 && _width == 375.0) {
_topHeight = 78;
}
double mapsettingsHeight = 225;
if (_topHeight == 55){
mapsettingsHeight = 225;
} else {
mapsettingsHeight = 255;
}
if (_markers.contains(title)) {
print("error");
} else {
setState(() {
_markers.add(Marker(
markerId: MarkerId(title),
position: latlang,
infoWindow: InfoWindow(
title: title,
snippet: title,
onTap: () {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return Material(
child: Container(
width: _width,
height: mapsettingsHeight,
decoration: BoxDecoration(
color: Color(0xFFF9F9F9).withAlpha(200),
borderRadius: BorderRadius.only(topLeft:Radius.circular(10),topRight:Radius.circular(10))
),
child: Column(
children: <Widget>[
Container(
width: _width,
height: 40,
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
height: 20,
alignment: Alignment.center,
child: Text(title,style:TextStyle(fontFamily:'Helvetica',fontSize:15,color:Colors.black,fontWeight:FontWeight.w600))
),
GestureDetector(
child: Container(
width: 50,
height: 50,
alignment: Alignment.topRight,
child: Icon(Icons.keyboard_arrow_down,size:25,color:Colors.black.withAlpha(100)),
),
onTap: () {
Navigator.pop(context);
}
)
],
),
),
_customDivider(),
Divider(height:10,color:Colors.transparent),
Container(
width: _width,
height: 12.5,
padding: EdgeInsets.only(left:10,right:10),
child: GestureDetector(
child: Text("Edit Marker",style: TextStyle(fontFamily:'Helvetica',fontSize:12.5,color:Colors.blue,fontWeight:FontWeight.w400)),
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: TextField(
controller: _locationMarkerTitle,
decoration: InputDecoration(
hintText: 'Name this location',
hintStyle: TextStyle(fontFamily:'Helvetica',fontSize:15,color:Colors.black,fontWeight:FontWeight.w200),
),
),
titlePadding: EdgeInsets.all(10),
content: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
GestureDetector(
child: Text("Submit (this doesn't work!)",style: TextStyle(fontFamily:'Helvetica',fontSize:15,color:Colors.blue,fontWeight:FontWeight.w200)),
onTap: () {
_addMarker(LatLng(_position.latitude,_position.longitude),_locationMarkerTitle.text);
Navigator.pop(context);
}
),
],
)
);
}
);
}
),
),
Divider(height:10,color:Colors.transparent),
Container(
width: _width,
height: 12.5,
padding: EdgeInsets.only(left:10,right:10),
child: GestureDetector(
child: Text("Remove Marker",style: TextStyle(fontFamily:'Helvetica',fontSize:12.5,color:Colors.blue,fontWeight:FontWeight.w400)),
onTap: () {
setState(() {
//where the solution go
});
}
),
),
],
),
),
);
}
);
}
),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueAzure),
));
});
}
}
I assume somewhere in your build() function you have the Google Maps widget:
GoogleMap(
markers: _markers,
...
You simply have to remove the individual Marker from your _markers list and invoke setState so that the GoogleMap widget is rebuilt with the updated list of markers.
Edit - to be more specific to the author's use case:
MarkerIds uniquely identify a Marker, so you can use those to find the Marker you want to remove:
_markers.remove(_markers.firstWhere((Marker marker) => marker.markerId.value == title));
or
_markers.remove(_markers.firstWhere((Marker marker) => marker.markerId == MarkerId(title)));
In my search there is some ways to do this. One way is to use FutureBuilder and StreamBuilder at the same time. But I want to avoid this nesting StreamBuilder inside FutureBuilder?
I tried to create a method and call this from stream builder.
_getData() async{
FirebaseUser user = await FirebaseAuth.instance.currentUser();
String uid = user.uid.toString();
var snapshots =
Firestore.instance.collection('userinfo').document(uid).snapshots();
return snapshots;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text('Retrieve Text Input'),
),
body: new Container(
padding: EdgeInsets.only(top: 20.0, left: 10.0, right: 10.0),
child: new StreamBuilder(
stream: _getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var userDocument = snapshot.data;
return new Column(
children: <Widget>[
TextFormField(
initialValue: userDocument["name"].toString(),
//controller: _AdContr,
decoration: new InputDecoration(
labelText: 'Name',
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
//fillColor: Colors.green
),
),
SizedBox(height: 20),
TextFormField(
//initialValue: Text(userDocument["Surname"]).toString(),
//controller: _AdContr,
decoration: new InputDecoration(
labelText: 'Surname',
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
//fillColor: Colors.green
),
)
],
);
}
}),
),
);
}
But I am getting this error:
type 'Future' is not a subtype of type 'Stream'
How do I solve this problem?
You will need to nest a StreamBuilder inside a FutureBuilder because you will need to await the user from FirebaseAuth before being able to get the stream. However, this is not bad at all.
This is how Flutter works: nesting widgets.
..., child: FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState != ConnectionState.done) return Container();
return StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance.collection('userinfo').document(snapshot.data.uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) return Container();
return Column(...);
},
);
})
Another aspect that is crucial for this to work is that you always return a widget from any builder. If you do not return a widget to a builder in Flutter, your app will break, i.e. a runtime exception will be thrown.
Edit:
Home Page - I'm fetching a list of strings from my firebase collection. I then want to make a call to firestore storage and get the downloadable image link and store it in a list that I will pass to Page 2. The code below is how i'm getting the downloadable links.
Future<List<String>> test(List images) async{
List<String> listOfImages = List<String>();
for(int i = 0; i < images.length; i++){
final ref = FirebaseStorage.instance.ref().child(images[i]);
var url = await ref.getDownloadURL();
listOfImages.add(url);
}
return listOfImages;
}
I'm then passing the list of Images in the following manner
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CarDetailScreen(
carImages: test( car['images'])
)),
);
Page 2 - I'm receiving a Future> that I would like to convert to a List
List<T> map<T>(List list, Function handler) {
List<T> result = [];
for (var i = 0; i < list.length; i++) {
result.add(handler(i, list[i]));
}
return result;
}
new CarouselSlider(
items: ***["This is where I need a List"]***.map((url) {
return new Container(
margin: new EdgeInsets.all(5.0),
child:
new GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ViewCarImages(
carImages: _getImages(snapshot))),
);
},
child:
new ClipRRect(
borderRadius: new BorderRadius.all(new Radius.circular(5.0)),
child:
new Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
)
)
)
);
}).toList(),
viewportFraction: 0.9,
aspectRatio: 2.0,
autoPlay: false,
)
You can just return a List of Widget, like this:
Future<List<Widget>> test(List images) async{
List<Widget> listOfImages = List<Widget>();
for(int i = 0; i < images.length; i++){
final ref = FirebaseStorage.instance.ref().child(images[i]);
var url = await ref.getDownloadURL();
listOfImages.add(Image.network(url));
}
return listOfImages;
}
Below code will solve your problem. In carouselSlide class(check items variable) written below and you need to follow below procedure to work the Future object in carousel
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
backgroundColor: Colors.white,
title: Text("some text",
style: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.black,
fontSize: 16.0)),
centerTitle: false,
actions: <Widget>[
IconButton(
tooltip: 'Search something',
icon: Icon(
Icons.search,
color: SharedColor().sapphireDarkBlue,
),
onPressed: () async {
var selected = await showSearch<int>(
context: context, delegate: _delegate);
if (selected != null && selected != _lastIntegerSelected) {
setState(() {
//pass the data from searchfield query to here
});
}
},
),
],
automaticallyImplyLeading: false,
),
body: _carouselBuild(),
);
}
FutureBuilder<caraouselImage> _carouselBuild() {
return FutureBuilder<caraouselImage>(
future: ImageRestApiManager().caraouselImage(myEmail),
builder: (context, AsyncSnapshot<caraouselImage> snapshot) {
if (snapshot.hasData) {
return CarouselSlider(
items: snapshot.data.collection.map((i) {
return Builder(builder: (BuildContext context) {
return Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Stack(children: <Widget>[
Image.network(i.imageurl,
fit: BoxFit.cover, width: 1000.0),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(200, 0, 0, 0),
Color.fromARGB(0, 0, 0, 0)
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
padding: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
child: Text(
_currentCookieSelected.toString(),
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
]),
),
);
});
}).toList(),
autoPlay: false,
enlargeCenterPage: true,
viewportFraction: 0.9,
aspectRatio: 2.0,
onPageChanged: (index) {
setState(() {
_currentImageSelected = snapshot.data.collection[index].id;
});
});
} else {
return CircularProgressIndicator();
}
});
}
}
I solved using below youtube tutorial
https://www.youtube.com/watch?v=xBLtPDHvT44
A FutureBuilder should get you wht you're looking for. Here, I've wrapped your CarouselSlider in a FutureBuilder, and have the future property set to your carImages Future. When your list of Strings is ready, the FutureBuilder will create the CarouselSlider via its builder method, and all of the list items will be present then:
return FutureBuilder<List<String>>(
future: carImages,
initialData: [],
builder: (context, snapshot) {
return new CarouselSlider(
viewportFraction: 0.9,
aspectRatio: 2.0,
autoPlay: false,
items: snapshot.data.map((url) {
return new Container(
margin: new EdgeInsets.all(5.0),
child:
new GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ViewCarImages(
carImages: _getImages(snapshot))),
);
},
child: new ClipRRect(
borderRadius: new BorderRadius.all(new Radius.circular(5.0)),
child: new Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
)
)
)
);
}).toList(),
);
}
);
I need to add text, next to an image which is using GestureDetector to open the URL. I'm attaching an image below for reference so it's easier to understand.
Here is the code for this class:
class SocialMedia extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Social Media"),
),
body: new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('images/affedbackground.png'),
fit: BoxFit.cover,
),
),
// child: Padding(
// padding: const EdgeInsets.fromLTRB(8.0, 110.0, 8.0, 20.0),
child: ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(24.0),
child: GestureDetector(
onTap: _launchURLInstagram,
child: Image.asset(
'images/instagram.png',
// On click should redirect to an URL
width: 32.0,
height: 32.0,
fit: BoxFit.contain,
),
),
),
GestureDetector(
onTap: _launchURLFacebook,
child: Image.asset(
'images/facebook.png', // On click should redirect to an URL
width: 32.0,
height: 32.0,
fit: BoxFit.contain,
),
)
],
),
),
// ),
);
}
_launchURLInstagram() async {
const url =
'https://instagram.com/';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
_launchURLFacebook() async {
const url = 'https://www.facebook.com/';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
}
Set Row instead of Image:
GestureDetector(
child: Row(
children[
Text('...'),
Image.asset(
'images/instagram.png'...
),
]
),
Is this what you're looking for?