I'm trying to send a picture selected with image_picker from child to his parent, the child widget is PhotoPicker and the parent is UpdateInformationScreen. For that I've tried to use a callback but the File _deviceImage is null outside _pickImage method and I don't know why.
Inside _pickImage, _deviceImage is not null:
Future _pickImage(ImageSource source) async {
try {
XFile? pickedFile = await ImagePicker().pickImage(source: source);
if (pickedFile != null) {
setState(() {
_deviceImage = File(pickedFile.path);
print("_device 1: $_deviceImage"); // <--------------------------- Not null
});
}
} on PlatformException catch (e) {
Utils.showErrorMessage(e.message);
}
}
But in the onPressed it's null
TextButton.icon(
onPressed: () {
_pickImage(ImageSource.gallery);
widget.onImageSelected(_deviceImage);
print("_device 2: $_deviceImage"); // <--------------------------- Null
}, // => _pickImage(ImageSource.gallery),
icon: const Icon(
Icons.photo,
size: 30,
color: googleButtonTextColor,
),
label: const Text(
gallery,
style: TextStyle(
fontSize: 18,
color: googleButtonTextColor,
),
),
),
Child widget:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/constant/text.dart';
import 'package:flutter_test/utils/utils.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import '../constant/color.dart';
import '../model/instructor.dart';
class PhotoPicker extends StatefulWidget {
final Function(File?) onImageSelected;
const PhotoPicker({Key? key, required this.onImageSelected}) : super(key: key);
@override
State<PhotoPicker> createState() => _PhotoPickerState();
}
class _PhotoPickerState extends State<PhotoPicker> {
File? _deviceImage;
//----------------------------------------------------------------------------
//----------------------- Pick image from gallery or camera ------------------
//----------------------------------------------------------------------------
Future _pickImage(ImageSource source) async {
try {
XFile? pickedFile = await ImagePicker().pickImage(source: source);
if (pickedFile != null) {
setState(() {
_deviceImage = File(pickedFile.path);
print("_device 1: $_deviceImage"); <--------------------------- Not null
});
}
} on PlatformException catch (e) {
Utils.showErrorMessage(e.message);
}
}
@override
Widget build(BuildContext context) {
final instructor = ModalRoute.of(context)!.settings.arguments as Instructor;
return Column(
children: [
SizedBox(
child: _deviceImage != null
? CircleAvatar(
radius: 90,
backgroundImage: Image.file(
_deviceImage!,
fit: BoxFit.cover,
).image
)
: CircleAvatar(
radius: 90,
backgroundImage: instructor.photo != null
? NetworkImage(instructor.photo.toString())
: const AssetImage(
"assets/images/no_photo.jpg"
)
as ImageProvider,
),
),
const SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton.icon(
onPressed: () {
_pickImage(ImageSource.gallery);
widget.onImageSelected(_deviceImage);
print("_device 2: $_deviceImage");
}, // => _pickImage(ImageSource.gallery),
icon: const Icon(
Icons.photo,
size: 30,
color: googleButtonTextColor,
),
label: const Text(
gallery,
style: TextStyle(
fontSize: 18,
color: googleButtonTextColor,
),
),
),
TextButton.icon(
onPressed: () {
_pickImage(ImageSource.camera);
widget.onImageSelected(_deviceImage); <--------------------------- Null
},//=> _pickImage(ImageSource.camera),
icon: const Icon(
Icons.photo_camera,
size: 30,
color: googleButtonTextColor,
),
label: const Text(
camera,
style: TextStyle(
fontSize: 18,
color: googleButtonTextColor,
),
),
),
],
),
],
);
}
}
The parent widget:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/utils/utils.dart';
import 'package:flutter_test/view/information_screen.dart';
import 'package:flutter_test/widget/photo_picker.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:provider/provider.dart';
import '../constant/color.dart';
import '../constant/text.dart';
import '../model/instructor.dart';
import '../provider/instructor_view_model.dart';
class UpdateInformationScreen extends StatefulWidget {
static const String ROUTE_NAME = "/updateInformation";
const UpdateInformationScreen({Key? key}) : super(key: key);
@override
State<UpdateInformationScreen> createState() =>
_UpdateInformationScreenState();
}
class _UpdateInformationScreenState extends State<UpdateInformationScreen> {
final firstNameController = TextEditingController();
final phoneNumberController = TextEditingController();
String? phoneNumber;
File? photo;
//----------------------------------------------------------------------------------------------
//----------------------------- Free memory allocated to the existing variables ----------------
//----------------------------------------------------------------------------------------------
@override
void dispose() {
firstNameController.dispose();
phoneNumberController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final instructor = ModalRoute.of(context)!.settings.arguments as Instructor;
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.pushNamed(context, InformationScreen.ROUTE_NAME);
},
),
title: const Text(appTitle),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20,),
PhotoPicker(
onImageSelected: (image) {
print("Image up: $image"); <--------------------------- Null
setState(() {
photo = image;
print("Image up set state: $photo");
});
},
),
const SizedBox(height: 20,),
//----------------------------- First name -------------------------
TextField(
controller: firstNameController,
cursorColor: cursorColor,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: const Icon(
Icons.person,
color: grey,
size: 30,
),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.grey),
borderRadius: BorderRadius.circular(5.5),
),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.orange),
borderRadius: BorderRadius.circular(5.5),
),
labelText: instructor.firstName != null
? instructor.firstName.toString()
: updateFirstnameText,
),
),
const SizedBox(
height: 20,
),
//----------------------------- Phone number -----------------------
IntlPhoneField(
controller: phoneNumberController,
initialValue: instructor.phoneNumber != null
? Utils.phoneNumberWithoutCountryCode(instructor.phoneNumber.toString())
: phoneSignInLabelText,
decoration: InputDecoration(
labelText: instructor.phoneNumber != null
? Utils.phoneNumberWithoutCountryCode(instructor.phoneNumber.toString())
: phoneSignInLabelText,
border: const OutlineInputBorder(
borderSide: BorderSide(),
),
),
initialCountryCode: 'FR',
onChanged: (phone) {
phoneNumber = phone.completeNumber;
print(phone.completeNumber);
},
),
const SizedBox(
height: 20,
),
//----------------------------- Update button ----------------------
ElevatedButton(
onPressed: () {
_clickOnUpdateInformationButton(
context,
instructor.uid!,
//----------------------------- First name -----------------
firstNameController.text.isNotEmpty
? firstNameController.text.trim()
: instructor.firstName!,
//----------------------------- Phone number ---------------
phoneNumberController.text.isNotEmpty
? phoneNumber!
: instructor.phoneNumber!,
);
},
style: ElevatedButton.styleFrom(
fixedSize: const Size(double.infinity, 50),
primary: cursorColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Row(
children: const [
Padding(
padding: EdgeInsets.only(left: 5, right: 100),
child: Icon(
Icons.update,
color: Colors.white,
size: 25,
),
),
Text(
updateInfoButton,
style: TextStyle(
fontSize: 18,
color: Colors.white,
),
),
],
),
),
const SizedBox(
height: 20,
),
],
),
),
);
}
//----------------------------------------------------------------------------
//----------------------------- Click on update information button -----------
//----------------------------------------------------------------------------
void _clickOnUpdateInformationButton(BuildContext context, String uid, String firstName, String phoneNumber) {
final instructorViewModel = Provider.of<InstructorViewModel>(context, listen: false);
instructorViewModel.updateInstructor(uid, firstName, phoneNumber);
Utils.goToInformationScreen(context);
Utils.showSuccessMessage(updateInformationSuccessText);
}
}
Thanks in advance