I am trying to create a profile page where we can select the image from the camera and that image will be set as the profile picture. I am saving the image in the directory I have access to and updating the UI after the image is captured. But when I'm restarting the app the captured image is no longer there and I'm seeing the default placeholder image after every restart. Here is the code for the image picker (using provider):
class ProfileInfoProvider with ChangeNotifier {
// this will hold the image
File? _imageFile;
// this will hold the name
late String _name;
// method to set the profile image
Future<void> setProfileImage({required ImageSource source}) async {
// instantiation image picker
ImagePicker picker = ImagePicker();
// picking the image with the source selected
XFile? pickedImage = await picker.pickImage(source: source);
// if user did pick an image
if (pickedImage != null) {
// converting the pickedImage to the pickedImageFile
File pickedImageFile = File(pickedImage.path);
// getting the image name
String imageName = pickedImage.name;
// getting the locaiton we can store the data in
String dirName = (await getApplicationDocumentsDirectory()).path;
// copying the file to the path
File copiedFile = await pickedImageFile.copy('$dirName/$imageName');
// setting the image
_imageFile = copiedFile;
// notifying the listeners
notifyListeners();
}
}
// setter to set the name
void setName(String name) {
_name = name;
// notifying listeners
notifyListeners();
}
// getter to get the image file
File? get getImageFile {
return _imageFile;
}
// getter to get the name
String get getName {
return _name;
}
}
Here is the drawer where I want the image:
class CustomDrawer extends StatelessWidget {
const CustomDrawer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer(
// this container is the background of the Drawer
child: Container(
height: double.infinity,
color: Theme.of(context).scaffoldBackgroundColor,
// this columnn will hold the First container that holds the name and the image
child: Column(
children: [
// this container holds the name and the image. Using container for a different background
Container(
width: double.infinity,
height: 200, // the dark background height
color: Theme.of(context).primaryColor,
// safe area to make sure that the content does not overlap with the notification bar
child: SafeArea(
// padding for the column
child: Padding(
padding: const EdgeInsets.all(20.0),
// this holds the actual image and name
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Consumer<ProfileInfoProvider>(
builder: (context, profileInfoPorivder, child) {
return CircleAvatar(
radius: 40,
backgroundColor: Theme.of(context).accentColor,
backgroundImage: profileInfoPorivder.getImageFile ==
null
? AssetImage('assets/images/no_profile.jpg')
: FileImage(profileInfoPorivder.getImageFile!)
as ImageProvider,
);
},
),
Text(
'Dipansh Parmar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 18,
),
),
],
),
),
),
),
// this list view holds different options. Wrapping it inside Expanded to give it the full remaining height
Expanded(
child: ListView(
padding: const EdgeInsets.all(0),
children: [
ListTile(
onTap: () {
Navigator.pushNamed(
context,
EditProfilePage.routeName,
);
},
leading: Icon(
Icons.edit,
color: Colors.white,
size: 20,
),
title: Text(
'Edit profile',
style: TextStyle(color: Colors.white),
),
),
],
),
),
],
),
),
);
}
}
Any help will be appreciated.
Related
I have two Lists, imageFileList and values
// contains a list of image files which are captured from the camera page
List<File> imageFileList = widget.imageFileList;
// Values list is filled with boolean values which are defaulted to false and contains the same length as imageFileList
List<bool> values = widget.values;
the imageFileList gets a new File added when user selects a picture from the gallery, and i want to simultaneously add another bool entry to the values List, as such -
Future pickImage() async {
try {
final image =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return;
final imageTemp = File(image.path);
setState(() {
// adding a new image to imageFileList
imageFileList.add(imageTemp);
// adding a new bool to values List
values.add(false);
});
// rebuildAllChildren(context);
} catch (e) {
print(e);
}
}
When i use pickImage(), the file gets added to the imageFileList, but the false bool is not getting added to the values List.
debug output shows this line when i print the lengths of imageFileList and values List -
print("imagelist = " +
imageFileList.length.toString());
print("values = " +
values.length.toString());
// debug output
I/flutter (20710): imagelist = 5
I/flutter (20710): values = 4
any suggestions on what might be wrong?
here is the full code -
Widget build(BuildContext context) {
// contains a list of image files which are captured from the camera page
List<File> imageFileList = widget.imageFileList;
// Values list is filled with boolean values which are defaulted to false and contains the same length as imageFileList
List<bool> values = widget.values;
Future pickImage() async {
try {
final image =
await ImagePicker().pickImage(source: ImageSource.gallery);
if (image == null) return;
final imageTemp = File(image.path);
setState(() {
imageFileList.add(imageTemp);
values.add(false);
});
// rebuildAllChildren(context);
} catch (e) {
print(e);
}
}
getStoragePermissionStatus() async {
await Permission.camera.request();
var status = await Permission.storage.status;
if (status.isGranted) {
log('Camera Permission: GRANTED');
setState(() {
_isStoragePermissionGranted = true;
});
// asking the user to select an image IF they grant storage permission
await pickImage();
print(_isStoragePermissionGranted);
} else {
log('Camera Permission: DENIED');
}
}
return WillPopScope(
onWillPop: (() => onWillPop(context)),
child: Scaffold(
backgroundColor: Color(lightBlueColor),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Captures',
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
Container(
height: returnDeviceHeight(context) / 1.2,
width: returnDeviceWidth(context),
child: ListView.builder(
itemCount: imageFileList.length,
itemBuilder: (BuildContext context, int index) {
String key = imageFileList[index].toString();
return Column(
children: <Widget>[
Container(
width: returnDeviceWidth(context) - 10,
height: returnDeviceHeight(context) / 2,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
color: Colors.black,
border: Border.all(
color: values[index] ? Colors.green : Colors.red,
width: 4,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Center(
child: InkWell(
onTap: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => PreviewScreen(
fileList: imageFileList,
imageFile: imageFileList[index],
),
),
);
},
child: Image.file(
imageFileList[index],
fit: BoxFit.cover,
),
),
),
),
],
),
),
Divider(
height: 2.0,
),
],
);
},
),
),
ReturnSizedBox10(),
InkWell(
onTap: () async {
await getStoragePermissionStatus();
},
child: Container(
width: returnDeviceWidth(context) - 10,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
color: Color(darkBlueColor),
),
child: Center(
child: LightBluefontstyleMont(
text: "Pick an image from gallery", size: 20),
),
),
),
InkWell(
onTap: () async {
print("imagelist = " +
imageFileList.length.toString());
print("values = " +
values.length.toString());
},
child: Container(
width: returnDeviceWidth(context) - 10,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.0)),
color: Colors.white,
),
child: Center(
child: LightBluefontstyleMont(text: "Done", size: 20),
),
),
)
],
),
),
),
);
}
I have tried to with the setState and without it, however both times, only the imageFileList gets the new entry added.
I need to add a new entry to values List when the imageFileList gets a new entry added to it.
I suspect its to do with the widget.values being passed by pointer.
what u can do is copy the value and then assigning it to List<bool> values variable.
change this:
List<bool> values = widget.values;
to this:
List<bool> values = widget.values.toList();
Well, turns out every time I add a new image to the imageFileList, the values List gets reset; to the value it had in widget.values.
Fixed it by using initState -
late List<File> imageFileList;
late List<dynamic> values;
#override
void initState() {
// contains a list of image files which are captured from the camera page
imageFileList = widget.imageFileList;
// Values list is filled with boolean values which are defaulted to false and contains the same length as imageFileList
values = widget.values.toList();
// TODO: implement initState
super.initState();
}
I'm attempting to upload URL posterImg in the createGroup method but I get the following error "The argument type 'String' can't be assigned to the parameter type 'File"
To provide more context, I'm currently capturing the URL of images stored online using the variable posterImg, I then want to convert the path to a file without storing the image locally and then saving it to a file so it can be uploaded to firebase.
In the snippet below I have tried casting posterImg to image (which is of the type file) but it fails.
Can anyone advise how to modify my code to pass a URL stored in posterImg to the Firebase??
Please can anyone share the festive sprite and up with this problem, as I'm pulling my hair out.
class _CreateGroupScreenState extends ConsumerState<CreateGroupScreen> {
final TextEditingController groupNameController = TextEditingController();
File? image;
late String name;
late String posterImg = "";
void selectImage2() async {
image = await pickImageFromGallery(context);
setState(() {});
}
Future<void> createGroup() async {
bool groupLive;
if (await groupExist(context)) {
groupLive = true;
} else {
groupLive = false;
}
if (!groupLive && groupNameController.text
.trim()
.isNotEmpty && image != null) {
ref.read(groupControllerProvider).createGroup(
context,
name,
posterImg!,
ref.read(selectedGroupContacts),
);
ref.read(selectedGroupContacts.state).update((state) => []);
Navigator.pop(context);
}
}
#override
void dispose() {
super.dispose();
groupNameController.dispose();
}
Future<bool> groupExist(context) async {
var groupRepository = ref.read(groupRepositoryProvider);
var groupExists = await groupRepository.groupExists(widget.groupContainer.mid);
return groupExists;
}
#override
Widget build(BuildContext context) {
name = widget.groupContainer.name;
if (widget.groupContainer.img != null) {
posterImg = widget.groupContainer.img;
} else {
posterImg = '';
}
return Scaffold(
appBar: AppBar(
title: const Text('Create Group'),
),
body: Center(
child: Column(
children: [
const SizedBox(height: 10),
Stack(
children: [
image == null
? const CircleAvatar(
//backgroundImage: NetworkImage(posterImg),
backgroundImage: AssetImage('assets/nobody.png'),
//backgroundImage: AssetImage(Assets),
radius: 64,
)
: CircleAvatar(
backgroundImage: FileImage(
// posterImg,
//widget.groupContainer.img,
image!,
),
radius: 64,
),
Positioned(
bottom: -10,
left: 80,
child: IconButton(
onPressed: selectImage,
icon: const Icon(
Icons.add_a_photo,
),
),
),
],
),
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
controller: groupNameController,
decoration: const InputDecoration(
hintText: 'Enter Group Name',
),
),
),
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.all(8),
child: const Text(
'Select Contacts',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
const SelectContactsGroup(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: createGroup,
backgroundColor: tabColor,
child: const Icon(
Icons.done,
color: Colors.white,
),
),
);
}
}
There is no way to upload a file to Cloud Storage through the Firebase SDKs from a URL. The only options are to upload from a local file, a blob, or a base64 encoded string with the binary data.
If you have only a URL of the file you want to store in Cloud Storage, you will have to load the data into your application and from there upload it to Cloud Storage.
Also see:
Upload image from URL to Firebase Storage using flutter
How to upload an image to storage firebase from an url (for Android, but the same problem)
I am currently able to share the text on top of the image using share package but I want to share the image and text along with it without my icons of refresh and share. Couldn't find any answers for this. Have used multiple share packages but couldn't achieve the expected result.
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: <Widget>[
Container(
constraints: BoxConstraints.expand(),
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage("${imgUrl}$count"),
fit: BoxFit.fill)
),
),
FutureBuilder<Advice>(
future: advice,
builder: (context, snapshot) {
if (snapshot.hasData) {
return SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 30.0),
child: FadeTransition(
opacity: _animation,
child: Text(
snapshot.data!.adviceText,
style: TextStyle(
decoration: TextDecoration.none,
fontSize: 30.0,
color: Colors.white,
fontFamily: 'quoteScript'),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh, color: Colors.white),
onPressed: () async {
setState(() {
_controller.reset();
_controller.forward();
count++;
advice = fetchAdvice();
});
},
),
IconButton(
icon: Icon(Icons.share, color: Colors.white),
onPressed: () {
Share.share("Here's an advice for you: ${snapshot.data!.adviceText}");
},
),
],
),
],
),
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return Center(child: CircularProgressIndicator());
},
),
]),
);
}
This is the text and image widget. Can be shared with the dynamic text.
SHARE: The best way I think is to use screenshot package to capture the widget which contains both image and text that you need to share, then share it as a picture with share_plus package.
For instance:
// Create a controller
ScreenshotController screenshotController = ScreenshotController();
[...]
// Wrap the widget which you want to capture with `Screenshot`
Screenshot<Widget>(
controller: screenshotController,
child: Container(
child: [...]
),
),
[...]
// Create a method to take a screenshot and share it
// This method I get from my project, so you can modify it to fit your purpose
Future<void> shareScreen(String title, String name) async {
final screenShot = await screenshotController.capture();
if (screenShot != null) {
final Directory tempDir = await getTemporaryDirectory();
final File file = await File('${tempDir.path}/$name').create();
await file.writeAsBytes(screenShot);
await Share.shareFiles(<String>[file.path], subject: title, text: name);
file.delete();
}
}
Then replace the Share.share method in the below example with the shareScreen method just created above.
HIDE BUTTON: You can create a new variable like bool isSharing = false; to control the visible state of those 2 buttons. The important part is the Share.share method must be an async method to make the code works because it needs await to know when the share action is done.
For instance:
[...]
if (!isSharing) // <--- Add this line
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh, color: Colors.white),
onPressed: () async {
setState(() {
_controller.reset();
_controller.forward();
count++;
advice = fetchAdvice();
});
},
),
IconButton(
icon: Icon(Icons.share, color: Colors.white),
// And modify here <---
onPressed: () async {
setState(() => isSharing = true); // Hide the buttons
await Share.share(
"Here's an advice for you: ${snapshot.data!.adviceText}");
setState(() => isSharing = false); // Show the buttons
},
),
],
),
[...]
I am able to only the Gallery image or photo but not going to the camera and clicking image in flutter. Please fix my issue
Here is my code
Widget build(BuildContext context) {
super.build(context);
r
child: Text('Select the Device Photo',
style: TextStyle(fontSize: 26),),
),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: new GestureDetector(
onTap: () {
_imagePosition = 0;
getGallery();
getCamera();
},
child: Container(
width: 184,
height: 180,
child: Card(
child: (_imageList[0].path != '')
? Image.file(_imageList[0], fit: BoxFit.fill,)
: Icon(Icons.add_photo_alternate,
size: 130, color: Colors.grey[700])
),),
),
),
],
),
),
),
],
),
);
}
Future getGallery() async {
// Get image from gallery.
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
_cropImage(image);
});
}
Future getCamera() async {
// Get image from Camera.
File image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
_image = image;
_cropImage(image);
});
}
I want to pick Image from Gallery and camera and save into sqlite. Everything is working fine i.e my gallery image is saving but not picking image from camera.
I'm trying to open a PDF dynamically and not from a static string, so that I can upload multiple pdf's and it opens whichever the user has selected. It instantiates using the FullPDFViewerScreen widget below, and I'd like to be able to pass other PDF's and change the title also subjective to the PDF chosen.
Here is my class for it:
import 'data.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_full_pdf_viewer/full_pdf_viewer_scaffold.dart';
import 'dart:typed_data';
class Detail extends StatelessWidget {
final Book book;
Detail(this.book);
final String _documentPath = 'PDFs/test-en.pdf';
#override
Widget build(BuildContext context) {
Future<String> prepareTestPdf() async {
final ByteData bytes =
await DefaultAssetBundle.of(context).load(_documentPath);
final Uint8List list = bytes.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final tempDocumentPath = '${tempDir.path}/$_documentPath';
final file = await File(tempDocumentPath).create(recursive: true);
file.writeAsBytesSync(list);
return tempDocumentPath;
}
//app bar
final appBar = AppBar(
elevation: .5,
title: Text(book.title),
);
///detail of book image and it's pages
final topLeft = Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(16.0),
child: Hero(
tag: book.title,
child: Material(
elevation: 15.0,
shadowColor: Colors.yellow.shade900,
child: Image(
image: AssetImage(book.image),
fit: BoxFit.cover,
),
),
),
),
text('${book.pages} pages', color: Colors.black38, size: 12)
],
);
///detail top right
final topRight = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
text(book.title,
size: 16, isBold: true, padding: EdgeInsets.only(top: 16.0)),
text(
'by ${book.writer}',
color: Colors.black54,
size: 12,
padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
),
Row(
children: <Widget>[
text(
book.price,
isBold: true,
padding: EdgeInsets.only(right: 8.0),
),
],
),
SizedBox(height: 32.0),
Material(
borderRadius: BorderRadius.circular(20.0),
shadowColor: Colors.blue.shade200,
elevation: 5.0,
child: MaterialButton(
onPressed: () {
// We need to prepare the test PDF, and then we can display the PDF.
prepareTestPdf().then(
(path) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullPdfViewerScreen(path)),
);
},
);
},
minWidth: 160.0,
color: Colors.blue,
child: text('Read Now', color: Colors.white, size: 13),
),
)
],
);
final topContent = Container(
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Flexible(flex: 2, child: topLeft),
Flexible(flex: 3, child: topRight),
],
),
);
///scrolling text description
final bottomContent = Container(
height: 220.0,
child: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Text(
book.description,
style: TextStyle(fontSize: 13.0, height: 1.5),
),
),
);
return Scaffold(
appBar: appBar,
body: Column(
children: <Widget>[topContent, bottomContent],
),
);
}
//create text widget
text(String data,
{Color color = Colors.black87,
num size = 14,
EdgeInsetsGeometry padding = EdgeInsets.zero,
bool isBold = false}) =>
Padding(
padding: padding,
child: Text(
data,
style: TextStyle(
color: color,
fontSize: size.toDouble(),
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
),
);
}
class FullPdfViewerScreen extends StatelessWidget {
final String pdfPath;
FullPdfViewerScreen(this.pdfPath);
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
appBar: AppBar(
title: Text("Document"),
),
path: pdfPath);
}
}
main.dart where it generates the route for the detail page. I need to access the books.path that's where the pdf route is mentioned for each book.
generateRoute(RouteSettings settings) {
final path = settings.name.split('/');
final title = path[1];
Book book = books.firstWhere((it) => it.title == title);
return MaterialPageRoute(
settings: settings,
builder: (context) => Detail(book),
);
}
Update:
It seems, I misunderstood you question.
You just need to pass title and path as a parameter.
Change:
final Book book;
Detail(this.book);
final String _documentPath = 'PDFs/test-en.pdf';
To:
final Book book;
final String documentPath;
Detail(this.book, this.documentPath);
And you can get Title from book.title variable
If you want to get the pdf file from URL, you can use flutter_downloader widget.
Here is a working example.
You can pass the list of files with their file name and url.
Download them using:
task.taskId = await FlutterDownloader.enqueue(
url: task.link,
headers: {"auth": "test_for_sql_encoding"},
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true);
And check if they are completed with:
if(item.task.status == DownloadTaskStatus.complete)
When completed, you can pass _localPath/fileName to FullPdfViewerScreen to open the file. (_localPath is the path you used while downloading the file.
You should also pass the path as parameter. Like this;
class Detail extends StatelessWidget {
final Book book;
final String documentPath; // <-- Add this line
Detail(this.book, this.documentPath); <-- Edit this line
#override
Widget build(BuildContext context) {
Future<String> prepareTestPdf() async {
final ByteData bytes =
await DefaultAssetBundle.of(context).load(documentPath); // Also edit your path
final Uint8List list = bytes.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final tempDocumentPath = '${tempDir.path}/$documentPath';
final file = await File(tempDocumentPath).create(recursive: true);
file.writeAsBytesSync(list);
return tempDocumentPath;
}
...
On your generateRoute, you should do like this;
generateRoute(RouteSettings settings) {
final path = settings.name.split('/');
final title = path[1];
Book book = books.firstWhere((it) => it.title == title);
return MaterialPageRoute(
settings: settings,
builder: (context) => Detail(book,settings.name), // <-- This is your path as parameter.
);
}