Building a Camera App with Flutter.
Tags: flutter
Published: 2020-07-08 00:23:42

Hi Flutter friends, today I want to show you how to build a simple camera application with flutter. So sit tight, “get your code editor” and let's begin.
How will it work?🤔

This is how our simple camera will work. When the user takes a picture, the camera redirects the user to a preview page where he can share it with friends or contacts. Basically we will have two screens, the camera screen and, the preview screen. In the Camera screen, we’ll have two buttons, a capture button and toggle button(to switch the camera direction— front or back). Also in the preview screen, we only need a share icon button by which when tapped prompts a share widget.

## First things first.
We’ll need a couple of packages as we build, so head on to pub.dev and get these packages below:

```
// add theses packages
  camera: ^0.5.8+2
  esys_flutter_share: ^1.0.2
  path: ^1.6.4
  path_provider: ^1.6.11
```
Make sure to run:

`flutter pub get`

👍Now head over to AndroidManifest.xml. You can find this file in app/src/main. Paste the below permission inside your AndroidManifest.xml file just above the application tag:

```
// paste this in
    <uses-permission android:name="android.permission.CAMERA" />
    //
```

👍In the lib folder, create a new folder named screens. Inside it, create a new file and name it camera.dart.
Create a Stateful widget inside the camera.dart file like this below and make sure to import material.dart:

```
import 'package:flutter/material.dart';

class CameraScreen extends StatefulWidget {
  @override
  _CameraScreenState createState() => _CameraScreenState();
}

class _CameraScreenState extends State<CameraScreen> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
```

The first thing we will need to do is to define the following:
— **CameraController**: this is responsible for establishing a connection to the device camera.
— **List cameras**: this will hold a list of all the cameras available on the device. Usually, the length of the list is 2, which is the front and back camera. Index 0 is for the back and index 1 is for the front.
— **int selectedCameraIndex**: this will hold the current camera index the user has selected.
Then we’ll create a method to initialize the camera object which should be done asynchronously hence the return type would be a future.

```
CameraController cameraController;
List cameras;
int selectedCameraIndex;
  
Future initCamera(CameraDescription cameraDescription) async {
    if (cameraController != null) {
      await cameraController.dispose();
    }

    cameraController =
        CameraController(cameraDescription, ResolutionPreset.high);

    cameraController.addListener(() {
      if (mounted) {
        setState(() {});
      }
    });

    if (cameraController.value.hasError) {
      print('Camera Error ${cameraController.value.errorDescription}');
    }

    try {
      await cameraController.initialize();
    } catch (e) {
       String errorText = 'Error ${e.code} \nError message: ${e.description}';
    }

    if (mounted) {
      setState(() {});
    }
  }
```

### Camera Preview Widget
Now, we’ll create a **cameraPreview**: this will return a CameraPreview widget if the camera controller object is initialized successfully else it will return a loading screen.

```
Widget cameraPreview() {
    if (cameraController == null || !cameraController.value.isInitialized) {
      return Text(
        'Loading',
        style: TextStyle(
            color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.bold),
      );
    }

    return AspectRatio(
      aspectRatio: cameraController.value.aspectRatio,
      child: CameraPreview(cameraController),
    );
  }
```

### Camera Control Widget
Next, we’ll create a camera control widget, this widget is responsible for taking pictures when tapped it captures what the camera is viewing.

```
Widget cameraControl(context) {
    return Expanded(
      child: Align(
        alignment: Alignment.center,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            FloatingActionButton(
              child: Icon(
                Icons.camera,
                color: Colors.black,
              ),
              backgroundColor: Colors.white,
              onPressed: () {
                onCapture(context);
              },
            )
          ],
        ),
      ),
    );
  }
```

### Camera Toggle
This widget is responsible for holding the toggle button, which can be used to toggle between the front and back cameras.

```
Widget cameraToggle() {
    if (cameras == null || cameras.isEmpty) {
      return Spacer();
    }

    CameraDescription selectedCamera = cameras[selectedCameraIndex];
    CameraLensDirection lensDirection = selectedCamera.lensDirection;

    return Expanded(
      child: Align(
        alignment: Alignment.centerLeft,
        child: FlatButton.icon(
            onPressed: () {
              onSwitchCamera();
            },
            icon: Icon(
              getCameraLensIcons(lensDirection),
              color: Colors.white,
              size: 24,
            ),
            label: Text(
              '${lensDirection.toString().substring(lensDirection.toString().indexOf('.') + 1).toUpperCase()}',
              style:
                  TextStyle(color: Colors.white, fontWeight: FontWeight.w500),
            )),
      ),
    );
  }
```

