grid_camera is a Flutter package that enhances the camera experience with a customizable grid overlay system. Built on top of the official camera plugin, it provides an easy-to-use widget for applications requiring precise photo composition, document scanning, or grid-based image capture.
A Flutter camera package that provides a customizable grid overlay for precise photo composition. Features include adjustable grid rows/columns, aspect ratio control, built-in permission handling, and captured images with grid overlay. Perfect for applications requiring precise photo composition, document scanning, or grid-based image capture.
- Customizable camera preview with adjustable grid overlay
- Configurable grid rows, columns, and appearance
- Flexible aspect ratio control
- Built-in permission handling with customizable UI
- Image capture with grid overlay
- Utility extensions for quick widget styling
- Responsive design and error handling
- Easy integration with existing Flutter apps
- πΈ Document scanning apps
- π Technical photography
- π Measurement applications
- π¨ Design and composition tools
- π± Any app requiring precise photo capture
- πΈ Customizable camera preview with grid overlay
- π― Adjustable grid rows and columns
- π Configurable aspect ratio
- π¨ Customizable grid appearance
- π± Built-in permission handling
- π Image capture with grid overlay
- β‘ Utility extensions for quick widget styling
- π― Gap widget for consistent spacing
- π± Platform Support:
- β Android
- β iOS
This package relies on the official camera plugin. You'll need to configure your project according to the platform-specific requirements.
Add the following permissions to your AndroidManifest.xml
:
<uses-permission android:name="android.permission.CAMERA" />
Add the following keys to your Info.plist
:
<key>NSCameraUsageDescription</key>
<string>Your camera usage description here</string>
For detailed camera setup instructions, please refer to the official camera plugin documentation.
Add this to your package's pubspec.yaml
file:
dependencies:
grid_camera: ^0.1.0
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:grid_camera/grid_camera.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
themeMode: ThemeMode.dark,
darkTheme: ThemeData.dark(),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Uint8List? imageInBytes;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Grid Camera Demo",
style: Theme.of(context).textTheme.titleLarge,
),
const Gap(24),
Container(
height: MediaQuery.of(context).size.width * .8,
width: MediaQuery.of(context).size.width * .8,
decoration: BoxDecoration(
color: Theme.of(context).primaryColorLight.withOpacity(0.5),
borderRadius: BorderRadius.circular(10),
image: imageInBytes == null
? null
: DecorationImage(image: MemoryImage(imageInBytes!)),
),
alignment: Alignment.center,
child: imageInBytes != null
? IconButton.outlined(
onPressed: () {
imageInBytes = null;
setState(() {});
},
icon: const Icon(Icons.delete_forever, size: 48),
)
: IconButton.outlined(
onPressed: () async {
final img = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CameraPage()),
);
if (img == null) return;
imageInBytes = img;
setState(() {});
},
icon: const Icon(Icons.upload, size: 48),
),
),
const Gap(kToolbarHeight),
],
).padXXDefault,
),
);
}
}
class CameraPage extends StatelessWidget {
const CameraPage({super.key});
@override
Widget build(BuildContext context) {
return GridCameraWidget(
onDonePressed: (Uint8List img) => Navigator.pop(context, img),
rowCount: 10,
columnCount: 10,
gridWidth: 0.5,
aspectRatio: 1 / 1,
);
}
}
GridCameraWidget(
rowCount: 4,
columnCount: 4,
gridColor: Colors.white,
gridWidth: 2.0,
aspectRatio: 4/3,
clickPhotoIcon: Icon(Icons.camera),
doneIcon: Icon(Icons.check_circle),
refreshIcon: Icon(Icons.refresh),
cameraLoadingWidget: CircularProgressIndicator(),
onDonePressed: (imageBytes) {
// Handle captured image
},
onCameraAccessDenied: () {
// Handle permission denied
},
otherExceptionTitleText: 'Custom Error Title',
otherExceptionBodyText: 'Custom error message',
otherExceptionOkayText: 'Retry',
);
A simple widget for creating consistent spacing:
// Creates both vertical and horizontal space
const Gap(16);
// In a column
Column(
children: [
Text('Hello'),
Gap(8), // 8.0 pixels of space
Text('World'),
],
);
Convenient extensions for common padding patterns:
// Horizontal padding
myWidget.padXX(16)
// Vertical padding
myWidget.padYY(16)
// Default horizontal padding (16px)
myWidget.padXXDefault
// Bottom padding
myWidget.padYBottom(16)
// Top padding
myWidget.padYTop(16)
// Left padding
myWidget.padXLeft(16)
// Right padding
myWidget.padXRight(16)
// All-side padding
myWidget.padAll(16)
// Expand widget
myWidget.expand
Property | Type | Description |
---|---|---|
rowCount | int | Number of horizontal grid lines + 1 |
columnCount | int | Number of vertical grid lines + 1 |
gridColor | Color? | Color of grid lines |
gridWidth | double | Width of grid lines |
aspectRatio | double | Camera preview aspect ratio |
onDonePressed | Function(Uint8List) | Callback when image is captured |
onCameraAccessDenied | VoidCallback? | Callback when camera permission denied |
onOtherException | VoidCallback? | Callback for other errors |
permissionDeniedWidget | Widget? | Custom widget for permission denied state |
cameraLoadingWidget | Widget? | Custom loading widget |
clickPhotoIcon | Widget? | Custom capture button icon |
doneIcon | Widget? | Custom done button icon |
refreshIcon | Widget? | Custom refresh button icon |
otherExceptionTitleText | String? | Custom error dialog title |
otherExceptionBodyText | String? | Custom error dialog message |
otherExceptionOkayText | String? | Custom error dialog button text |
Common issues and their solutions:
- Camera not initializing: Ensure you've added all required permissions and followed the camera plugin setup.
- Black screen: Check if the camera permission is granted at runtime.
- Grid not visible: Verify that the gridColor contrasts with your camera preview.
- gradle error: To overcome this problem, you'll need to upgrade gradle version to latest version OR uou can use "grid_camera" package's old version (e.g grid_camera: ^0.0.8).
For more specific camera-related issues, please refer to the camera plugin's troubleshooting guide.
Contributions are welcome! If you find a bug or want a feature, please file an issue.
This project is licensed under the MIT License - see the LICENSE file for details.
This package builds upon the camera package, gives grid overlay and easy way to use camera widget.