أداة إنها في الأساس عبارة عن زر عمل عائم يقوم بتشغيل واجهة تسجيل لمدة 30 ثانية فلاتر

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:leaderboard/features/voiceComments/repostiroies/comment_data_firestore.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math' as math;

class RecordingPage extends ConsumerStatefulWidget {
  final String newsId;

  const RecordingPage({Key? key, required this.newsId}) : super(key: key);

  @override
  ConsumerState<RecordingPage> createState() => _RecordingPageState();
}

class _RecordingPageState extends ConsumerState<RecordingPage>
    with SingleTickerProviderStateMixin {
  FlutterSoundRecorder? _recorder;
  bool _isRecording = false;
  int _remainingSeconds = 30; // Changed to 30 seconds
  Timer? _timer;
  late AnimationController _animationController;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _initRecorder();
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 30), // Changed to 30 seconds
    );
    _animation = Tween<double>(begin: 1, end: 0).animate(_animationController);
  }

  Future<void> _initRecorder() async {
    final status = await Permission.microphone.request();
    if (status != PermissionStatus.granted) {
      throw RecordingPermissionException('Microphone permission not granted');
    }

    _recorder = FlutterSoundRecorder();
    await _recorder!.openRecorder();
  }

  void _startRecording() async {
    if (_recorder == null) return;

    await _recorder!.startRecorder(toFile: 'audio.aac');
    _startTimer();
    _animationController.forward();

    setState(() {
      _isRecording = true;
      _remainingSeconds = 30; // Reset to 30 seconds
    });
  }

  void _stopRecording() async {
    if (_recorder == null) return;

    final path = await _recorder!.stopRecorder();
    _timer?.cancel();
    _animationController.stop();

    if (path != null) {
      final voiceCommentProvider = ref.read(voiceCommentFirestoreProvider);
      await voiceCommentProvider.addVoiceComment(
        newsId: widget.newsId,
        audioFilePath: path,
      );
    }

    setState(() {
      _isRecording = false;
    });

    Navigator.of(context).pop(); // Close the overlay
  }

  void _startTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        if (_remainingSeconds > 0) {
          _remainingSeconds--;
        } else {
          _stopRecording();
        }
      });
    });
  }

  @override
  void dispose() {
    _recorder?.closeRecorder();
    _timer?.cancel();
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black.withOpacity(0.5),
      child: Center(
        child: Container(
          width: 200,
          height: 200,
          decoration: BoxDecoration(
            color: Colors.white,
            shape: BoxShape.circle,
          ),
          child: AnimatedBuilder(
            animation: _animationController,
            builder: (context, child) {
              return CustomPaint(
                painter: RecordingProgressPainter(
                  animation: _animation,
                  backgroundColor: Colors.grey[300]!,
                  valueColor: Colors.blue,
                ),
                child: child,
              );
            },
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  '${(_remainingSeconds ~/ 60).toString().padLeft(2, '0')}:${(_remainingSeconds % 60).toString().padLeft(2, '0')}',
                  style: const TextStyle(
                      fontSize: 24, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 20),
                GestureDetector(
                  onTap: _isRecording ? _stopRecording : _startRecording,
                  child: Icon(
                    _isRecording ? Icons.stop : Icons.mic,
                    size: 50,
                    color: _isRecording ? Colors.red : Colors.blue,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class RecordingProgressPainter extends CustomPainter {
  RecordingProgressPainter({
    required this.animation,
    required this.backgroundColor,
    required this.valueColor,
  }) : super(repaint: animation);

  final Animation<double> animation;
  final Color backgroundColor;
  final Color valueColor;

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = backgroundColor
      ..strokeWidth = 10.0
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke;

    canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);

    paint.color = valueColor;
    double progress = (1.0 - animation.value) * 2 * math.pi;

    canvas.drawArc(
      Offset.zero & size,
      -math.pi / 2,
      progress,
      false,
      paint,
    );
  }

  @override
  bool shouldRepaint(RecordingProgressPainter oldDelegate) {
    return animation != oldDelegate.animation ||
        backgroundColor != oldDelegate.backgroundColor ||
        valueColor != oldDelegate.valueColor;
  }
}

RAW Paste Data

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:leaderboard/features/voiceComments/repostiroies/comment_data_firestore.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math' as math;

class RecordingPage extends ConsumerStatefulWidget {
  final String newsId;

  const RecordingPage({Key? key, required this.newsId}) : super(key: key);

  @override
  ConsumerState<RecordingPage> createState() => _RecordingPageState();
}

class _RecordingPageState extends ConsumerState<RecordingPage>
    with SingleTickerProviderStateMixin {
  FlutterSoundRecorder? _recorder;
  bool _isRecording = false;
  int _remainingSeconds = 30; // Changed to 30 seconds
  Timer? _timer;
  late AnimationController _animationController;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _initRecorder();
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 30), // Changed to 30 seconds
    );
    _animation = Tween<double>(begin: 1, end: 0).animate(_animationController);
  }

  Future<void> _initRecorder() async {
    final status = await Permission.microphone.request();
    if (status != PermissionStatus.granted) {
      throw RecordingPermissionException('Microphone permission not granted');
    }

    _recorder = FlutterSoundRecorder();
    await _recorder!.openRecorder();
  }

  void _startRecording() async {
    if (_recorder == null) return;

    await _recorder!.startRecorder(toFile: 'audio.aac');
    _startTimer();
    _animationController.forward();

    setState(() {
      _isRecording = true;
      _remainingSeconds = 30; // Reset to 30 seconds
    });
  }

  void _stopRecording() async {
    if (_recorder == null) return;

    final path = await _recorder!.stopRecorder();
    _timer?.cancel();
    _animationController.stop();

    if (path != null) {
      final voiceCommentProvider = ref.read(voiceCommentFirestoreProvider);
      await voiceCommentProvider.addVoiceComment(
        newsId: widget.newsId,
        audioFilePath: path,
      );
    }

    setState(() {
      _isRecording = false;
    });

    Navigator.of(context).pop(); // Close the overlay
  }

  void _startTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        if (_remainingSeconds > 0) {
          _remainingSeconds--;
        } else {
          _stopRecording();
        }
      });
    });
  }

  @override
  void dispose() {
    _recorder?.closeRecorder();
    _timer?.cancel();
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black.withOpacity(0.5),
      child: Center(
        child: Container(
          width: 200,
          height: 200,
          decoration: BoxDecoration(
            color: Colors.white,
            shape: BoxShape.circle,
          ),
          child: AnimatedBuilder(
            animation: _animationController,
            builder: (context, child) {
              return CustomPaint(
                painter: RecordingProgressPainter(
                  animation: _animation,
                  backgroundColor: Colors.grey[300]!,
                  valueColor: Colors.blue,
                ),
                child: child,
              );
            },
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  '${(_remainingSeconds ~/ 60).toString().padLeft(2, '0')}:${(_remainingSeconds % 60).toString().padLeft(2, '0')}',
                  style: const TextStyle(
                      fontSize: 24, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 20),
                GestureDetector(
                  onTap: _isRecording ? _stopRecording : _startRecording,
                  child: Icon(
                    _isRecording ? Icons.stop : Icons.mic,
                    size: 50,
                    color: _isRecording ? Colors.red : Colors.blue,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class RecordingProgressPainter extends CustomPainter {
  RecordingProgressPainter({
    required this.animation,
    required this.backgroundColor,
    required this.valueColor,
  }) : super(repaint: animation);

  final Animation<double> animation;
  final Color backgroundColor;
  final Color valueColor;

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = backgroundColor
      ..strokeWidth = 10.0
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke;

    canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);

    paint.color = valueColor;
    double progress = (1.0 - animation.value) * 2 * math.pi;

    canvas.drawArc(
      Offset.zero & size,
      -math.pi / 2,
      progress,
      false,
      paint,
    );
  }

  @override
  bool shouldRepaint(RecordingProgressPainter oldDelegate) {
    return animation != oldDelegate.animation ||
        backgroundColor != oldDelegate.backgroundColor ||
        valueColor != oldDelegate.valueColor;
  }
}

Leave a Comment

Your email address will not be published.