flutter_mp_pose_landmarker 0.1.3 copy "flutter_mp_pose_landmarker: ^0.1.3" to clipboard
flutter_mp_pose_landmarker: ^0.1.3 copied to clipboard

PlatformAndroid

A Flutter plugin for real-time pose detection using MediaPipe and native CameraX.

example/lib/main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_mp_pose_landmarker/flutter_mp_pose_landmarker.dart';
import 'package:permission_handler/permission_handler.dart';

Future<void> ensureCameraPermission() async {
  var status = await Permission.camera.status;
  if (!status.isGranted) {
    await Permission.camera.request();
  }
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ensureCameraPermission();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Pose Landmarker Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const PoseLandmarkerView(),
    );
  }
}

class PoseLandmarkerView extends StatefulWidget {
  const PoseLandmarkerView({super.key});
  @override
  State<PoseLandmarkerView> createState() => _PoseLandmarkerViewState();
}

class _PoseLandmarkerViewState extends State<PoseLandmarkerView> {
  int delegate = 0; // 0=CPU, 1=GPU
  int model = 1; // 0=Full, 1=Lite, 2=Heavy
  // Confidence parameters
  double _minPoseDetectionConfidence = 0.5;
  double _minPoseTrackingConfidence = 0.5;
  double _minPosePresenceConfidence = 0.5;

  List<PoseLandmarkPoint> _landmarks = [];
  late StreamSubscription<PoseLandMarker> _poseSubscription;

  bool _detectionPaused = false;
  bool _loggingEnabled = true;
  String _cameraLens = "Back";

  int _fps = 0;
  int _frameCount = 0;
  int _lastTimestamp = DateTime.now().millisecondsSinceEpoch;

  final _detectionController = TextEditingController();
  final _trackingController = TextEditingController();
  final _presenceController = TextEditingController();

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

    _detectionController.text = _minPoseDetectionConfidence.toString();
    _trackingController.text = _minPoseTrackingConfidence.toString();
    _presenceController.text = _minPosePresenceConfidence.toString();

    PoseLandmarker.setConfig(
      delegate: delegate,
      model: model,
      minPoseDetectionConfidence: _minPoseDetectionConfidence,
      minPoseTrackingConfidence: _minPoseTrackingConfidence,
      minPosePresenceConfidence: _minPosePresenceConfidence,
    );

