Toggle navigation
Toggle navigation
This project
Loading...
Sign in
OnePoem
/
OnePoem-App
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
Reason Pun
2022-01-10 20:50:03 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
41652ac8d9e03e6bd4cbe50e9ab9d06b759dcee3
41652ac8
1 parent
775d4287
add video record page
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
489 additions
and
15 deletions
lib/poem/page/poem_detail.dart
lib/poem/page/poem_record_video.dart
lib/poem/poem_router.dart
lib/timeline/widgets/friend_cell.dart
pubspec.lock
lib/poem/page/poem_detail.dart
View file @
41652ac
...
...
@@ -150,7 +150,10 @@ class _PoemDetailPageState extends State<PoemDetailPage> {
size:
36
.
px
,
),
onPressed:
()
{
Toast
.
show
(
"不要着急吖,正在开发ing...."
);
NavigatorUtils
.
push
(
context
,
'
${PoemRouter.poemRecordVideoPage}
?data=100'
,
);
},
)
],
...
...
lib/poem/page/poem_record_video.dart
View file @
41652ac
// 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:flutter/material.dart'
;
import
'package:image_picker/image_picker.dart'
;
import
'package:one_poem/widgets/my_app_bar.dart'
;
import
'package:video_player/video_player.dart'
;
class
PoemRecordVideoPage
extends
StatefulWidget
{
const
PoemRecordVideoPage
({
Key
?
key
,
this
.
title
})
:
super
(
key:
key
);
final
String
?
title
;
@override
_PoemRecordVideoPageState
createState
()
=>
_PoemRecordVideoPageState
();
}
class
_PoemRecordVideoPageState
extends
State
<
PoemRecordVideoPage
>
{
List
<
XFile
>?
_imageFileList
;
set
_imageFile
(
XFile
?
value
)
{
_imageFileList
=
value
==
null
?
null
:
[
value
];
}
dynamic
_pickImageError
;
bool
isVideo
=
false
;
VideoPlayerController
?
_controller
;
VideoPlayerController
?
_toBeDisposed
;
String
?
_retrieveDataError
;
final
ImagePicker
_picker
=
ImagePicker
();
final
TextEditingController
maxWidthController
=
TextEditingController
();
final
TextEditingController
maxHeightController
=
TextEditingController
();
final
TextEditingController
qualityController
=
TextEditingController
();
Future
<
void
>
_playVideo
(
XFile
?
file
)
async
{
if
(
file
!=
null
&&
mounted
)
{
await
_disposeVideoController
();
late
VideoPlayerController
controller
;
if
(
kIsWeb
)
{
controller
=
VideoPlayerController
.
network
(
file
.
path
);
}
else
{
controller
=
VideoPlayerController
.
file
(
File
(
file
.
path
));
}
_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
(()
{});
}
}
void
_onImageButtonPressed
(
ImageSource
source
,
{
BuildContext
?
context
,
bool
isMultiImage
=
false
})
async
{
if
(
_controller
!=
null
)
{
await
_controller
!.
setVolume
(
0.0
);
}
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
;
});
}
});
}
}
@override
void
deactivate
()
{
if
(
_controller
!=
null
)
{
_controller
!.
setVolume
(
0.0
);
_controller
!.
pause
();
}
super
.
deactivate
();
}
@override
void
dispose
()
{
_disposeVideoController
();
maxWidthController
.
dispose
();
maxHeightController
.
dispose
();
qualityController
.
dispose
();
super
.
dispose
();
}
Future
<
void
>
_disposeVideoController
()
async
{
if
(
_toBeDisposed
!=
null
)
{
await
_toBeDisposed
!.
dispose
();
}
_toBeDisposed
=
_controller
;
_controller
=
null
;
}
Widget
_previewVideo
()
{
final
Text
?
retrieveError
=
_getRetrieveErrorWidget
();
if
(
retrieveError
!=
null
)
{
return
retrieveError
;
}
if
(
_controller
==
null
)
{
return
const
Text
(
'You have not yet picked a video'
,
textAlign:
TextAlign
.
center
,
);
}
return
Padding
(
padding:
const
EdgeInsets
.
all
(
10.0
),
child:
AspectRatioVideo
(
_controller
),
);
}
Widget
_previewImages
()
{
final
Text
?
retrieveError
=
_getRetrieveErrorWidget
();
if
(
retrieveError
!=
null
)
{
return
retrieveError
;
}
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
();
}
}
Future
<
void
>
retrieveLostData
()
async
{
final
LostDataResponse
response
=
await
_picker
.
retrieveLostData
();
if
(
response
.
isEmpty
)
{
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
;
}
}
@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
),
),
),
],
),
);
}
Text
?
_getRetrieveErrorWidget
()
{
if
(
_retrieveDataError
!=
null
)
{
final
Text
result
=
Text
(
_retrieveDataError
!);
_retrieveDataError
=
null
;
return
result
;
}
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
();
}),
],
);
});
}
}
typedef
OnPickImageCallback
=
void
Function
(
double
?
maxWidth
,
double
?
maxHeight
,
int
?
quality
);
class
AspectRatioVideo
extends
StatefulWidget
{
const
AspectRatioVideo
(
this
.
controller
,
{
Key
?
key
})
:
super
(
key:
key
);
final
VideoPlayerController
?
controller
;
@override
AspectRatioVideoState
createState
()
=>
AspectRatioVideoState
();
}
class
AspectRatioVideoState
extends
State
<
AspectRatioVideo
>
{
VideoPlayerController
?
get
controller
=>
widget
.
controller
;
bool
initialized
=
false
;
void
_onVideoControllerUpdate
()
{
if
(!
mounted
)
{
return
;
}
if
(
initialized
!=
controller
!.
value
.
isInitialized
)
{
initialized
=
controller
!.
value
.
isInitialized
;
setState
(()
{});
}
}
@override
void
initState
()
{
super
.
initState
();
controller
!.
addListener
(
_onVideoControllerUpdate
);
}
@override
void
dispose
()
{
controller
!.
removeListener
(
_onVideoControllerUpdate
);
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
if
(
initialized
)
{
return
Center
(
child:
AspectRatio
(
aspectRatio:
controller
!.
value
.
aspectRatio
,
child:
VideoPlayer
(
controller
!),
),
);
}
else
{
return
Container
();
}
}
}
...
...
lib/poem/poem_router.dart
View file @
41652ac
...
...
@@ -4,13 +4,15 @@ import 'package:one_poem/routers/i_router.dart';
import
'page/poem_detail.dart'
;
import
'page/poem_page.dart'
;
import
'page/poem_publish.dart'
;
import
'page/poem_record_video.dart'
;
import
'page/poem_video_player.dart'
;
class
PoemRouter
implements
IRouterProvider
{
static
String
poemPage
=
'/poem'
;
static
String
poemDetailPage
=
'/detail'
;
static
String
poemRecordAudioPage
=
'/poem/record/audio'
;
static
String
poemVidePlayer
=
'/poem/video/player'
;
static
String
poemRecordVideoPage
=
'/poem/record/video'
;
static
String
poemVideoPlayer
=
'/poem/video/player'
;
static
String
poemPublish
=
'/poem/publish'
;
@override
...
...
@@ -32,6 +34,7 @@ class PoemRouter implements IRouterProvider {
},
),
);
router
.
define
(
poemRecordAudioPage
,
handler:
Handler
(
...
...
@@ -43,8 +46,21 @@ class PoemRouter implements IRouterProvider {
},
),
);
router
.
define
(
poemRecordVideoPage
,
handler:
Handler
(
handlerFunc:
(
_
,
Map
<
String
,
List
<
String
>>
params
)
{
String
?
id
=
params
[
'id'
]?.
first
;
return
const
PoemRecordVideoPage
(
// poemId: int.parse(id!),
);
},
),
);
router
.
define
(
poemVidePlayer
,
poemVide
o
Player
,
handler:
Handler
(
handlerFunc:
(
_
,
Map
<
String
,
List
<
String
>>
params
)
{
String
?
url
=
params
[
'url'
]?.
first
;
...
...
lib/timeline/widgets/friend_cell.dart
View file @
41652ac
...
...
@@ -108,7 +108,7 @@ class FriendCellState extends State<FriendCell> {
onTap:
()
{
NavigatorUtils
.
push
(
context
,
'
${PoemRouter.poemVidePlayer}
?url=100'
,
'
${PoemRouter.poemVide
o
Player}
?url=100'
,
);
},
child:
Text
(
...
...
pubspec.lock
View file @
41652ac
...
...
@@ -238,21 +238,21 @@ packages:
name: device_info_plus
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.
0
"
version: "3.2.
1
"
device_info_plus_linux:
dependency: transitive
description:
name: device_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.
0
"
version: "2.1.
1
"
device_info_plus_macos:
dependency: transitive
description:
name: device_info_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.
0
"
version: "2.2.
1
"
device_info_plus_platform_interface:
dependency: transitive
description:
...
...
@@ -273,7 +273,7 @@ packages:
name: device_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.
0
"
version: "2.1.
1
"
dio:
dependency: "direct main"
description:
...
...
@@ -506,7 +506,7 @@ packages:
name: image_picker_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.
1
"
version: "2.4.
2
"
integration_test:
dependency: "direct dev"
description: flutter
...
...
@@ -672,7 +672,7 @@ packages:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.
1
"
version: "2.0.
2
"
path_provider_windows:
dependency: transitive
description:
...
...
@@ -707,7 +707,7 @@ packages:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.
0
.2"
version: "2.
1
.2"
pool:
dependency: transitive
description:
...
...
@@ -763,7 +763,7 @@ packages:
name: quick_actions_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.
0
"
version: "1.0.
1
"
rational:
dependency: transitive
description:
...
...
@@ -1083,7 +1083,7 @@ packages:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.
4
"
version: "2.0.
5
"
url_launcher_web:
dependency: transitive
description:
...
...
@@ -1146,7 +1146,7 @@ packages:
name: video_player_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.
0
"
version: "5.0.
1
"
video_player_web:
dependency: transitive
description:
...
...
@@ -1209,7 +1209,7 @@ packages:
name: webview_flutter_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.
0
"
version: "1.8.
1
"
webview_flutter_wkwebview:
dependency: transitive
description:
...
...
Please
register
or
login
to post a comment