I am working on a Flutter application that uploads to and gets data from PostgreSQL Database.
I am using postgres: ^2.3.2 in pubspec.yaml file.
First, I am converting the image to base64.
And then, I am uploading the image to the database as BYTEA.
And when I query the image data from the database, it provides me with an integer list.
I searched the web, and found that I had to convert that list to Uint8List.
After converting it to Uint8List, I have to use the Image.memory method to convert the Uint8List to an image widget.
But when I do so, I get this error on the Debug Console:
═══════ Exception caught by image resource service ════════════════════════════
The following _Exception was thrown resolving an image codec:
Exception: Invalid image data
When the exception was thrown, this was the stack
#0 _futurize (dart:ui/painting.dart:5326:5)
#1 ImageDescriptor.encoded (dart:ui/painting.dart:5194:12)
#2 instantiateImageCodec (dart:ui/painting.dart:2048:60)
<asynchronous suspension>
════════════════════════════════════════════════════════════════════════════════
And this image on the device:
Device screenshot
The code is below:
import 'dart:typed_data';
import 'package:flutter/material.dart';
class ShowImage extends StatefulWidget {
const ShowImage({Key? key}) : super(key: key);
#override
_ShowImageState createState() => _ShowImageState();
}
class _ShowImageState extends State<ShowImage> {
dynamic _image;
Map data = {};
void setImage() async {
GetImage getImgObj = GetImage(connection: data['connection']);
dynamic imgBytes = await getImgObj.getImage();
print(imgBytes.runtimeType);
setState(() {
_image = Image.memory(imgBytes);
});
}
#override
Widget build(BuildContext context) {
data = ModalRoute.of(context)!.settings.arguments as Map;
// setImage();
return Scaffold(
appBar: AppBar(
title: Text("Show Image"),
centerTitle: true,
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(
onPressed: () {
setImage();
},
child: Text("Get Image"),
),
SizedBox(height: 10.0),
Container(
width: 100,
height: 350,
decoration:
BoxDecoration(border: Border.all(color: Colors.grey)),
child: _image == null
? Align(
alignment: Alignment.center,
child: Text(
"Image not selected",
textAlign: TextAlign.center,
),
)
: Align(
alignment: Alignment.center,
child: _image,
),
),
],
),
),
),
);
}
}
class GetImage {
var connection;
GetImage({this.connection});
Future<dynamic> getImage() async {
int itemId = 1;
String query = "SELECT img FROM lawbreach WHERE lid = $itemId;";
List<dynamic> results = await this.connection.query(query);
final bytes = Uint8List.fromList(results[0][0]);
return bytes;
}
}
I tried searching every corner of the web, but couldn't find any solution.
Hope, someone here has an answer to my question :)
The reason why the Image widget throws an "Invalid image data" Exception is because the base64 that you're trying to load is invalid. You need to check if the encoded base64 image that you've downloaded is being encoded to base64 again. Decode the downloaded base64 encoded image before loading it to the widget.
Uint8List _imageBytesDecoded = base64.decode(encodedImage);
...
Image.memory(_imageBytesDecoded)
How I solved my own, I extracted the image because it was in zip folder and go to pubspect.yaml assets: - images/image_22.jpg then in my widget I then added child: Image.asset('images/image_22.jpg')
Related
I want my app to place an automated call to a specific number when a certain condition arises. The two popular plugins are flutter_phone_direct_caller and url_launcher. Url launcher's problem is that the method will push the number to the dialer of your phone but wont start the call but flutter_phone_direct_caller claims it will initiate. This is the example in their documentation.
import 'package:flutter/material.dart';
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
void main() {
runApp(Scaffold(
body: Center(
child: RaisedButton(
onPressed: _callNumber,
child: Text('Call Number'),
),
),
));
}
_callNumber() async{
const number = '08592119XXXX'; //set the number here
bool res = await FlutterPhoneDirectCaller.callNumber(number);
}
this is the code for my page..when the button is pressed, call should initiate but for me, it returns an error.(my phone no. is XXXd out ,when i ran it i put in my actual no).
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:vitality/components/bottomAppBar.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:vitality/components/biom.dart';
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
class HomeScreen extends StatefulWidget {
static const String id = 'home_screen';
final String docid;
final bool isCaretaker;
HomeScreen({#required this.docid, #required this.isCaretaker});
#override
_HomeScreenState createState() => _HomeScreenState();
}
_callNumber() async {
const number = '86065XXXXX'; //set the number here
bool res = await FlutterPhoneDirectCaller.callNumber(number);
}
class _HomeScreenState extends State<HomeScreen> {
final auth = FirebaseAuth.instance;
var pulse;
var temp;
#override
Widget build(BuildContext context) {
print('got here');
print(auth.currentUser.uid);
String id = ModalRoute.of(context).settings.arguments;
CollectionReference main = FirebaseFirestore.instance.collection('maindb');
main.doc(id).get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('Document exists on the database');
pulse = documentSnapshot['pulse'];
temp = documentSnapshot['temperature'];
}
});
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Color(0xFF602247),
toolbarHeight: 50.0,
centerTitle: true,
title: Text(
'HEALTH TRACKER',
style: Theme.of(context).textTheme.headline4,
)),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image:
NetworkImage('https://cdn.wallpapersafari.com/12/24/GiZRfh.jpg'),
fit: BoxFit.cover,
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(.7), BlendMode.dstATop),
)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(widget.docid),
Text({widget.isCaretaker}.toString()),
biom(which: 'pulse', image: 'pulse', docid: widget.docid),
RoundBorderText(text: 'PULSE'),
biom(which: 'temperature', image: 'temper', docid: widget.docid),
RoundBorderText(text: 'TEMPERATURE'),
SizedBox(height: 30.0),
FlatButton(
child: Text('test call'),
onPressed: () async {
FlutterPhoneDirectCaller.callNumber('5');
})
]),
),
bottomNavigationBar: bottomAppBar(id: widget.docid),
);
}
}
class RoundBorderText extends StatelessWidget {
final String text;
RoundBorderText({this.text});
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(
left: 40.0, right: 40.0, top: 8.0, bottom: 8.0),
decoration: BoxDecoration(
// border: Border.all(
// color: Colors.black,
// width: 1.0,
// ),
borderRadius: BorderRadius.all(Radius.circular(20))),
child: Text(text, style: Theme.of(context).textTheme.headline1));
}
}
E/flutter (23210): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: MissingPluginException(No implementation found for method callNumber on channel flutter_phone_direct_caller)
This plugin has 94% popularity so it works for most people. Does anyone know what the issue is?
The way flutter integrates with native functionality is that is creates what are called MethodChannels using which they can call functions that are registered inside native java code from dart.
So one reason this error might be coming is that your flutter code is not able to communicate with the native java code, means it is not finding any channel or it is not finding the method registered by the package through a channel there.
I suspect this could be a build issue.
Steps
Uninstall the app from your device.
Rebuild app again.
This should solve the issue.
I'm trying to display an image using FileImage() but it's throwing the "'file != null': is not true" error. The image is coming from an Image Picker function. The wierd thing is that, I know that the file exists because when I try to print it, it shows me the path for the file.
EDIT: Here's the link to the entire code if you need to check it:
https://github.com/MBanawa/ecommerce_app/blob/master/lib/Admin/uploadItems.dart
Below is the function for the ImagePicker with a print method:
File imageFile;
pickImage(ImageSource imageSource) async {
Navigator.pop(context);
final pickedFile = await ImagePicker().getImage(source: imageSource);
setState(() {
imageFile = File(pickedFile.path);
print('Path $imageFile');
});
}
I trigger the function in an onPressed() by either camera or gallery :
camera:
onPressed: () => pickImage(ImageSource.camera),
gallery
onPressed: () => pickImage(ImageSource.gallery),
Below is how I call the imageFile:
Container(
height: 230.0,
width: MediaQuery.of(context).size.width * 0.8,
child: Center(
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(imageFile),
fit: BoxFit.cover,
),
),
),
),
),
),
The container above is inside a function called displayAdminUploadFormScreen() that gets called if imageFile is not null. I place that after the build method of my statefulwidget state:
#override
Widget build(BuildContext context) {
return imageFile == null
? displayAdminHomeScreen()
: displayAdminUploadFormScreen();
}
What I'm confused about is that, print() returns an actual link after taking a picture. here is a sample result when taking a picture using the camera:
I/flutter (16227): Path File: '/storage/emulated/0/Android/data/com.example.ecommerceapp/files/Pictures/068e58d6-88af-4b13-9453-c8c8d836083c5388058709499582194.jpg'
the exact error massage was:
The following assertion was thrown building UploadPage(dirty, dependencies:
[MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#71a32]], state: _UploadPageState#0a649):
'package:flutter/src/painting/image_provider.dart': Failed assertion: line 854 pos 14: 'file !=
null': is not true.
When checking the links that that error gave, it points to FileImage(imageFile). If I open up FileImage, line 854 pos 14 is an assertion:
const FileImage(this.file, { this.scale = 1.0 })
: assert(file != null),
assert(scale != null);
I also tried the traditional way of creating an ImagePicker function. (the one in pub.dev) but it's still showing the same error. Any guidance will be much appreciated. Thank you!
it's because you are making a call of clearFormInfo() function inside IconButton widget. I believe that you wanted to call this function inside callback onPressed property.
So instead of:
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
onPressed: clearFormInfo(),
)
Please use:
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
onPressed: () {
clearFormInfo();
},
),
You need to check if the imageFile is null. If it is, then you can use a simple Container until imageFile is not null. Try something like this:
Container(
height: 230.0,
width: MediaQuery.of(context).size.width * 0.8,
child: Center(
child: AspectRatio(
aspectRatio: 16 / 9,
child: imageFile != null // Here is important!
? Container(
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(imageFile),
fit: BoxFit.cover,
),
),
)
: Container(),
),
),
),
displayAdminUploadFormScreen() is a function and not a widget right?
So does this function can get the value of imageFile correctly?
Try to print the imageFile at the start of this function, to check if you get the image path correctly.
displayAdminUploadFormScreen(){
print(imagePath);
.
// rest of code
.
}
If the result is null, then then you might need to pass the imageFile
displayAdminUploadFormScreen(File imageFile){
print(imagePath);
.
// rest of cocde
.
}
and
#override
Widget build(BuildContext context) {
return imageFile == null
? displayAdminHomeScreen()
: displayAdminUploadFormScreen(imageFile);
}
Have you set the permissions inside Info.plist and AndroidManifest.xml files?
Inside **/path/to/project/android/app/src/[debug/main/profile] you need to put something like
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Inside **/path/to/project/ios/Runnmer/Info.plist you need to put something like
<key>NSCameraUsageDescription</key>
<string>camera</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photos for this</string>
I'm not 100% sure on the Android one as I haven't developed for Android yet, but i'm sure that's true of the iOS version.
I'm using Flutter's Image Picker plugin in order to allow the user to change their avatar. When they go to their account page they see their regular avatar photo with a camera icon on top. Clicking the camera icon will allow them to either take a photo from their camera or choose a new avatar from their gallery. After choosing the new one, the avatar photo automatically updates. However, when navigating away from their account page, the old avatar is visible throughout the rest of the app. I'm using Provider with a Change Notifier and Consumers for Avatars everywhere else. The problem though is that I can only access the Provider within a build so I don't know where I can call the Provider in my code. Add to this the fact that the Avatar I'm using all around the app comes from an internet url. After choosing with Image Picker, the new avatar photo gets uploaded to a server. The name of the new photo replaces the name of the old photo. Hence my app doesn't even know anything changed. Even reloading the pages doesn't work. However if I hot restart my app, the new avatar photo appears. Any ideas what I can do?
Here's the Image Picker code;
class Picker extends StatefulWidget {
Picker({Key key, this.title}) : super(key: key);
final String title;
#override
_PickerState createState() => _PickerState();
}
class _PickerState extends State<Picker>
with TickerProviderStateMixin,ImagePickerListener{
File _image;
AnimationController _controller;
ImagePickerHandler imagePicker;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
imagePicker= ImagePickerHandler(this,_controller);
imagePicker.init();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var socialProvider = Provider.of<SocialProvider>(context);
return Container(
child: GestureDetector(
onTap: () => imagePicker.showDialog(context),
child: Center(
child: Stack(
children: <Widget>[
Center(
child: _image == null?
Consumer<SocialProvider>(
builder: (context, socialProvider, child) {
return
Image.network(socialProvider.currentavatar,
width: 200,
height: 200,
);
}) :
Container(
height: 200.0,
width: 200.0,
decoration: BoxDecoration(
color: Colors.grey,
image: DecorationImage(
image: FileImage(_image),
fit: BoxFit.cover,
),
),
),
),
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: Container(
color: Colors.black26,
child: Icon(Icons.camera_alt,
color: Colors.white,
size: 40,
),
),
),
),
,
)
),
),
);
}
#override
userImage(File _image) async{
setState(() {
this._image = _image;
});
}
}
Currently the Consumers are correctly updating the avatars throughout the app whenever a user obtains a new avatar through logging in via social media. The new avatar is uploaded to the server and the ChangeNotifier is informed. The code for the Provider here is ;
Future<void> postSocialData(String avatar) async {
final url = "http://example.com/example.php¤tavatar=" + $avatar;
final response = await http.get(url);
if (response.statusCode == 200) {
currentavatar = "http://example.com/user.jpg";
var box = await Hive.openBox('currentuser');
box.put('currentavatar', "http://example.com/user.jpg",);
notifyListeners();
}
}
So I tried putting this into my Provider and calling it from an onTap function in the Image Picker build. Here's the onTap function;
GestureDetector(
onTap: () async {
String avatar = await _listener.openGallery(socialProvider.currentuserid);
String updatedavatar = "http://example.com/" + avatar;
socialProvider.updateAvatar(updatedavatar);
},
child: roundedButton(
"Gallery",
EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 0.0),
const Color(0xFF167F67),
const Color(0xFFFFFFFF)),
),
And here's the Provider it calls;
Future<void> updateAvatar(String avatar) async {
var box = await Hive.openBox('currentuser');
box.put('currentavatar', avatar);
currentavatar = avatar;
notifyListeners();
}
But that didn't update the consumers with the new avatar. I guess because the external url for the avatar hasn't changed as the photo has simply been replaced and keeps the same name.
Using Hive's listener was a good idea. But it didn't help because it turns out the uploaded image - having the same URL as the replaced image - isn't being refreshed on the server-side. So the caching must be sorted out on the web server.
class CustomCircleAvatar extends StatefulWidget {
final Image myImage;
final String initials;
CustomCircleAvatar({this.myImage, this.initials});
#override
_CustomCircleAvatarState createState() => new _CustomCircleAvatarState();
}
class _CustomCircleAvatarState extends State {
bool _checkLoading = true;
#override
void initState() {
super.initState();
widget.myImage.image.resolve(new ImageConfiguration()).addListener(
ImageStreamListener((ImageInfo info, bool synchronousCall) {
if (mounted) {
setState(() {
_checkLoading = false;
});
}
}));
}
#override
Widget build(BuildContext context) {
return _checkLoading == true
? new CircleAvatar(
child: new Text(
widget.initials,
style: TextStyle(fontSize: 60),
))
: new CircleAvatar(
backgroundImage: widget.myImage.image,
);
}
}
Positioned _profilePhoto(BuildContext context) {
return Positioned(
bottom: -70,
child: Container(
width: 150.0,
height: 150.0,
padding: EdgeInsets.all(3.0),
decoration: BoxDecoration(color: Colors.white, shape: BoxShape.circle),
child: CustomCircleAvatar(
myImage: Image.network(sellerPicture), // This sellerPicture i got from sharedPreferences
initials: '$sellerName'.substring(0, 1).toUpperCase(),
),
),
);
}
help me, the image Show from URL but the terminal say that URL != null is not true
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building ProfileScreen(dirty, state: _ProfileScreenState#29296):
'package:flutter/src/painting/_network_image_io.dart': Failed assertion: line 22 pos 14: 'url != null': is not true.
The code seems to be incomplete. If the error appears once but the image from the URL is still displayed on the widget, it's likely that the Widget tries to load the image from url that hasn't been initialized. Possibly during first render of the screen, url is still empty, and on reload (i.e. via setState()), the url has been initialized. A workaround for this issue is to set a default image url in the parameter.
Or add a checker for sellerPicture
Container(
/// Check if sellerPicture is null
child: sellerPicture != null ?
CustomCircleAvatar(
myImage: Image.network(sellerPicture), // This sellerPicture i got from
sharedPreferences
initials: '$sellerName'.substring(0, 1).toUpperCase(),
),
/// if sellerPicture is null, display a default image Widget
/// i.e. image from local Assets
: DefaultImageWidget(),
),
)
This is the code
import 'package:flutter/material.dart';
import 'Authentication.dart';
import 'photoUpload.dart';
import 'Posts.dart';
import 'package:firebase_database/firebase_database.dart';
// import 'package:flutter_blogapp/Authentication.dart';
// import 'package:flutter_blogapp/photoUpload.dart';
class HomePage extends StatefulWidget
{
HomePage
(
{
this.auth,
this.onSignedOut,
}
);
final AuthImplementation auth;
final VoidCallback onSignedOut;
#override
State<StatefulWidget> createState()
{
return _HomePageState();
}
}
class _HomePageState extends State<HomePage>
{
List<Posts> postsList = [];
#override
void initState()
{
super.initState();
DatabaseReference postsRef = FirebaseDatabase.instance.reference().child("Posts");
postsRef.once().then((DataSnapshot snap)
{
var KEYS = snap.value.keys;
var DATA = snap.value;
postsList.clear();
for(var individualKey in KEYS)
{
Posts posts = new Posts
(
DATA[individualKey]['image'],
DATA[individualKey]['desctiption'],
DATA[individualKey]['data'],
DATA[individualKey]['time'],
);
postsList.add(posts);
}
setState(()
{
print('Length : $postsList.length');
});
});
}
void _logoutUser() async
{
try
{
await widget.auth.signOut();
widget.onSignedOut();
}
catch (e)
{
print(e.toString());
}
}
#override
Widget build(BuildContext context)
{
return new Scaffold
(
appBar: new AppBar
(
title: new Text('Home'),
),
body : new Container
(
child: postsList.length == 0 ? new Text(" No Post available ") : new ListView.builder
(
itemCount: postsList.length,
itemBuilder: (_, index)
//itemBuilder: (BuildContext _, int index ) //<-----
{
return PostsUI(postsList[index].image, postsList[index].description, postsList[index].date, postsList[index].time);
}
),
),
bottomNavigationBar: new BottomAppBar
(
color: Colors.pink,
child: new Container
(
margin: const EdgeInsets.only(left: 70.0, right: 70.0),
child: new Row
(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>
[
new IconButton
(
icon: new Icon(Icons.local_car_wash),
iconSize: 50,
color: Colors.white,
onPressed: _logoutUser,
),
new IconButton
(
icon: new Icon(Icons.add_a_photo),
iconSize: 50,
color: Colors.white,
onPressed: ()
{
Navigator.push
(
context,
MaterialPageRoute(builder: (context)
{
return new UploadPhotoPage();
})
);
},
),
],
),
),
),
);
}
// Designing Posts UI <remove from Text field><??'defaut value'>
Widget PostsUI(String image, String description, String date, String time)
{
return new Card
(
elevation: 10.0,
margin: EdgeInsets.all(15.0),
child: new Container
(
padding: new EdgeInsets.all(14.0),
child: new Column
(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>
[
new Row
(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>
[
new Text
(
date,
style: Theme.of(context).textTheme.subtitle,
textAlign: TextAlign.center,
),
new Text
(
time,
style: Theme.of(context).textTheme.subtitle,
textAlign: TextAlign.center,
), //<----
],
),
SizedBox(height: 10.0,),
new Image.network(image, fit:BoxFit.cover),
SizedBox(height: 10.0,),
new Text
(
description, //= null ? "true" : "False", //??'defaut value'
style: Theme.of(context).textTheme.subhead,
textAlign: TextAlign.center,
),
],
)
)
);
}
}
The error that i was getting
The following assertion was thrown building: A non-null String must be
provided to a Text widget. 'package:flutter/src/widgets/text.dart':
Failed assertion: line 285 pos 10: 'data != null'
Either the assertion indicates an error in the framework itself, or we
should provide substantially more information in this error message to
help you determine and fix the underlying cause. In either case,
please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=BUG.md
When the exception was thrown, this was the stack
2 new Text package:flutter/…/widgets/text.dart:285
3 _HomePageState.PostsUI package:flutter_blogapp/HomePage.dart:184
4 _HomePageState.build. package:flutter_blogapp/HomePage.dart:100
5 SliverChildBuilderDelegate.build package:flutter/…/widgets/sliver.dart:446
6 SliverMultiBoxAdaptorElement._build. package:flutter/…/widgets/sliver.dart:1260
What can i do to retrieve the data in my app? I have tried what i can do. can some one help me in finding where am I wrong? I have started learning recently.
First thing, pull the code out of the initState and put in a different function. This will keep your initState clean.
What I can see is you're trying to update data in firestore.
What you could do is define a new function as follows:
final databaseReference = Firestore.instance;
setPostsData() async {
DocumentSnapshot snapshot = await databaseReference
.collection("NAME_OF_COLLECTION")
.document("NAME_OF_DOCUMENT")
.get();
// use this DocumentSnapshot snapshot to get the current data that is there in the document inside of your collection.
var currentData = snapshot.data;
print(currentData); // to check whats actually there and if its working...
//lets assume newPostsList is the data that you want to put in this referenced document.
//to update the data in firestore:
await databaseReference
.collection("NAME_OF_COLLECTION")
.document("NAME_OF_DOCUMENT")
.updateData({newPostsList});
}
Then you can put setPostsData in the initState.
For this to work, you might want to redesign your database structure, for example: it should be like users --> posts --> List_of_all_posts. If did this way, NAME_OF_COLLECTION will be users and NAME_OF_DOCUMENT will be posts.