我有一个Flutter应用,可从ESP32相机流式传输视频。我感兴趣的代码应该对图像进行快照。但是,当我按下按钮时,出现以下错误。
我尝试更改代码中的分辨率以及发送流的摄像机的分辨率。两者都给了我同样的错误。
有人可以帮助我吗?
E/flutter (22722): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Exception: Invalid image dimensions.
E/flutter (22722): #0 Scene.toImage (dart:ui/compositing.dart:28:7)
E/flutter (22722): #1 OffsetLayer.toImage (package:flutter/src/rendering/layer.dart:1208:26)
E/flutter (22722): #2 RenderRepaintBoundary.toImage (package:flutter/src/rendering/proxy_box.dart:2960:24)
E/flutter (22722): #3 _HomeState.takeScreenShot (package:traincam/main.dart:173:32)
E/flutter (22722): #4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:993:19)
E/flutter (22722): #5 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1111:38)
E/flutter (22722): #6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:183:24)
E/flutter (22722): #7 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:598:11)
E/flutter (22722): #8 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:287:5)
E/flutter (22722): #9 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:222:7)
E/flutter (22722): #10 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:476:9)
E/flutter (22722): #11 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:77:12)
E/flutter (22722): #12 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:122:9)
E/flutter (22722): #13 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
E/flutter (22722): #14 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:120:18)
E/flutter (22722): #15 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:106:7)
E/flutter (22722): #16 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:358:19)
E/flutter (22722): #17 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:338:22)
E/flutter (22722): #18 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:267:11)
E/flutter (22722): #19 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:295:7)
E/flutter (22722): #20 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:240:7)
E/flutter (22722): #21 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:213:7)
E/flutter (22722): #22 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter (22722): #23 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (22722): #24 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter (22722): #25 _invoke1 (dart:ui/hooks.dart:265:10)
E/flutter (22722): #26 _dispatchPointerDataPacket (dart:ui/hooks.dart:174:5)
代码如下:
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:gesture_zoom_box/gesture_zoom_box.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:save_in_gallery/save_in_gallery.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: "Train Cam",
home: Home(
channel: IOWebSocketChannel.connect('ws://192.168.1.213:8888'),
)
);
}
}
class Home extends StatefulWidget {
final WebSocketChannel channel;
Home({Key key, @required this.channel}) : super (key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final videoWidth = 640;
final videoHeight = 480;
double newVideoSizeWidth = 640;
double newVideoSizeHeight = 480;
bool isLandscape = false;
String _timeString;
var _globalKey = new GlobalKey();
final _imageSaver = ImageSaver();
@override
void initState() {
super.initState();
isLandscape = false;
}
@override
void dispose() {
widget.channel.sink.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: OrientationBuilder(
builder: (context, orientation) {
var screenWidth = MediaQuery.of(context).size.width;
var screenHeight = MediaQuery.of(context).size.height;
if (orientation == Orientation.portrait) {
//screenWidth < screenHeight
isLandscape = false;
newVideoSizeWidth =
screenWidth > videoWidth ? videoWidth : screenWidth;
newVideoSizeHeight =
videoHeight * newVideoSizeHeight / screenWidth;
}
else {
isLandscape = true;
newVideoSizeHeight =
screenHeight > videoHeight ? videoHeight : screenHeight;
newVideoSizeWidth =
videoWidth * newVideoSizeHeight / videoHeight;
}
return Container(
color: Colors.black,
child: StreamBuilder(
stream: widget.channel.stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
);
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
SizedBox(
height: isLandscape ? 0 : 30,
),
Stack(
children: <Widget>[
RepaintBoundary(
key: _globalKey,
),
GestureZoomBox(
maxScale: 5.0,
doubleTapScale: 2.0,
duration: Duration(milliseconds: 200),
child: Image.memory(
snapshot.data,
gaplessPlayback: true,
width: newVideoSizeWidth,
height: newVideoSizeWidth,
),
),
Positioned.fill(
child: Align(
child: Column(
children: <Widget>[
SizedBox(
height: 16,
),
Text('Train Cam', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w300),),
SizedBox(
height: 8,
),
Text('Live | $_timeString', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w300),),
]
),
alignment: Alignment.topCenter,
))
],
),
Expanded(flex: 1,
child: Container(
color: Colors.black,
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(icon: Icon(Icons.videocam, size: 24,), onPressed: () { },),
IconButton(icon: Icon(Icons.photo_camera, size: 24,), onPressed: takeScreenShot,),
IconButton(icon: Icon(Icons.mic, size: 24,), onPressed: () { },),
IconButton(icon: Icon(Icons.speaker, size: 24,), onPressed: () { },),
IconButton(icon: Icon(Icons.add_alert, size: 24,), onPressed: () { },)
],
),
)
),)
],
),
],
);
}
},
),
);
}
)
);
}
takeScreenShot()async{
RenderRepaintBoundary boundary = _globalKey.currentContext.findRenderObject();
var image = await boundary.toImage();
var byteData = await image.toByteData(format: ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
final res = await _imageSaver.saveImage(imageBytes: pngBytes);
Fluttertoast.showToast(msg: res? "Screenshot saved" : "Screenshot failed!",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
}
}
答案 0 :(得分:0)
拍摄视频快照时 Flutter 中的图像尺寸无效, 这意味着您需要为错误小部件设置约束 例如:
我对以下代码有同样的问题:
Widget carIconImage =
Image.asset(BYDConstants().getBydImageHome("box.png"));
用具有宽度和高度的容器包裹,问题解决了。
Widget carIconImage =
Container(width:30,height:30,child: Image.asset(BYDConstants().getBydImageHome("box.png")),);