Now find below every other widget needed in **camera.dart** file before we dive into the build method. Also, look closely to see the **initState** function. In the **initState** function we call the **initCamera** function inside a future method which checks for device available cameras.

```
@override
  void initState() {
    // TODO: implement initState
    super.initState();
    availableCameras().then((value) {
      cameras = value;
      if(cameras.length > 0){
        setState(() {
          selectedCameraIndex = 0;
        });
        initCamera(cameras[selectedCameraIndex]).then((value) {

        });
      } else {
        print('No camera available');
      }
    }).catchError((e){
      print('Error : ${e.code}');
    });
  }

onCapture(context) async {
    try {
      final p = await getTemporaryDirectory();
      final name = DateTime.now();
      final path = "${p.path}/$name.png";

      await cameraController.takePicture(path).then((value) {
        print('here');
        print(path);
        Navigator.push(context, MaterialPageRoute(builder: (context) =>PreviewScreen(imgPath: path,fileName: "$name.png",)));
      });

    } catch (e) {
      showCameraException(e);
    }
  }

getCameraLensIcons(lensDirection) {
    switch (lensDirection) {
      case CameraLensDirection.back:
        return CupertinoIcons.switch_camera;
      case CameraLensDirection.front:
        return CupertinoIcons.switch_camera_solid;
      case CameraLensDirection.external:
        return CupertinoIcons.photo_camera;
      default:
        return Icons.device_unknown;
    }
  }

  onSwitchCamera() {
    selectedCameraIndex =
        selectedCameraIndex < cameras.length - 1 ? selectedCameraIndex + 1 : 0;
    CameraDescription selectedCamera = cameras[selectedCameraIndex];
    initCamerar(selectedCamera);
  }
```

### The Build Widget
Take a critical look at the build method below, we have created a Scaffold widget which a child of Container. The Container has a child of Stack. Inside the Stack, we’ll add two Align widgets. The first Align widget should have an alignment of center and a child of **cameraPreview()**. The second Align widget will have an alignment of **bottomCenter** and a child of Row. The Row then takes in two widgets which are **cameraToggle()** and **cameraControl()**.

```
@override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        child: Stack(
          children: <Widget>[
            Align(
              alignment: Alignment.center,
              child: cameraPreview(),
            ),
            Align(
              alignment: Alignment.bottomCenter,
              child: Container(
                height: 120,
                width: double.infinity,
                padding: EdgeInsets.all(15),
                color: Colors.transparent,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: <Widget>[
                    cameraToggle(),
                    cameraControl(context),
                    Spacer(),
                  ],
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
```

### Preview Page
We are now done with our Camera Page but left with one more page, the preview page.
Now create a new file inside the screens folder and name it preview.dart. and paste the code below inside it.

```
import 'dart:io';
import 'dart:typed_data';

import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class PreviewScreen extends StatefulWidget {
  final String imgPath;
  final String fileName;
  PreviewScreen({this.imgPath, this.fileName});

  @override
  _PreviewScreenState createState() => _PreviewScreenState();
}

class _PreviewScreenState extends State<PreviewScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: true,
      ),
      body: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Expanded(
              flex: 2,
              child: Image.file(File(widget.imgPath),fit: BoxFit.cover,),
            ),
            Align(
              alignment: Alignment.bottomCenter,
              child: Container(
                width: double.infinity,
                height: 60,
                color: Colors.black,
                child: Center(
                  child: IconButton(
                    icon: Icon(Icons.share,color: Colors.white,),
                    onPressed: (){
                      getBytes().then((bytes) {
                        print('here now');
                        print(widget.imgPath);
                        print(bytes.buffer.asUint8List());
                        Share.file('Share via', widget.fileName, bytes.buffer.asUint8List(), 'image/path');
                      });
                    },
                  ),
                ),
              ),
            )
          ],
        ),
      )
    );
  }

  Future getBytes () async {
    Uint8List bytes = File(widget.imgPath).readAsBytesSync() as Uint8List;
    return ByteData.view(bytes.buffer);
  }
}
```

Now run the app🎉 congratulations you’ve successfully built your first camera app. Also, remember to add this to your resume 😉

You can get the full source code here:
[Github](https://github.com/samuelezedi/flutter_camera_app)

>That’s it for this article! I hope you enjoyed it, and be sure to follow me here Flutter articles and comment for any feedback you might have about this article.