渲染 0.1.3+1 的记录小部件在颤振中失败

问题描述 投票:0回答:1

我正在尝试做一个允许用户使用相机(实际上使用相机依赖项)的应用程序,在相机屏幕中,我想显示一个必须与视频一起记录的计时器。然后当我想在图库中查看视频时,计时器必须在视频中。我无法使用相机依赖项来做到这一点,所以我尝试使用渲染依赖项,其想法是记录相机预览的小部件和屏幕中显示的计时器。

// ignore_for_file: use_build_context_synchronously, unnecessary_null_comparison, avoid_print

import 'dart:io';

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:video_app/providers/countdown_provider.dart';
import 'package:video_app/providers/countup_provider.dart';
import 'package:video_app/providers/tabata_provider.dart';
import 'package:video_app/screens/timer_screen.dart';
import 'package:video_app/screens/video_screen.dart';
import 'package:render/render.dart';


class CameraPage extends StatefulWidget {
  const CameraPage({super.key, required this.timerType});
  final TimerType timerType;

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

enum WidgetState {none, loading, loaded, error}

class CameraPageState extends State<CameraPage> {
  bool _isRecording = false;
  WidgetState widgetState = WidgetState.none;
  late CameraController _cameraController;
  Offset? _focusPoint;
  bool _isFlashOn = false;
  late MotionRecorder stopController;
  final RenderController renderController = RenderController(logLevel: LogLevel.debug);
  // final renderController = RenderController();
  
  @override
  void initState() {
    
    _initCamera();
    super.initState();
  }

  @override
  void dispose() {
    _cameraController.dispose();
    super.dispose();
  }

  _initCamera() async {
    widgetState = WidgetState.loading;
    final cameras = await availableCameras();
    final back = cameras.firstWhere((camera) => camera.lensDirection == CameraLensDirection.back);
    _cameraController = CameraController(back, ResolutionPreset.max);
    await _cameraController.initialize();
    //setState(() => _isLoading = false);
    if (_cameraController.value.hasError) {
      widgetState = WidgetState.error;
      if (mounted) setState(() {});
    } else {
      widgetState = WidgetState.loaded;
      if (mounted) setState(() {});
    }
  }

  _recordVideo() async {
    if (_isRecording == false) {
      stopController = renderController.recordMotion();
      // await _cameraController.prepareForVideoRecording();
      // await _cameraController.startVideoRecording();
      setState(() => _isRecording = true);
    } else {
      RenderResult result = await stopController.stop();
      setState(() => _isRecording = false);
      File refile = result.output;
      XFile file = XFile(refile.path);
      // final file = await _cameraController.stopVideoRecording();
      // setState(() => _isRecording = false);
      final route = MaterialPageRoute(
        fullscreenDialog: true,
        builder: (_) => VideoPage(file: file),
      );
      Navigator.push(context, route);
    }
  }

  @override
  Widget build(BuildContext context) {
    final countdownProvider = Provider.of<CountDownProvider>(context);
    final countupProvider = Provider.of<CountupProvider>(context);
    final tabataProvider = Provider.of<TabataProvider>(context);
    switch (widgetState) {
      case WidgetState.none:
      case WidgetState.loading:
        return buildScaffold(context, const Text('Iniciando camara...'));
      case WidgetState.loaded:
        return SafeArea(
          child: Scaffold(
            body: LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
                return Stack(
                alignment: Alignment.bottomCenter,
                children: [
                  //

                  Positioned.fill(
                    top: 50,
                    child: AspectRatio(
                      aspectRatio: _cameraController.value.aspectRatio,
                      child: GestureDetector(
                        onTapDown: (TapDownDetails details){
                          final Offset tapPosition = details.localPosition;
                          final Offset relativTapPosition = Offset(
                            tapPosition.dx / constraints.maxWidth,
                            tapPosition.dy / constraints.maxHeight,
                          );
                          _setFocusPoint(relativTapPosition);
                        },
                        child: Render(
                          controller: renderController,
                          child: CameraPreview(_cameraController,
                            child:
                              Positioned(
                                top: 0,
                                right: 10,
                                child: Container(
                                  color: Colors.transparent,
                                  child: Text( widget.timerType != TimerType.tabata 
                                      ? (widget.timerType == TimerType.countdown
                                        ? context.select((CountDownProvider count) => count.timeLeftString)
                                        : context.select((CountupProvider count) => count.timeLeftString)) 
                                      : context.select((TabataProvider count) => count.timeLeftString), 
                                    style: const TextStyle(fontSize: 50, fontWeight: FontWeight.normal, color: Colors.white),),
                                ),
                              ),
                          ),
                        ),
                      ),
                    ),
                  ),
          
                  Positioned(
                      bottom: 0,
                      child: Padding(
                        padding: const EdgeInsets.all(25),
                        child: IconButton(
                          icon: Icon(_isRecording ? Icons.stop : Icons.circle, color: _isRecording ? Colors.black : Colors.red), iconSize: 75,
                          onPressed: ()  {
                            switch (widget.timerType){
                                case TimerType.countdown:
                                  countdownProvider.startStopTimer();
                                  break;
                                case TimerType.countup:
                                  countupProvider.startStopTimer();
                                  break;
                                case TimerType.tabata:
                                  tabataProvider.startStopTimer();
                                  break;
                              }
                            _recordVideo();
                          },
                        ),
                      ),
                  ),
                ],
              );
              },
            ),
          ),
        );
      case WidgetState.error:
        return buildScaffold(context, const Text("¡Ooops! Error al cargar la cámara 😩. Reinicia la apliación."));
    }}

    Widget buildScaffold(BuildContext context, Text text) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text("Cámara"),
        backgroundColor: Colors.transparent,
        elevation: 0,
      ),
      body: Center(
                child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          if(widgetState == WidgetState.loading)
                            const CircularProgressIndicator(),
                          text
                        ],
                      ),
            ),
    );
    }


    
}

我没有找到其他方法来做到这一点。我愿意接受想法。

如果您想要类似应用程序的参考:Google Play 上的“WeTime”或 Google Play 和应用商店中的“WODProof”

flutter video camera rendering
1个回答
0
投票

我能够解决它,问题是当我想将文件传递给 XFile 时

enderResult result = await stopController.stop();
  setState(() => _isRecording = false);
  File refile = result.output;
  XFile file = XFile(refile.path);

删除最后两行并将 result.output 传递到 videoPage (并修改 videoPage 屏幕)

final route = MaterialPageRoute(
    fullscreenDialog: true,
    builder: (_) => VideoPage(file: result.output),
  );

这有效,但视频后来看起来非常滞后,所以如果有人有更好的主意来实现应用程序的此功能,我将非常感激

© www.soinside.com 2019 - 2024. All rights reserved.