camera_main.dart 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. import 'dart:io';
  2. import 'dart:math';
  3. import 'package:camera/camera.dart';
  4. import 'package:douyin_demo/providers/CameraProvider.dart';
  5. import 'package:firebase_ml_vision/firebase_ml_vision.dart';
  6. import 'package:flutter/material.dart';
  7. // import 'package:image_gallery_saver/image_gallery_saver.dart';
  8. import 'package:provider/provider.dart';
  9. class CameraPage extends StatelessWidget {
  10. const CameraPage({Key? key}) : super(key: key);
  11. @override
  12. Widget build(BuildContext context) {
  13. return Scaffold(
  14. backgroundColor: Theme.of(context).primaryColor,
  15. body: CameraMain(
  16. rpx: MediaQuery.of(context).size.width / 750,
  17. ),
  18. bottomNavigationBar: BottomAppBar(),
  19. );
  20. }
  21. }
  22. class CameraMain extends StatefulWidget {
  23. CameraMain({Key? key, required this.rpx}) : super(key: key);
  24. final double rpx;
  25. @override
  26. _CameraMainState createState() => _CameraMainState();
  27. }
  28. class _CameraMainState extends State<CameraMain> {
  29. late CameraProvider provider;
  30. double rpx = 0;
  31. double toTop = 0;
  32. double outBox = 0;
  33. double innerBox = 0;
  34. late CameraController _controller;
  35. bool findFace = false;
  36. var cameras;
  37. @override
  38. void initState() {
  39. super.initState();
  40. // provider = Provider.of<CameraProvider>(context);
  41. // getCameras();
  42. rpx = widget.rpx;
  43. toTop = 100 * rpx;
  44. outBox = 170 * rpx;
  45. innerBox = 130 * rpx;
  46. }
  47. getCameras() async {
  48. cameras = await availableCameras();
  49. _controller = CameraController(cameras[1], ResolutionPreset.medium);
  50. _controller.initialize().then((_) {
  51. if (!mounted) {
  52. return;
  53. }
  54. _controller.startImageStream((CameraImage availableImage) {
  55. _controller.stopImageStream();
  56. _scanFrame(availableImage);
  57. });
  58. setState(() {});
  59. });
  60. }
  61. void _scanFrame(CameraImage availableImage) async {
  62. final FirebaseVisionImageMetadata metadata = FirebaseVisionImageMetadata(
  63. rawFormat: availableImage.format.raw,
  64. size: Size(
  65. availableImage.width.toDouble(), availableImage.height.toDouble()),
  66. planeData: availableImage.planes
  67. .map((currentPlane) => FirebaseVisionImagePlaneMetadata(
  68. bytesPerRow: currentPlane.bytesPerRow,
  69. height: currentPlane.height,
  70. width: currentPlane.width))
  71. .toList(),
  72. rotation: ImageRotation.rotation90);
  73. final FirebaseVisionImage visionImage =
  74. FirebaseVisionImage.fromBytes(availableImage.planes[0].bytes, metadata);
  75. final FaceDetector detector = FirebaseVision.instance.faceDetector();
  76. final List<Face> faces = await detector.processImage(visionImage);
  77. if (faces.length > 0) {
  78. setState(() {
  79. findFace = true;
  80. _controller.startImageStream((CameraImage availableImage) {
  81. _controller.stopImageStream();
  82. _scanFrame(availableImage);
  83. });
  84. });
  85. } else {
  86. setState(() {
  87. findFace = false;
  88. _controller.startImageStream((CameraImage availableImage) {
  89. _controller.stopImageStream();
  90. _scanFrame(availableImage);
  91. });
  92. });
  93. }
  94. // for (TextBlock block in visionText.blocks) {
  95. // // final Rectangle<int> boundingBox = block.boundingBox;
  96. // // final List<Point<int>> cornerPoints = block.cornerPoints;
  97. // print(block.text);
  98. // final List<RecognizedLanguage> languages = block.recognizedLanguages;
  99. // for (TextLine line in block.lines) {
  100. // // Same getters as TextBlock
  101. // print(line.text);
  102. // for (TextElement element in line.elements) {
  103. // // Same getters as TextBlock
  104. // print(element.text);
  105. // }
  106. // }
  107. // }
  108. }
  109. @override
  110. void dispose() {
  111. _controller.dispose();
  112. super.dispose();
  113. }
  114. @override
  115. Widget build(BuildContext context) {
  116. provider = Provider.of<CameraProvider>(context);
  117. _controller = provider.cameraController;
  118. if (_controller == null) {
  119. return Container(
  120. child: Center(child: CircularProgressIndicator()),
  121. );
  122. }
  123. bool ifMakeVideo = provider.ifMakeVideo;
  124. if (_controller.value == null) {
  125. return Container(
  126. child: Center(child: CircularProgressIndicator()),
  127. );
  128. }
  129. final size = MediaQuery.of(context).size;
  130. return _controller.value.isInitialized
  131. ? Stack(children: [
  132. // Camera.open(cameraId),
  133. findFace
  134. ? Positioned(
  135. top: 0,
  136. left: 0,
  137. child: Container(
  138. width: 100,
  139. height: 100,
  140. color: Colors.red,
  141. ))
  142. : Container(),
  143. ClipRect(
  144. child: Transform.scale(
  145. scale: _controller.value.aspectRatio / size.aspectRatio,
  146. child: Center(
  147. child: AspectRatio(
  148. aspectRatio: _controller.value.aspectRatio,
  149. child: CameraPreview(_controller),
  150. ),
  151. ),
  152. )),
  153. Positioned(
  154. //顶部关闭按钮
  155. top: toTop,
  156. left: 30 * rpx,
  157. child: IconButton(
  158. icon: Icon(
  159. Icons.close,
  160. color: Colors.white,
  161. size: 60 * rpx,
  162. ),
  163. onPressed: () {
  164. Navigator.pop(context);
  165. },
  166. ),
  167. ),
  168. Positioned(
  169. //选择音乐
  170. top: toTop,
  171. left: 250 * rpx,
  172. child: Container(
  173. width: 250 * rpx,
  174. child: TextButton(
  175. onPressed: () {},
  176. child: Row(
  177. children: [
  178. Icon(
  179. Icons.music_note,
  180. color: Colors.white,
  181. ),
  182. SizedBox(
  183. width: 10 * rpx,
  184. ),
  185. Text(
  186. "选择音乐",
  187. style: TextStyle(color: Colors.white),
  188. ),
  189. ],
  190. ),
  191. ),
  192. ),
  193. ),
  194. Positioned(
  195. //拍照按钮
  196. bottom: 140 * rpx,
  197. // left: (750*rpx-outBox)/2,
  198. child: Container(
  199. width: 750 * rpx,
  200. child: Row(
  201. mainAxisAlignment: MainAxisAlignment.spaceAround,
  202. children: [
  203. ifMakeVideo
  204. ? Container(
  205. width: 80 * rpx,
  206. )
  207. : IconWithText(
  208. icon: Icon(
  209. Icons.search,
  210. color: Colors.white,
  211. ),
  212. text: "道具"),
  213. ifMakeVideo
  214. ? AnimVideoButton(
  215. rpx: rpx,
  216. outWidth: outBox,
  217. innerWidth: innerBox - 30 * rpx,
  218. provider: provider,
  219. )
  220. : CircleTakePhoto(
  221. outBox: outBox,
  222. innerBox: innerBox,
  223. ),
  224. ifMakeVideo
  225. ? IconButton(
  226. padding: EdgeInsets.all(0),
  227. icon: Icon(
  228. Icons.check_circle,
  229. color: Color.fromARGB(255, 219, 48, 85),
  230. size: 80 * rpx,
  231. ),
  232. onPressed: () async {
  233. provider.cameraController
  234. .stopVideoRecording();
  235. // await ImageGallerySaver.saveFile(
  236. // provider.fileName);
  237. File(provider.fileName).delete();
  238. },
  239. )
  240. : IconWithText(
  241. icon: Icon(
  242. Icons.search,
  243. color: Colors.white,
  244. ),
  245. text: "道具"),
  246. ])),
  247. ),
  248. Positioned(
  249. bottom: 40 * rpx,
  250. child: ScrollBottomBar(
  251. rpx: rpx,
  252. ),
  253. ),
  254. Positioned(
  255. right: 30 * rpx,
  256. top: 80 * rpx,
  257. child: IconButton(
  258. icon: Icon(Icons.camera_front),
  259. onPressed: () {
  260. provider.changeCamera();
  261. }),
  262. )
  263. ])
  264. : Container();
  265. }
  266. }
  267. class AnimVideoButton extends StatefulWidget {
  268. AnimVideoButton(
  269. {Key? key,
  270. required this.outWidth,
  271. required this.innerWidth,
  272. required this.rpx,
  273. required this.provider})
  274. : super(key: key);
  275. final double outWidth;
  276. final double innerWidth;
  277. final double rpx;
  278. final CameraProvider provider;
  279. _AnimVideoButtonState createState() => _AnimVideoButtonState();
  280. }
  281. class _AnimVideoButtonState extends State<AnimVideoButton>
  282. with TickerProviderStateMixin {
  283. late Animation<double> animation;
  284. late AnimationController controller;
  285. double outWidth = 0;
  286. double innerWidth = 0;
  287. double outBorder = 0;
  288. double rpx = 0;
  289. double maxBorder = 0;
  290. bool ifRecording = false;
  291. late CameraProvider provider;
  292. double curBorder = 0;
  293. @override
  294. void dispose() {
  295. controller.dispose();
  296. super.dispose();
  297. }
  298. @override
  299. void initState() {
  300. super.initState();
  301. ifRecording = true;
  302. provider = widget.provider;
  303. outWidth = widget.outWidth;
  304. innerWidth = widget.innerWidth;
  305. rpx = widget.rpx;
  306. outBorder = 5 * rpx;
  307. maxBorder = (outWidth - innerWidth) / 2 - 10 * rpx;
  308. curBorder = outBorder;
  309. controller =
  310. AnimationController(duration: Duration(milliseconds: 500), vsync: this);
  311. animation =
  312. Tween<double>(begin: outBorder, end: maxBorder).animate(controller)
  313. ..addListener(() {
  314. setState(() {
  315. curBorder = animation.value;
  316. });
  317. });
  318. controller.repeat(reverse: true);
  319. }
  320. pauseRecording() {
  321. // provider.cameraController.pauseVideoRecording();
  322. controller.stop();
  323. provider.cameraController.pauseVideoRecording();
  324. setState(() {
  325. ifRecording = false;
  326. });
  327. }
  328. resumeRecording() {
  329. // provider.cameraController.resumeVideoRecording();
  330. controller.repeat(reverse: true);
  331. provider.cameraController.resumeVideoRecording();
  332. setState(() {
  333. ifRecording = true;
  334. });
  335. }
  336. @override
  337. Widget build(BuildContext context) {
  338. return Container(
  339. width: outWidth,
  340. height: outWidth,
  341. decoration: BoxDecoration(
  342. shape: BoxShape.circle,
  343. color: Colors.transparent,
  344. border: Border.all(
  345. width: curBorder, color: Color.fromARGB(128, 219, 48, 85))),
  346. child: Container(
  347. child: !ifRecording
  348. ? IconButton(
  349. padding: EdgeInsets.all(0),
  350. icon: Icon(
  351. Icons.play_arrow,
  352. size: innerWidth,
  353. color: Color.fromARGB(255, 219, 48, 85),
  354. ),
  355. onPressed: () {
  356. resumeRecording();
  357. },
  358. )
  359. : IconButton(
  360. padding: EdgeInsets.all(0),
  361. icon: Icon(
  362. Icons.pause,
  363. size: innerWidth,
  364. color: Color.fromARGB(255, 219, 48, 85),
  365. ),
  366. onPressed: () {
  367. pauseRecording();
  368. },
  369. ),
  370. ),
  371. );
  372. }
  373. }
  374. class ScrollBottomBar extends StatefulWidget {
  375. ScrollBottomBar({Key? key, required this.rpx}) : super(key: key);
  376. final double rpx;
  377. _ScrollBottomBarState createState() => _ScrollBottomBarState();
  378. }
  379. class _ScrollBottomBarState extends State<ScrollBottomBar> {
  380. double rpx=0;
  381. double eachWidth = 0;
  382. double eachSide = 0;
  383. List<String> items=[];
  384. late ScrollController controller;
  385. double startX = 0;
  386. double finalX = 0;
  387. double minValue = 0;
  388. double maxValue = 0;
  389. double curX = 0;
  390. int curIndex = 0;
  391. @override
  392. void initState() {
  393. super.initState();
  394. rpx = widget.rpx;
  395. eachWidth = 130 * rpx;
  396. eachSide = (750 - eachWidth / rpx) / 2 * rpx;
  397. curIndex = 2;
  398. minValue = 0;
  399. items = [
  400. '拍照',
  401. '拍15秒',
  402. '拍60秒',
  403. '影集',
  404. '开直播',
  405. ];
  406. maxValue = (items.length - 1) * eachWidth;
  407. curX = curIndex * eachWidth;
  408. controller = ScrollController(initialScrollOffset: curX);
  409. }
  410. moveToItem(index) {
  411. curX = index * eachWidth;
  412. controller.animateTo(curX,
  413. duration: Duration(milliseconds: 200), curve: Curves.linear);
  414. setState(() {
  415. curX = curX;
  416. curIndex = index;
  417. });
  418. }
  419. @override
  420. Widget build(BuildContext context) {
  421. return Column(children: [
  422. Listener(
  423. onPointerDown: (result) {
  424. setState(() {
  425. startX = result.position.dx;
  426. });
  427. },
  428. onPointerMove: (result) {
  429. double moveValue = result.position.dx;
  430. double moved = startX - moveValue;
  431. // curX+moved
  432. double afterMoved = min(max(curX + moved, minValue), maxValue);
  433. setState(() {
  434. curX = afterMoved;
  435. startX = result.position.dx;
  436. });
  437. },
  438. onPointerUp: (result) {
  439. int index = 0;
  440. double finalPosition = curX - eachWidth / 2;
  441. index = (finalPosition / eachWidth).ceil();
  442. moveToItem(index);
  443. },
  444. child: Container(
  445. width: 750 * rpx,
  446. height: 100 * rpx,
  447. child: SingleChildScrollView(
  448. scrollDirection: Axis.horizontal,
  449. controller: controller,
  450. child: Container(
  451. child: Row(
  452. mainAxisSize: MainAxisSize.min,
  453. children: [
  454. SizedBox(
  455. width: eachSide,
  456. ),
  457. Row(
  458. children: List.generate(items.length, (index) {
  459. return Container(
  460. width: eachWidth,
  461. child: TextButton(
  462. child: Text(
  463. items[index],
  464. style: TextStyle(
  465. color: curIndex == index
  466. ? Colors.white
  467. : Colors.white.withOpacity(0.5)),
  468. ),
  469. style: ButtonStyle(
  470. padding:
  471. MaterialStateProperty.all(EdgeInsets.zero)),
  472. onPressed: () {
  473. moveToItem(index);
  474. },
  475. ),
  476. );
  477. })),
  478. SizedBox(
  479. width: eachSide,
  480. ),
  481. ],
  482. ),
  483. ),
  484. )),
  485. ),
  486. Center(
  487. child: Container(
  488. decoration:
  489. BoxDecoration(shape: BoxShape.circle, color: Colors.white),
  490. width: 8 * rpx,
  491. height: 8 * rpx,
  492. ),
  493. )
  494. ]);
  495. }
  496. }
  497. class CircleTakePhoto extends StatelessWidget {
  498. const CircleTakePhoto(
  499. {Key? key, required this.outBox, required this.innerBox})
  500. : super(key: key);
  501. final double outBox;
  502. final double innerBox;
  503. @override
  504. Widget build(BuildContext context) {
  505. double rpx = MediaQuery.of(context).size.width / 750;
  506. CameraProvider provider = Provider.of<CameraProvider>(context);
  507. // double outBox=160*rpx;
  508. // double innerBox=130*rpx;
  509. return Container(
  510. width: outBox,
  511. height: outBox,
  512. padding: EdgeInsets.all(10 * rpx),
  513. decoration: BoxDecoration(
  514. color: Colors.transparent,
  515. borderRadius: BorderRadius.circular(90 * rpx),
  516. border: Border.all(
  517. width: 10 * rpx, color: Color.fromARGB(128, 219, 48, 85)),
  518. ),
  519. child: TextButton(
  520. style:
  521. ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.zero)),
  522. onPressed: () async {
  523. // provider.changeFileName('png');
  524. // print(provider.fileName);
  525. // await provider.cameraController
  526. // .takePicture(provider.fileName)
  527. // .then((_) {
  528. // // Navigator.push(context, MaterialPageRoute(fullscreenDialog: true,builder: (_){
  529. // // return Image.file(File(provider.fileName) );
  530. // // }));
  531. // ImagePickerSaver.saveFile(
  532. // fileData: File(provider.fileName).readAsBytesSync());
  533. // });
  534. provider.changeFileName('mp4');
  535. provider.cameraController.startVideoRecording(provider.fileName);
  536. // provider.cameraController.startVideoRecording();
  537. provider.changePhotoWidget();
  538. },
  539. child: Container(
  540. width: innerBox,
  541. height: innerBox,
  542. alignment: Alignment.center,
  543. decoration: BoxDecoration(
  544. color: Color.fromARGB(255, 219, 48, 85),
  545. borderRadius: BorderRadius.circular(75 * rpx)),
  546. )),
  547. );
  548. }
  549. }
  550. class IconWithText extends StatelessWidget {
  551. const IconWithText({Key? key, required this.icon, required this.text})
  552. : super(key: key);
  553. final Icon icon;
  554. final String text;
  555. @override
  556. Widget build(BuildContext context) {
  557. return Column(
  558. children: [
  559. icon,
  560. Text(
  561. text,
  562. style: TextStyle(color: Colors.white),
  563. )
  564. ],
  565. );
  566. }
  567. }