    _poseSubscription = PoseLandmarker.poseLandmarkStream.listen((pose) {
      if (!_detectionPaused) {
        setState(() {
          _landmarks = pose.landmarks;

          // FPS calculation
          _frameCount++;
          int now = DateTime.now().millisecondsSinceEpoch;
          if (now - _lastTimestamp >= 1000) {
            _fps = _frameCount;
            _frameCount = 0;
            _lastTimestamp = now;
            if (_loggingEnabled) print("FPS: $_fps");
          }
        });
      }
    });
  }

  void _switchCamera() {
    PoseLandmarker.switchCamera();
    setState(() {
      _cameraLens = _cameraLens == "Back" ? "Front" : "Back";
    });
    if (_loggingEnabled) print("Switched camera to $_cameraLens");
  }

  void _toggleLogging() {
    setState(() {
      _loggingEnabled = !_loggingEnabled;
    });
    print("Logging: $_loggingEnabled");
  }

  void _pauseResumeDetection() {
    setState(() {
      _detectionPaused = !_detectionPaused;
    });
    print(_detectionPaused ? "Detection Paused" : "Detection Resumed");
  }

  void _applyConfidenceSettings() {
    setState(() {
      _minPoseDetectionConfidence =
          double.tryParse(_detectionController.text) ?? 0.5;
      _minPoseTrackingConfidence =
          double.tryParse(_trackingController.text) ?? 0.5;
      _minPosePresenceConfidence =
          double.tryParse(_presenceController.text) ?? 0.5;

      PoseLandmarker.setConfig(
        delegate: delegate,
        model: model,
        minPoseDetectionConfidence: _minPoseDetectionConfidence,
        minPoseTrackingConfidence: _minPoseTrackingConfidence,
        minPosePresenceConfidence: _minPosePresenceConfidence,
      );

      if (_loggingEnabled) {
        print(
            "Updated confidence: detection=$_minPoseDetectionConfidence, tracking=$_minPoseTrackingConfidence, presence=$_minPosePresenceConfidence");
      }
    });
  }

  @override
  void dispose() {
    _poseSubscription.cancel();
    _detectionController.dispose();
    _trackingController.dispose();
    _presenceController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            FloatingActionButton(
              heroTag: "switchCamera",
              child: const Icon(Icons.cameraswitch),
              onPressed: _switchCamera,
            ),
            FloatingActionButton(
              heroTag: "pauseResume",
              child: Icon(_detectionPaused ? Icons.play_arrow : Icons.pause),
              onPressed: _pauseResumeDetection,
            ),
            FloatingActionButton(
              heroTag: "loggingToggle",
              child: const Icon(Icons.bug_report),
              onPressed: _toggleLogging,
            ),
          ],
        ),
      ),
      body: Stack(
        fit: StackFit.expand,
        children: [
          const NativeCameraPreview(),
          Positioned(
            top: 16,
            right: 16,
            width: 150,
            height: 150,
            child: Container(
              decoration: BoxDecoration(
                border: Border.all(color: Colors.white, width: 2),
                color: Colors.black.withOpacity(0.3),
              ),
              child: CustomPaint(
                painter: LandmarkPainter(_landmarks),
              ),
            ),
          ),
          Positioned(
            top: 16,
            left: 16,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  padding: const EdgeInsets.all(8),
                  color: Colors.black54,
                  child: Text(
                    'Landmarks: ${_landmarks.length}',
                    style: const TextStyle(color: Colors.white),
                  ),
                ),
                Container(
                  padding: const EdgeInsets.all(8),
                  color: Colors.black54,
                  child: Text(
                    'Camera: $_cameraLens',
                    style: const TextStyle(color: Colors.white),
                  ),
                ),
                Container(
                  padding: const EdgeInsets.all(8),
                  color: Colors.black54,
                  child: Text(
                    'FPS: $_fps',
                    style: const TextStyle(color: Colors.white),
                  ),
                ),
              ],
            ),
          ),
          // Confidence controls
          Positioned(
            bottom: 16,
            left: 16,
            right: 16,
            child: Container(
              padding: const EdgeInsets.all(8),
              color: Colors.black45,
              child: Column(
                children: [
                  Row(
                    children: [
                      Expanded(
                        child: TextField(
                          controller: _detectionController,
                          decoration: const InputDecoration(
                            labelText: "Detection Confidence",
                            fillColor: Colors.white,
                            filled: true,
                          ),
                          keyboardType:
                              TextInputType.numberWithOptions(decimal: true),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: TextField(
                          controller: _trackingController,
                          decoration: const InputDecoration(
                            labelText: "Tracking Confidence",
                            fillColor: Colors.white,
                            filled: true,
                          ),
                          keyboardType:
                              TextInputType.numberWithOptions(decimal: true),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: TextField(
                          controller: _presenceController,
                          decoration: const InputDecoration(
                            labelText: "Presence Confidence",
                            fillColor: Colors.white,
                            filled: true,
                          ),
                          keyboardType:
                              TextInputType.numberWithOptions(decimal: true),
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  ElevatedButton(
                    onPressed: _applyConfidenceSettings,
                    child: const Text("Apply"),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class NativeCameraPreview extends StatelessWidget {
  const NativeCameraPreview({super.key});
  @override
  Widget build(BuildContext context) {
    return const SizedBox.expand(
      child: AndroidView(
        viewType: 'camera_preview_view',
        layoutDirection: TextDirection.ltr,
      ),
    );
  }
}

class LandmarkPainter extends CustomPainter {
  final List<PoseLandmarkPoint> landmarks;
  LandmarkPainter(this.landmarks);

  static const List<List<int>> connections = [
    [0, 1],
    [1, 2],
    [2, 3],
    [3, 7],
    [0, 4],
    [4, 5],
    [5, 6],
    [6, 8],
    [9, 10],
    [11, 12],
    [11, 13],
    [13, 15],
    [12, 14],
    [14, 16],
    [11, 23],
    [12, 24],
    [23, 24],
    [23, 25],
    [25, 27],
    [24, 26],
    [26, 28],
    [27, 31],
    [28, 32],
    [15, 17],
    [16, 18],
    [17, 19],
    [18, 20],
    [19, 21],
    [20, 22],
  ];

  @override
  void paint(Canvas canvas, Size size) {
    final pointPaint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.fill;

    final linePaint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 2;

    for (var c in connections) {
      if (c[0] < landmarks.length && c[1] < landmarks.length) {
        final a = landmarks[c[0]];
        final b = landmarks[c[1]];
        canvas.drawLine(
          Offset(a.x * size.width, a.y * size.height),
          Offset(b.x * size.width, b.y * size.height),
          linePaint,
        );
      }
    }

    for (var lm in landmarks) {
      canvas.drawCircle(
        Offset(lm.x * size.width, lm.y * size.height),
        4,
        pointPaint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant LandmarkPainter old) =>
      old.landmarks != landmarks;
}
4
likes
150
points
318
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for real-time pose detection using MediaPipe and native CameraX.

Homepage
Repository (GitHub)
View/report issues

Topics

#pose #mediapipe #camera #landmark

Documentation

API reference

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on flutter_mp_pose_landmarker

Packages that implement flutter_mp_pose_landmarker