native_camera_view 0.0.1 copy "native_camera_view: ^0.0.1" to clipboard
native_camera_view: ^0.0.1 copied to clipboard

Native Camera View - Flutter Plugin

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:native_camera_view/native_camera_view.dart'; // Import plugin của bạn
import 'dart:io';
import 'package:permission_handler/permission_handler.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Native Camera View Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isLoading = true;
  bool _isPermissionGranted = false;
  CameraController? _cameraController;
  bool _isCameraPaused = false;

  // Các state này vẫn tồn tại, nhưng CameraPreviewView sẽ được gọi với giá trị cố định
  CameraPreviewFit _currentFit = CameraPreviewFit.cover;
  bool _isFrontCameraSelected = false;

  @override
  void initState() {
    super.initState();
    _requestCameraPermission();
  }

  Future<void> _requestCameraPermission() async {
    PermissionStatus status;
    if (Platform.isAndroid) {
      status = await Permission.camera.request();
    } else if (Platform.isIOS) {
      status = await Permission.camera.status;
      if (status.isDenied || status.isPermanentlyDenied) {
        // Giữ nguyên để UI hiển thị thông báo
      } else {
        status = PermissionStatus.granted;
      }
    } else {
      if (mounted) {
        setState(() => _isLoading = false);
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Nền tảng không được hỗ trợ.')),
        );
      }
      return;
    }

    if (mounted) {
      setState(() {
        _isPermissionGranted = status.isGranted;
        _isLoading = false;
      });
      if (!status.isGranted && Platform.isAndroid) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('Quyền camera bị từ chối (${status.name}). View có thể không hoạt động.')),
        );
      }
    }
  }

  // Đổi tên hàm callback theo yêu cầu
  void _setCameraController(CameraController controller) {
    if (mounted) {
      _cameraController = controller;
      print("Example App: CameraController set!");
      if (_isCameraPaused) {
        _cameraController?.pauseCamera();
      }
    }
  }

  Future<void> _togglePauseResume() async {
    if (_cameraController == null) return;
    if (_isCameraPaused) {
      await _cameraController?.resumeCamera();
    } else {
      await _cameraController?.pauseCamera();
    }
    if (mounted) {
      setState(() => _isCameraPaused = !_isCameraPaused);
    }
  }

  Future<void> _captureImage() async {
    if (_cameraController == null || (_isCameraPaused && !Platform.isIOS )) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(_isCameraPaused ? 'Camera đang tạm dừng.' : 'Controller chưa sẵn sàng.')),
      );
      return;
    }
    final path = await _cameraController?.captureImage();
    if (path != null && mounted) {
      print("Image captured at: $path");
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => DisplayPictureScreen(imagePath: path),
        ),
      );
    } else if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Chụp ảnh thất bại.')),
      );
    }
  }

  Future<void> _switchCamera() async {
    if (_cameraController == null || _isCameraPaused) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(_isCameraPaused ? 'Resume camera trước.' : 'Controller chưa sẵn sàng.')),
      );
      return;
    }
    // Mặc dù CameraPreviewView được gọi với isFrontCamera: false cố định,
    // chúng ta vẫn giữ logic này ở đây để có thể gọi xuống controller nếu cần.
    // Tuy nhiên, UI của CameraPreviewView sẽ không tự động thay đổi theo _isFrontCameraSelected nữa.
    final newIsFront = !_isFrontCameraSelected;
    await _cameraController?.switchCamera(newIsFront);
    if (mounted) {
      setState(() {
        _isFrontCameraSelected = newIsFront; // Cập nhật state cục bộ
      });
      print("Example App: Switched camera state to front: $newIsFront. Note: CameraPreviewView uses fixed params.");
    }
  }

  void _changeCameraFit(CameraPreviewFit? fit) {
    if (fit == null || _isCameraPaused) return;
    if (mounted) {
      setState(() {
        _currentFit = fit; // Cập nhật state cục bộ
      });
      print("Example App: Changed fit mode state to ${fit.name}. Note: CameraPreviewView uses fixed params.");
    }
  }

  Future<void> _deleteAllPhotos() async {
    if (_cameraController == null) return;
    bool? success = await _cameraController?.deleteAllCapturedPhotos();
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(success == true ? 'Đã xóa tất cả ảnh.' : 'Xóa ảnh thất bại hoặc không có ảnh.')),
      );
    }
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Plugin Camera Example'),
        actions: [
          if (_cameraController != null && _isPermissionGranted) ...[
            IconButton(
              icon: Icon(_isCameraPaused ? Icons.play_arrow : Icons.pause),
              tooltip: _isCameraPaused ? 'Resume' : 'Pause',
              onPressed: _togglePauseResume,
            ),
            IconButton(
              icon: const Icon(Icons.switch_camera),
              tooltip: 'Switch Camera',
              onPressed: _isCameraPaused ? null : _switchCamera,
            ),
          ]
        ],
      ),
      body: Column(
        children: [
          if (_isPermissionGranted && (Platform.isAndroid || Platform.isIOS))
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  const Text("Current App Fit Mode:"), // Hiển thị state của app
                  DropdownButton<CameraPreviewFit>(
                    value: _currentFit, // Giá trị từ state của app
                    onChanged: _isCameraPaused ? null : _changeCameraFit,
                    items: CameraPreviewFit.values
                        .map((fit) => DropdownMenuItem(
                      value: fit,
                      child: Text(fit.name),
                    ))
                        .toList(),
                  ),
                ],
              ),
            ),
          Expanded(
            child: Center(
              child: _isLoading
                  ? const CircularProgressIndicator()
                  : _isPermissionGranted
                  ? CameraPreviewView(
                // Không còn key động dựa trên _currentFit và _isFrontCameraSelected nữa
                // vì các tham số này giờ được hardcode.
                // Nếu bạn muốn CameraPreviewView được tạo lại khi các giá trị hardcode này
                // thay đổi (ví dụ, bạn thay đổi chúng trong code và hot reload),
                // bạn có thể giữ lại một ValueKey tĩnh hoặc UniqueKey().
                key: const ValueKey("fixed_camera_preview"), // Hoặc UniqueKey()
                setCameraController: _setCameraController, // Sử dụng tên callback mới
                // Truyền các giá trị cố định theo yêu cầu
                cameraPreviewFit: CameraPreviewFit.contain,
                isFrontCamera: false,
              )
                  : _buildPermissionDeniedUI(),
            ),
          ),
          if (_cameraController != null && _isPermissionGranted)
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: ElevatedButton.icon(
                icon: const Icon(Icons.delete_forever),
                label: const Text("Xóa tất cả ảnh đã chụp"),
                onPressed: _deleteAllPhotos,
                style: ElevatedButton.styleFrom(backgroundColor: Colors.red[400]),
              ),
            ),
        ],
      ),
      floatingActionButton: _isPermissionGranted && !_isCameraPaused
          ? FloatingActionButton(
        onPressed: _captureImage,
        tooltip: 'Chụp ảnh',
        child: const Icon(Icons.camera_alt),
      )
          : null,
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }

  Widget _buildPermissionDeniedUI() {
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Icon(Icons.no_photography_outlined, size: 60, color: Colors.red),
          const SizedBox(height: 16),
          const Text(
            'Quyền truy cập camera là bắt buộc để sử dụng tính năng này.',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 16),
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: () async {
              await openAppSettings();
              _requestCameraPermission();
            },
            child: const Text('Mở Cài đặt ứng dụng'),
          ),
          const SizedBox(height: 12),
          TextButton(
            onPressed: _requestCameraPermission,
            child: const Text('Thử lại xin quyền'),
          ),
        ],
      ),
    );
  }
}

class DisplayPictureScreen extends StatelessWidget {
  final String imagePath;

  const DisplayPictureScreen({super.key, required this.imagePath});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Ảnh đã chụp')),
      body: Center(child: Image.file(File(imagePath))),
    );
  }
}