Home > front end >  How to move Future methods from Stateful widget to their own class Flutter
How to move Future methods from Stateful widget to their own class Flutter

Time:05-22

I have several Future methods contained within a Stateful widget. Identical methods appear in different parts of the app. I want to add the methods to one Class instead of rewriting four times in the app but I haven't been able to figure it out because several of the methods call setState to update the UI. The methods are called when users choose an image from their gallery, choose to take a photo, upload the selected image to the database for display in the app, the image is compressed, etc.

class ExampleClass extends StatefulWidget {
  const ExampleClass({Key? key}) : super(key: key);

  @override
  State<ExampleClass> createState() => _ExampleClassState();
}

class _ExampleClassState extends State<ExampleClass> {
  File? file;

  Future<void> _captureImageWithCamera() async {
    Get.back();
    XFile? pickedFile = await ImagePicker().pickImage(
      source: ImageSource.camera,
    );
    setState(() {
      file = File(pickedFile!.path);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

I have only included one of the methods (captureImageWithCamera) assuming that a suggested solution for one could be applied to all of them. If I need to provide more code I will be happy to do so. Any help will be greatly appreciated.

CodePudding user response:

You can return pickedFile from your extracted method, then each class which uses the result can call setState itself, using the returned value.

class ImageCapturer {
  Future<XFile?> captureImageWithCamera() async {
    Get.back();
    XFile? pickedFile = await ImagePicker().pickImage(
      source: ImageSource.camera,
    );
    
    return pickedFile;
  }
}

class ExampleClass extends StatefulWidget {
  const ExampleClass({Key? key}) : super(key: key);

  @override
  State<ExampleClass> createState() => _ExampleClassState();
}

class _ExampleClassState extends State<ExampleClass> {
  File? file;

  Future<void> _captureImageWithCamera() async {
    final file = await ImageCapturer().captureImageWithCamera();

    setState(() {
      this.file = File(file!.path);;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

CodePudding user response:

You could abstract the code away to one function with a callback parameter, like so:

class OtherExampleClass {
  Future<void> captureImageWithCamera(
        void Function(XFile? pickedFile) callback,
    ) async {
    Get.back();
    XFile? pickedFile = await ImagePicker().pickImage(
      source: ImageSource.camera,
    );
    
    callback(pickedFile);
  }
}

class ExampleClass extends StatefulWidget {
  const ExampleClass({Key? key}) : super(key: key);

  @override
  State<ExampleClass> createState() => _ExampleClassState();
}

class _ExampleClassState extends State<ExampleClass> {
  File? file;

  Future<void> _captureImageWithCamera() async {
    await OtherExampleClass().captureImageWithCamera((XFile? pickedFile) {
            setState(() {
                file = File(pickedFile!.path);
            });
        }
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

CodePudding user response:

when you are using third party library to something outside your app like picking a photo, downloading some data, making http calls. consider to create service class for this stuff, service class is basically this: a class with single responsibility, like this class should do only one thing. in your case you should create ImagePickerService the class job is pick an Image and return it to you nothing more, you should not call any flutter framework inside it like setState because it's not his job to update the UI.

class ImagePickerService {
  //make this class singleton so you do not make a new instance every time you want it.
  static final ImagePickerService _instance = ImagePickerService._(); 
  ImagePickerService._();
  factory ImagePickerService() => _instance;

  Future<XFile?> pickImage(ImageSource imageSource) async {
    return await ImagePicker().pickImage(
      source: imageSource,
    );
  }
}

so now when ever you want to pick an Image you just need to call this service like this.

onTap: () async {
    final file = await ImagePickerService().pickImage(ImageSource.camera);
    setState(() {
      // you got the file boss do anything you want
    });
  }

now when you create a new page you just create page and defined some services inside it.

  • Related