Reason Pun

clear and repair

Showing 100 changed files with 849 additions and 403 deletions
......@@ -50,7 +50,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.mofunsky.one_poem"
minSdkVersion 20
minSdkVersion 21
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
......
......@@ -17,19 +17,6 @@
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
......

72.3 KB | W: | H:

22.4 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

72.3 KB | W: | H:

22.4 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

2.63 KB | W: | H:

2.63 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

1.67 KB | W: | H:

1.67 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

3.59 KB | W: | H:

3.59 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

5.53 KB | W: | H:

5.5 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

7.26 KB | W: | H:

7.21 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

122 KB | W: | H:

41.2 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

190 KB | W: | H:

64.6 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

202 KB | W: | H:

71.4 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

13 KB | W: | H:

4.19 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

888 KB | W: | H:

231 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

72.3 KB | W: | H:

22.4 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin

611 Bytes | W: | H:

610 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin

938 Bytes | W: | H:

939 Bytes | W: | H:

  • 2-up
  • Swipe
  • Onion skin
......@@ -3,7 +3,7 @@ import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:one_poem/poem/widgets/poem_content.dart';
import 'package:one_poem/recorder/widgets/poem_voice_widget.dart';
import 'package:one_poem/recorder/audio/widgets/poem_voice_widget.dart';
import 'package:one_poem/routers/fluro_navigator.dart';
import 'package:one_poem/util/toast_utils.dart';
import 'package:one_poem/widgets/bars/home_action_bar.dart';
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:one_poem/widgets/my_app_bar.dart';
import 'package:flutter/services.dart';
import 'package:one_poem/recorder/video/preview_screen.dart';
import 'package:one_poem/util/toast_utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';
class PoemRecordVideoPage extends StatefulWidget {
const PoemRecordVideoPage({Key? key, this.title}) : super(key: key);
final String? title;
const PoemRecordVideoPage({Key? key}) : super(key: key);
@override
_PoemRecordVideoPageState createState() => _PoemRecordVideoPageState();
}
class _PoemRecordVideoPageState extends State<PoemRecordVideoPage> {
List<XFile>? _imageFileList;
class _PoemRecordVideoPageState extends State<PoemRecordVideoPage>
with WidgetsBindingObserver {
CameraController? controller;
VideoPlayerController? videoController;
set _imageFile(XFile? value) {
_imageFileList = value == null ? null : [value];
}
File? _imageFile;
File? _videoFile;
// Initial values
bool _isCameraInitialized = false;
bool _isRearCameraSelected = true;
bool _isVideoCameraSelected = false;
bool _isRecordingInProgress = false;
double _minAvailableExposureOffset = 0.0;
double _maxAvailableExposureOffset = 0.0;
double _minAvailableZoom = 1.0;
double _maxAvailableZoom = 1.0;
dynamic _pickImageError;
bool isVideo = false;
// Current values
double _currentZoomLevel = 1.0;
double _currentExposureOffset = 0.0;
FlashMode? _currentFlashMode;
VideoPlayerController? _controller;
VideoPlayerController? _toBeDisposed;
String? _retrieveDataError;
List<File> allFileList = [];
final ImagePicker _picker = ImagePicker();
final TextEditingController maxWidthController = TextEditingController();
final TextEditingController maxHeightController = TextEditingController();
final TextEditingController qualityController = TextEditingController();
final resolutionPresets = ResolutionPreset.values;
Future<void> _playVideo(XFile? file) async {
if (file != null && mounted) {
await _disposeVideoController();
late VideoPlayerController controller;
if (kIsWeb) {
controller = VideoPlayerController.network(file.path);
ResolutionPreset currentResolutionPreset = ResolutionPreset.high;
List<CameraDescription>? cameras = [];
@override
void initState() {
// Hide the status bar in Android
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
initCameras().then((value) {
cameras = value;
if (cameras != null) {
onNewCameraSelected(cameras![0]);
refreshAlreadyCapturedImages();
} else {
controller = VideoPlayerController.file(File(file.path));
Toast.show("摄像头出错啦~");
}
_controller = controller;
// In web, most browsers won't honor a programmatic call to .play
// if the video has a sound track (and is not muted).
// Mute the video so it auto-plays in web!
// This is not needed if the call to .play is the result of user
// interaction (clicking on a "play" button, for example).
final double volume = kIsWeb ? 0.0 : 1.0;
await controller.setVolume(volume);
await controller.initialize();
await controller.setLooping(true);
await controller.play();
setState(() {});
});
super.initState();
}
Future<List<CameraDescription>?> initCameras() async {
try {
WidgetsFlutterBinding.ensureInitialized();
List<CameraDescription> cameras = await availableCameras();
return cameras;
} on CameraException catch (e) {
return null;
}
}
void _onImageButtonPressed(ImageSource source,
{BuildContext? context, bool isMultiImage = false}) async {
if (_controller != null) {
await _controller!.setVolume(0.0);
refreshAlreadyCapturedImages() async {
final directory = await getApplicationDocumentsDirectory();
List<FileSystemEntity> fileList = await directory.list().toList();
allFileList.clear();
List<Map<int, dynamic>> fileNames = [];
for (var file in fileList) {
if (file.path.contains('.jpg') || file.path.contains('.mp4')) {
allFileList.add(File(file.path));
String name = file.path.split('/').last.split('.').first;
fileNames.add({0: int.parse(name), 1: file.path.split('/').last});
}
}
if (isVideo) {
final XFile? file = await _picker.pickVideo(
source: source, maxDuration: const Duration(seconds: 10));
await _playVideo(file);
} else if (isMultiImage) {
await _displayPickImageDialog(context!,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final pickedFileList = await _picker.pickMultiImage(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
setState(() {
_imageFileList = pickedFileList;
});
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
} else {
await _displayPickImageDialog(context!,
(double? maxWidth, double? maxHeight, int? quality) async {
try {
final pickedFile = await _picker.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
setState(() {
_imageFile = pickedFile;
});
} catch (e) {
setState(() {
_pickImageError = e;
});
}
});
if (fileNames.isNotEmpty) {
final recentFile =
fileNames.reduce((curr, next) => curr[0] > next[0] ? curr : next);
String recentFileName = recentFile[1];
if (recentFileName.contains('.mp4')) {
_videoFile = File('${directory.path}/$recentFileName');
_imageFile = null;
_startVideoPlayer();
} else {
_imageFile = File('${directory.path}/$recentFileName');
_videoFile = null;
}
setState(() {});
}
}
@override
void deactivate() {
if (_controller != null) {
_controller!.setVolume(0.0);
_controller!.pause();
Future<XFile?> takePicture() async {
final CameraController? cameraController = controller;
if (cameraController!.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
super.deactivate();
}
@override
void dispose() {
_disposeVideoController();
maxWidthController.dispose();
maxHeightController.dispose();
qualityController.dispose();
super.dispose();
try {
XFile file = await cameraController.takePicture();
return file;
} on CameraException catch (e) {
return null;
}
}
Future<void> _disposeVideoController() async {
if (_toBeDisposed != null) {
await _toBeDisposed!.dispose();
Future<void> _startVideoPlayer() async {
if (_videoFile != null) {
videoController = VideoPlayerController.file(_videoFile!);
await videoController!.initialize().then((_) {
// Ensure the first frame is shown after the video is initialized,
// even before the play button has been pressed.
setState(() {});
});
await videoController!.setLooping(true);
await videoController!.play();
}
_toBeDisposed = _controller;
_controller = null;
}
Widget _previewVideo() {
final Text? retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
Future<void> startVideoRecording() async {
final CameraController? cameraController = controller;
if (controller!.value.isRecordingVideo) {
// A recording has already started, do nothing.
return;
}
if (_controller == null) {
return const Text(
'You have not yet picked a video',
textAlign: TextAlign.center,
);
try {
await cameraController!.startVideoRecording();
setState(() {
_isRecordingInProgress = true;
});
} on CameraException catch (e) {
// print('Error starting to record video: $e');
}
return Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatioVideo(_controller),
);
}
Widget _previewImages() {
final Text? retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
Future<XFile?> stopVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// Recording is already is stopped state
return null;
}
if (_imageFileList != null) {
return Semantics(
child: ListView.builder(
key: UniqueKey(),
itemBuilder: (context, index) {
// Why network for web?
// See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform
return Semantics(
label: 'image_picker_example_picked_image',
child: kIsWeb
? Image.network(_imageFileList![index].path)
: Image.file(File(_imageFileList![index].path)),
);
},
itemCount: _imageFileList!.length,
),
label: 'image_picker_example_picked_images');
} else if (_pickImageError != null) {
return Text(
'Pick image error: $_pickImageError',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
Widget _handlePreview() {
if (isVideo) {
return _previewVideo();
} else {
return _previewImages();
try {
XFile file = await controller!.stopVideoRecording();
setState(() {
_isRecordingInProgress = false;
});
return file;
} on CameraException catch (e) {
return null;
}
}
Future<void> retrieveLostData() async {
final LostDataResponse response = await _picker.retrieveLostData();
if (response.isEmpty) {
Future<void> pauseVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// Video recording is not in progress
return;
}
if (response.file != null) {
if (response.type == RetrieveType.video) {
isVideo = true;
await _playVideo(response.file);
} else {
isVideo = false;
setState(() {
_imageFile = response.file;
_imageFileList = response.files;
});
}
} else {
_retrieveDataError = response.exception!.code;
try {
await controller!.pauseVideoRecording();
} on CameraException catch (e) {
// print('Error pausing video recording: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(
isBack: true,
isTransparent: true,
),
body: Center(
child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android
? FutureBuilder<void>(
future: retrieveLostData(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
case ConnectionState.done:
return _handlePreview();
default:
if (snapshot.hasError) {
return Text(
'Pick image/video error: ${snapshot.error}}',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
},
)
: _handlePreview(),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Semantics(
label: 'image_picker_example_from_gallery',
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(ImageSource.gallery, context: context);
},
heroTag: 'image0',
tooltip: 'Pick Image from gallery',
child: const Icon(Icons.photo),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(
ImageSource.gallery,
context: context,
isMultiImage: true,
);
},
heroTag: 'image1',
tooltip: 'Pick Multiple Image from gallery',
child: const Icon(Icons.photo_library),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
isVideo = false;
_onImageButtonPressed(ImageSource.camera, context: context);
},
heroTag: 'image2',
tooltip: 'Take a Photo',
child: const Icon(Icons.camera_alt),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.gallery);
},
heroTag: 'video0',
tooltip: 'Pick Video from gallery',
child: const Icon(Icons.video_library),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.camera);
},
heroTag: 'video1',
tooltip: 'Take a Video',
child: const Icon(Icons.videocam),
),
),
],
),
);
}
Future<void> resumeVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// No video recording was in progress
return;
}
Text? _getRetrieveErrorWidget() {
if (_retrieveDataError != null) {
final Text result = Text(_retrieveDataError!);
_retrieveDataError = null;
return result;
try {
await controller!.resumeVideoRecording();
} on CameraException catch (e) {
// print('Error resuming video recording: $e');
}
return null;
}
Future<void> _displayPickImageDialog(
BuildContext context, OnPickImageCallback onPick) async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Add optional parameters'),
content: Column(
children: <Widget>[
TextField(
controller: maxWidthController,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: "Enter maxWidth if desired"),
),
TextField(
controller: maxHeightController,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
hintText: "Enter maxHeight if desired"),
),
TextField(
controller: qualityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: "Enter quality if desired"),
),
],
),
actions: <Widget>[
TextButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text('PICK'),
onPressed: () {
double? width = maxWidthController.text.isNotEmpty
? double.parse(maxWidthController.text)
: null;
double? height = maxHeightController.text.isNotEmpty
? double.parse(maxHeightController.text)
: null;
int? quality = qualityController.text.isNotEmpty
? int.parse(qualityController.text)
: null;
onPick(width, height, quality);
Navigator.of(context).pop();
}),
],
);
});
void resetCameraValues() async {
_currentZoomLevel = 1.0;
_currentExposureOffset = 0.0;
}
}
typedef OnPickImageCallback = void Function(
double? maxWidth, double? maxHeight, int? quality);
void onNewCameraSelected(CameraDescription cameraDescription) async {
final previousCameraController = controller;
class AspectRatioVideo extends StatefulWidget {
const AspectRatioVideo(this.controller, {Key? key}) : super(key: key);
final CameraController cameraController = CameraController(
cameraDescription,
currentResolutionPreset,
imageFormatGroup: ImageFormatGroup.jpeg,
);
final VideoPlayerController? controller;
await previousCameraController?.dispose();
@override
AspectRatioVideoState createState() => AspectRatioVideoState();
}
resetCameraValues();
class AspectRatioVideoState extends State<AspectRatioVideo> {
VideoPlayerController? get controller => widget.controller;
bool initialized = false;
if (mounted) {
setState(() {
controller = cameraController;
});
}
void _onVideoControllerUpdate() {
if (!mounted) {
return;
// Update UI if controller updated
cameraController.addListener(() {
if (mounted) setState(() {});
});
try {
await cameraController.initialize();
await Future.wait([
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value),
cameraController
.getMaxZoomLevel()
.then((value) => _maxAvailableZoom = value),
cameraController
.getMinZoomLevel()
.then((value) => _minAvailableZoom = value),
]);
_currentFlashMode = controller!.value.flashMode;
} on CameraException catch (e) {
// print('Error initializing camera: $e');
}
if (initialized != controller!.value.isInitialized) {
initialized = controller!.value.isInitialized;
setState(() {});
if (mounted) {
setState(() {
_isCameraInitialized = controller!.value.isInitialized;
});
}
}
@override
void initState() {
super.initState();
controller!.addListener(_onVideoControllerUpdate);
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
// App state changed before we got the chance to initialize.
if (cameraController == null || !cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
} else if (state == AppLifecycleState.resumed) {
onNewCameraSelected(cameraController.description);
}
}
@override
void dispose() {
controller!.removeListener(_onVideoControllerUpdate);
controller?.dispose();
videoController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (initialized) {
return Center(
child: AspectRatio(
aspectRatio: controller!.value.aspectRatio,
child: VideoPlayer(controller!),
),
);
} else {
return Container();
}
return SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
body: _isCameraInitialized
? Column(
children: [
AspectRatio(
aspectRatio: 1 / controller!.value.aspectRatio,
child: Stack(
children: [
controller!.buildPreview(),
Padding(
padding: const EdgeInsets.fromLTRB(
16.0,
8.0,
16.0,
8.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Align(
alignment: Alignment.topRight,
child: Container(
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
right: 8.0,
),
child: DropdownButton<ResolutionPreset>(
dropdownColor: Colors.black87,
underline: Container(),
value: currentResolutionPreset,
items: [
for (ResolutionPreset preset
in resolutionPresets)
DropdownMenuItem(
child: Text(
preset
.toString()
.split('.')[1]
.toUpperCase(),
style: const TextStyle(
color: Colors.white),
),
value: preset,
)
],
onChanged: (value) {
setState(() {
currentResolutionPreset = value!;
_isCameraInitialized = false;
});
onNewCameraSelected(
controller!.description);
},
hint: const Text("Select item"),
),
),
),
),
// Spacer(),
Padding(
padding: const EdgeInsets.only(
right: 8.0, top: 16.0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_currentExposureOffset
.toStringAsFixed(1) +
'x',
style:
const TextStyle(color: Colors.black),
),
),
),
),
Expanded(
child: RotatedBox(
quarterTurns: 3,
child: SizedBox(
height: 30,
child: Slider(
value: _currentExposureOffset,
min: _minAvailableExposureOffset,
max: _maxAvailableExposureOffset,
activeColor: Colors.white,
inactiveColor: Colors.white30,
onChanged: (value) async {
setState(() {
_currentExposureOffset = value;
});
await controller!
.setExposureOffset(value);
},
),
),
),
),
Row(
children: [
Expanded(
child: Slider(
value: _currentZoomLevel,
min: _minAvailableZoom,
max: _maxAvailableZoom,
activeColor: Colors.white,
inactiveColor: Colors.white30,
onChanged: (value) async {
setState(() {
_currentZoomLevel = value;
});
await controller!.setZoomLevel(value);
},
),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Container(
decoration: BoxDecoration(
color: Colors.black87,
borderRadius:
BorderRadius.circular(10.0),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_currentZoomLevel.toStringAsFixed(1) +
'x',
style: const TextStyle(
color: Colors.white),
),
),
),
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: _isRecordingInProgress
? () async {
if (controller!
.value.isRecordingPaused) {
await resumeVideoRecording();
} else {
await pauseVideoRecording();
}
}
: () {
setState(() {
_isCameraInitialized = false;
});
onNewCameraSelected(cameras![
_isRearCameraSelected ? 1 : 0]);
setState(() {
_isRearCameraSelected =
!_isRearCameraSelected;
});
},
child: Stack(
alignment: Alignment.center,
children: [
const Icon(
Icons.circle,
color: Colors.black38,
size: 60,
),
_isRecordingInProgress
? controller!
.value.isRecordingPaused
? const Icon(
Icons.play_arrow,
color: Colors.white,
size: 30,
)
: const Icon(
Icons.pause,
color: Colors.white,
size: 30,
)
: Icon(
_isRearCameraSelected
? Icons.camera_front
: Icons.camera_rear,
color: Colors.white,
size: 30,
),
],
),
),
InkWell(
onTap: _isVideoCameraSelected
? () async {
if (_isRecordingInProgress) {
XFile? rawVideo =
await stopVideoRecording();
File videoFile =
File(rawVideo!.path);
int currentUnix = DateTime.now()
.millisecondsSinceEpoch;
final directory =
await getApplicationDocumentsDirectory();
String fileFormat = videoFile.path
.split('.')
.last;
_videoFile = await videoFile.copy(
'${directory.path}/$currentUnix.$fileFormat',
);
_startVideoPlayer();
} else {
await startVideoRecording();
}
}
: () async {
XFile? rawImage =
await takePicture();
File imageFile =
File(rawImage!.path);
int currentUnix = DateTime.now()
.millisecondsSinceEpoch;
final directory =
await getApplicationDocumentsDirectory();
String fileFormat =
imageFile.path.split('.').last;
await imageFile.copy(
'${directory.path}/$currentUnix.$fileFormat',
);
refreshAlreadyCapturedImages();
},
child: Stack(
alignment: Alignment.center,
children: [
Icon(
Icons.circle,
color: _isVideoCameraSelected
? Colors.white
: Colors.white38,
size: 80,
),
Icon(
Icons.circle,
color: _isVideoCameraSelected
? Colors.red
: Colors.white,
size: 65,
),
_isVideoCameraSelected &&
_isRecordingInProgress
? const Icon(
Icons.stop_rounded,
color: Colors.white,
size: 32,
)
: Container(),
],
),
),
InkWell(
onTap:
_imageFile != null || _videoFile != null
? () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
PreviewScreen(
imageFile: _imageFile!,
fileList: allFileList,
),
),
);
}
: null,
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.black,
borderRadius:
BorderRadius.circular(10.0),
border: Border.all(
color: Colors.white,
width: 2,
),
image: _imageFile != null
? DecorationImage(
image: FileImage(_imageFile!),
fit: BoxFit.cover,
)
: null,
),
child: videoController != null &&
videoController!
.value.isInitialized
? ClipRRect(
borderRadius:
BorderRadius.circular(8.0),
child: AspectRatio(
aspectRatio: videoController!
.value.aspectRatio,
child: VideoPlayer(
videoController!),
),
)
: Container(),
),
),
],
),
],
),
),
],
),
),
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 8.0,
right: 4.0,
),
child: TextButton(
onPressed: _isRecordingInProgress
? null
: () {
if (_isVideoCameraSelected) {
setState(() {
_isVideoCameraSelected =
false;
});
}
},
style: TextButton.styleFrom(
primary: _isVideoCameraSelected
? Colors.black54
: Colors.black,
backgroundColor: _isVideoCameraSelected
? Colors.white30
: Colors.white,
),
child: const Text('IMAGE'),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 4.0, right: 8.0),
child: TextButton(
onPressed: () {
if (!_isVideoCameraSelected) {
setState(() {
_isVideoCameraSelected = true;
});
}
},
style: TextButton.styleFrom(
primary: _isVideoCameraSelected
? Colors.black
: Colors.black54,
backgroundColor: _isVideoCameraSelected
? Colors.white
: Colors.white30,
),
child: const Text('VIDEO'),
),
),
),
],
),
),
Padding(
padding:
const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () async {
setState(() {
_currentFlashMode = FlashMode.off;
});
await controller!.setFlashMode(
FlashMode.off,
);
},
child: Icon(
Icons.flash_off,
color: _currentFlashMode == FlashMode.off
? Colors.amber
: Colors.white,
),
),
InkWell(
onTap: () async {
setState(() {
_currentFlashMode = FlashMode.auto;
});
await controller!.setFlashMode(
FlashMode.auto,
);
},
child: Icon(
Icons.flash_auto,
color: _currentFlashMode == FlashMode.auto
? Colors.amber
: Colors.white,
),
),
InkWell(
onTap: () async {
setState(() {
_currentFlashMode = FlashMode.always;
});
await controller!.setFlashMode(
FlashMode.always,
);
},
child: Icon(
Icons.flash_on,
color: _currentFlashMode == FlashMode.always
? Colors.amber
: Colors.white,
),
),
InkWell(
onTap: () async {
setState(() {
_currentFlashMode = FlashMode.torch;
});
await controller!.setFlashMode(
FlashMode.torch,
);
},
child: Icon(
Icons.highlight,
color: _currentFlashMode == FlashMode.torch
? Colors.amber
: Colors.white,
),
),
],
),
)
],
),
),
),
],
)
: const Center(
child: Text(
'LOADING',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}
......
import 'dart:io';
import 'package:flutter/material.dart';
import 'preview_screen.dart';
class CapturesScreen extends StatelessWidget {
final List<File> imageFileList;
const CapturesScreen({required this.imageFileList});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Captures',
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
GridView.count(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 2,
children: [
for (File imageFile in imageFileList)
Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 2,
),
),
child: InkWell(
onTap: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => PreviewScreen(
fileList: imageFileList,
imageFile: imageFile,
),
),
);
},
child: Image.file(
imageFile,
fit: BoxFit.cover,
),
),
),
],
),
],
),
),
);
}
}
import 'dart:io';
import 'package:flutter/material.dart';
import 'captures_screen.dart';
class PreviewScreen extends StatelessWidget {
final File imageFile;
final List<File> fileList;
const PreviewScreen({
required this.imageFile,
required this.fileList,
});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => CapturesScreen(
imageFileList: fileList,
),
),
);
},
child: Text('Go to all captures'),
style: TextButton.styleFrom(
primary: Colors.black,
backgroundColor: Colors.white,
),
),
),
Expanded(
child: Image.file(imageFile),
),
],
),
);
}
}
......@@ -31,7 +31,7 @@ class _AccountManagerPageState extends State<AccountManagerPage> {
children: <Widget>[
Stack(
children: <Widget>[
ClickItem(title: '店铺logo', onTap: () {}),
ClickItem(title: '头像', onTap: () {}),
Positioned(
top: 8.px,
bottom: 8.px,
......
......@@ -120,6 +120,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
camera:
dependency: "direct main"
description:
name: camera
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.4+5"
camera_platform_interface:
dependency: transitive
description:
name: camera_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
camera_web:
dependency: transitive
description:
name: camera_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1+1"
characters:
dependency: transitive
description:
......@@ -632,7 +653,7 @@ packages:
source: hosted
version: "1.8.0"
path_provider:
dependency: transitive
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
......@@ -764,6 +785,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1+1"
rational:
dependency: transitive
description:
......
......@@ -89,12 +89,16 @@ dependencies:
tapped: ^2.0.0-nullsafety.0
# 加载动画库
flutter_spinkit: ^5.0.0
# fijkplayer (Video player plugin for Flutter) Flutter 媒体播放器
fijkplayer: ^0.10.1
json_annotation: ^4.4.0
flutter_plugin_record: ^1.0.1
# fijkplayer (Video player plugin for Flutter) Flutter 媒体播放器
fijkplayer: ^0.10.1
camera: ^0.9.4+5
path_provider: ^2.0.8
dependency_overrides:
decimal: 1.5.0
......@@ -169,9 +173,7 @@ flutter:
assets:
- assets/data/
- assets/images/
- assets/images/home/
- assets/images/login/
- assets/images/account/
- assets/images/state/
- assets/images/poem/
- assets/images/category/
......