Home > Software engineering >  How to send image picked with image_picker from child widget to his parent?
How to send image picked with image_picker from child widget to his parent?

Time:07-18

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

CodePudding user response:

Change onPressed like this. The problem is image file picked inside method and ui is not waiting for this method complete it's job. await keyword used to wait for operation result. For more, search about asynchronous programming in dart.

onPressed: () async {
                await _pickImage(ImageSource.gallery);
                widget.onImageSelected(_deviceImage);
                print("_device 2: $_deviceImage"); // <--------------------------- Null
              }, // => _pickImage(ImageSource.gallery),
  • Related