Video player play image and video looping flutter - flutter

can video player play image for 10sec then loop to next video ?
How can i loop image only for 10sec then loop into next new video in my video clip data or video player controller ?
I manage to add video to video player but don't know how to add image to the player.
video clip data
class VideoClip {
final String fileName;
final String thumbName;
final String title;
final String parent;
int runningTime;
VideoClip(this.title, this.fileName, this.thumbName, this.runningTime, this.parent);
String videoPath() {
return "$parent/$fileName";
}
String thumbPath() {
return "$parent/$thumbName";
}
static List<VideoClip> remoteClips = [
VideoClip("For Bigger Fun", "ForBiggerFun.mp4", "images/ForBiggerFun.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
VideoClip("Elephant Dream", "ElephantsDream.mp4", "images/ForBiggerBlazes.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
VideoClip("BigBuckBunny", "BigBuckBunny.mp4", "images/BigBuckBunny.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
];
}
Video player controller
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_video_list_sample/clips.dart';
import 'package:video_player/video_player.dart';
import 'package:wakelock/wakelock.dart';
class PlayPage extends StatefulWidget {
PlayPage({Key key, #required this.clips}) : super(key: key);
final List<VideoClip> clips;
#override
_PlayPageState createState() => _PlayPageState();
}
class _PlayPageState extends State<PlayPage> {
VideoPlayerController _controller;
List<VideoClip> get _clips {
return widget.clips;
}
var _playingIndex = -1;
var _disposed = false;
var _isFullScreen = false;
var _isEndOfClip = false;
var _progress = 0.0;
var _showingDialog = false;
Timer _timerVisibleControl;
double _controlAlpha = 1.0;
var _playing = false;
bool get _isPlaying {
return _playing;
}
set _isPlaying(bool value) {
_playing = value;
_timerVisibleControl?.cancel();
if (value) {
_timerVisibleControl = Timer(Duration(seconds: 2), () {
if (_disposed) return;
setState(() {
_controlAlpha = 0.0;
});
});
} else {
_timerVisibleControl = Timer(Duration(milliseconds: 200), () {
if (_disposed) return;
setState(() {
_controlAlpha = 1.0;
});
});
}
}
void _onTapVideo() {
debugPrint("_onTapVideo $_controlAlpha");
setState(() {
_controlAlpha = _controlAlpha > 0 ? 0 : 1;
});
_timerVisibleControl?.cancel();
_timerVisibleControl = Timer(Duration(seconds: 2), () {
if (_isPlaying) {
setState(() {
_controlAlpha = 0.0;
});
}
});
}
#override
void initState() {
Wakelock.enable();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
_initializeAndPlay(0);
super.initState();
}
#override
void dispose() {
_disposed = true;
_timerVisibleControl?.cancel();
Wakelock.disable();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
_exitFullScreen();
_controller?.pause(); // mute instantly
_controller?.dispose();
_controller = null;
super.dispose();
}
void _toggleFullscreen() async {
if (_isFullScreen) {
_exitFullScreen();
} else {
_enterFullScreen();
}
}
void _enterFullScreen() async {
debugPrint("enterFullScreen");
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
await SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
if (_disposed) return;
setState(() {
_isFullScreen = true;
});
}
void _exitFullScreen() async {
debugPrint("exitFullScreen");
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
if (_disposed) return;
setState(() {
_isFullScreen = false;
});
}
void _initializeAndPlay(int index) async {
print("_initializeAndPlay ---------> $index");
final clip = _clips[index];
final controller = clip.parent.startsWith("http")
? VideoPlayerController.network(clip.videoPath())
: VideoPlayerController.asset(clip.videoPath());
final old = _controller;
_controller = controller;
if (old != null) {
old.removeListener(_onControllerUpdated);
old.pause();
debugPrint("---- old contoller paused.");
}
debugPrint("---- controller changed.");
setState(() {});
controller
..initialize().then((_) {
debugPrint("---- controller initialized");
old?.dispose();
_playingIndex = index;
_duration = null;
_position = null;
controller.addListener(_onControllerUpdated);
controller.play();
setState(() {});
});
}
var _updateProgressInterval = 0.0;
Duration _duration;
Duration _position;
void _onControllerUpdated() async {
if (_disposed) return;
// blocking too many updation
// important !!
final now = DateTime.now().millisecondsSinceEpoch;
if (_updateProgressInterval > now) {
return;
}
_updateProgressInterval = now + 500.0;
final controller = _controller;
if (controller == null) return;
if (!controller.value.isInitialized) return;
if (_duration == null) {
_duration = _controller.value.duration;
}
var duration = _duration;
if (duration == null) return;
var position = await controller.position;
_position = position;
final playing = controller.value.isPlaying;
final isEndOfClip = position.inMilliseconds > 0 && position.inSeconds + 1 >= duration.inSeconds;
if (playing) {
// handle progress indicator
if (_disposed) return;
setState(() {
_progress = position.inMilliseconds.ceilToDouble() / duration.inMilliseconds.ceilToDouble();
});
}
// handle clip end
if (_isPlaying != playing || _isEndOfClip != isEndOfClip) {
_isPlaying = playing;
_isEndOfClip = isEndOfClip;
debugPrint("updated -----> isPlaying=$playing / isEndOfClip=$isEndOfClip");
if (isEndOfClip && !playing) {
debugPrint("========================== End of Clip / Handle NEXT ========================== ");
final isComplete = _playingIndex == _clips.length - 1;
if (isComplete) {
print("played all!!");
if (!_showingDialog) {
_showingDialog = true;
_showPlayedAllDialog().then((value) {
_exitFullScreen();
_showingDialog = false;
});
}
} else {
_initializeAndPlay(_playingIndex + 1);
}
}
}
}
Future<bool> _showPlayedAllDialog() async {
return showDialog<bool>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(child: Text("Played all videos.")),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text("Close"),
),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _isFullScreen
? null
: AppBar(
title: Text("Play View"),
),
body: _isFullScreen
? Container(
child: Center(child: _playView(context)),
decoration: BoxDecoration(color: Colors.black),
)
: Column(children: <Widget>[
Container(
child: Center(child: _playView(context)),
decoration: BoxDecoration(color: Colors.black),
),
Expanded(
child: _listView(),
),
]),
);
}
void _onTapCard(int index) {
_initializeAndPlay(index);
}
Widget _playView(BuildContext context) {
final controller = _controller;
if (controller != null && controller.value.isInitialized) {
return AspectRatio(
//aspectRatio: controller.value.aspectRatio,
aspectRatio: 16.0 / 9.0,
child: Stack(
children: <Widget>[
GestureDetector(
child: VideoPlayer(controller),
onTap: _onTapVideo,
),
_controlAlpha > 0
? AnimatedOpacity(
opacity: _controlAlpha,
duration: Duration(milliseconds: 250),
child: _controlView(context),
)
: Container(),
],
),
);
} else {
return AspectRatio(
aspectRatio: 16.0 / 9.0,
child: Center(
child: Text(
"Preparing ...",
style: TextStyle(color: Colors.white70, fontWeight: FontWeight.bold, fontSize: 18.0),
)),
);
}
}
Widget _listView() {
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
itemCount: _clips.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
borderRadius: BorderRadius.all(Radius.circular(6)),
splashColor: Colors.blue[100],
onTap: () {
_onTapCard(index);
},
child: _buildCard(index),
);
},
).build(context);
}
Widget _controlView(BuildContext context) {
return Column(
children: <Widget>[
_topUI(),
Expanded(
child: _centerUI(),
),
_bottomUI()
],
);
}
Widget _centerUI() {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
onPressed: () async {
final index = _playingIndex - 1;
if (index > 0 && _clips.length > 0) {
_initializeAndPlay(index);
}
},
child: Icon(
Icons.fast_rewind,
size: 36.0,
color: Colors.white,
),
),
TextButton(
onPressed: () async {
if (_isPlaying) {
_controller?.pause();
_isPlaying = false;
} else {
final controller = _controller;
if (controller != null) {
final pos = _position?.inSeconds ?? 0;
final dur = _duration?.inSeconds ?? 0;
final isEnd = pos == dur;
if (isEnd) {
_initializeAndPlay(_playingIndex);
} else {
controller.play();
}
}
}
setState(() {});
},
child: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
size: 56.0,
color: Colors.white,
),
),
TextButton(
onPressed: () async {
final index = _playingIndex + 1;
if (index < _clips.length - 1) {
_initializeAndPlay(index);
}
},
child: Icon(
Icons.fast_forward,
size: 36.0,
color: Colors.white,
),
),
],
));
}
String convertTwo(int value) {
return value < 10 ? "0$value" : "$value";
}
Widget _topUI() {
final noMute = (_controller?.value?.volume ?? 0) > 0;
final duration = _duration?.inSeconds ?? 0;
final head = _position?.inSeconds ?? 0;
final remained = max(0, duration - head);
final min = convertTwo(remained ~/ 60.0);
final sec = convertTwo(remained % 60);
return Row(
children: <Widget>[
InkWell(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Container(
decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
BoxShadow(offset: const Offset(0.0, 0.0), blurRadius: 4.0, color: Color.fromARGB(50, 0, 0, 0)),
]),
child: Icon(
noMute ? Icons.volume_up : Icons.volume_off,
color: Colors.white,
)),
),
onTap: () {
if (noMute) {
_controller?.setVolume(0);
} else {
_controller?.setVolume(1.0);
}
setState(() {});
},
),
Expanded(
child: Container(),
),
Text(
"$min:$sec",
style: TextStyle(
color: Colors.white,
shadows: <Shadow>[
Shadow(
offset: Offset(0.0, 1.0),
blurRadius: 4.0,
color: Color.fromARGB(150, 0, 0, 0),
),
],
),
),
SizedBox(width: 10)
],
);
}
Widget _bottomUI() {
return Row(
children: <Widget>[
SizedBox(width: 20),
Expanded(
child: Slider(
value: max(0, min(_progress * 100, 100)),
min: 0,
max: 100,
onChanged: (value) {
setState(() {
_progress = value * 0.01;
});
},
onChangeStart: (value) {
debugPrint("-- onChangeStart $value");
_controller?.pause();
},
onChangeEnd: (value) {
debugPrint("-- onChangeEnd $value");
final duration = _controller?.value?.duration;
if (duration != null) {
var newValue = max(0, min(value, 99)) * 0.01;
var millis = (duration.inMilliseconds * newValue).toInt();
_controller?.seekTo(Duration(milliseconds: millis));
_controller?.play();
}
},
),
),
IconButton(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
color: Colors.yellow,
icon: Icon(
Icons.fullscreen,
color: Colors.white,
),
onPressed: _toggleFullscreen,
),
],
);
}
Widget _buildCard(int index) {
final clip = _clips[index];
final playing = index == _playingIndex;
String runtime;
if (clip.runningTime > 60) {
runtime = "${clip.runningTime ~/ 60}분 ${clip.runningTime % 60}초";
} else {
runtime = "${clip.runningTime % 60}초";
}
return Card(
child: Container(
padding: EdgeInsets.all(4),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 8),
child: clip.parent.startsWith("http")
? Image.network(clip.thumbPath(), width: 70, height: 50, fit: BoxFit.fill)
: Image.asset(clip.thumbPath(), width: 70, height: 50, fit: BoxFit.fill),
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(clip.title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Padding(
child: Text("$runtime", style: TextStyle(color: Colors.grey[500])),
padding: EdgeInsets.only(top: 3),
)
]),
),
Padding(
padding: EdgeInsets.all(8.0),
child: playing
? Icon(Icons.play_arrow)
: Icon(
Icons.play_arrow,
color: Colors.grey.shade300,
),
),
],
),
),
);
}
}

Related

Animation library is giving me this error about lifecycle state

Animation library is giving me this error about lifecycle state-- 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4519 pos 12: '_lifecycleState != _ElementLifecycle.defunct': is not true. is was getting this error when i used animation controller.
After the error my flutter application crashed and i have to reload everytime.
// ignore_for_file: depend_on_referenced_packages, sized_box_for_whitespace, avoid_print
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:habbit_app/controllers/timer_tab_controller.dart';
import 'package:habbit_app/screens/timer/set_timer_component.dart';
import 'package:flutter_ringtone_player/flutter_ringtone_player.dart';
import 'package:habbit_app/widgets/text_widget/label_text.dart';
class TimerTab extends StatefulWidget {
const TimerTab({super.key});
#override
State<TimerTab> createState() => _TimerTabState();
}
class _TimerTabState extends State<TimerTab> with TickerProviderStateMixin {
AnimationController? animationController;
TimerTabController tabController =
Get.put(TimerTabController(), permanent: false);
late TabController _controller;
bool isPlaying = false;
// int get countText2 {
// int count = tabController.totalSeconds;
// return;
// }
String get countText {
Duration count =
animationController!.duration! * animationController!.value;
return animationController!.isDismissed
? '${animationController!.duration!.inHours}:${(animationController!.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(animationController!.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
: '${count.inHours}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
}
#override
void dispose() {
animationController!.dispose();
super.dispose();
}
double progress = 1.0;
void notifiy() {
if (countText == "0:00:00") {
FlutterRingtonePlayer.playNotification();
}
}
#override
void initState() {
// ignore: todo
// TODO: implement initState
TimerTabController tabController =
Get.put(TimerTabController(), permanent: false);
super.initState();
_controller = TabController(length: 3, vsync: this);
_controller.addListener(() {
tabController.tabIndex.value = _controller.index;
// print(tabController.tabIndex.value);
});
super.initState();
animationController = AnimationController(
vsync: this, duration: Duration(seconds: tabController.totalSeconds));
animationController!.addListener(() {
notifiy();
if (animationController!.isAnimating) {
setState(() {
progress = animationController!.value;
});
} else {
setState(() {
progress = 1.0;
isPlaying = false;
});
}
});
}
#override
Widget build(BuildContext context) {
var color = Theme.of(context);
return Obx(
() => tabController.isFirst.value
? SetTimerComponent(
changeAnimation: () {
animationController!.duration =
Duration(seconds: tabController.totalSeconds);
tabController.changeFirst();
},
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// start timer screen
Container(
height: 350,
width: MediaQuery.of(context).size.width,
// color: color.cardColor,
child: Column(children: [
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: 300,
height: 300,
child: CircularProgressIndicator(
color: color.primaryColor,
backgroundColor:
color.disabledColor.withOpacity(0.6),
value: progress,
strokeWidth: 10,
)),
GestureDetector(
onTap: () {
if (animationController!.isDismissed) {
showModalBottomSheet(
context: context,
builder: (context) => Container(
height: 300,
child: CupertinoTimerPicker(
initialTimerDuration:
animationController!.duration!,
backgroundColor:
color.backgroundColor,
onTimerDurationChanged: (time) {
setState(() {
animationController!.duration =
time;
});
},
),
));
}
},
child: AnimatedBuilder(
animation: animationController!,
builder: ( context, child) => Text(
countText,
style: TextStyle(
fontSize: 60,
fontWeight: FontWeight.bold,
color: color.primaryColor),
),
),
),
],
),
]),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
print(tabController.totalSeconds);
if (animationController!.isAnimating) {
animationController!.stop();
setState(() {
isPlaying = false;
});
} else {
animationController!.reverse(
from: animationController!.value == 0
? 1.0
: animationController!.value);
setState(() {
isPlaying = true;
});
}
},
child: Container(
height: 35,
width: 90,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(Radius.circular(30)),
color: color.primaryColor),
child: Center(
child: LabelText(
text: isPlaying == true ? "PAUSE" : "RESUME",
isBold: true,
isColor: true,
color: Colors.white,
)),
),
),
GestureDetector(
onTap: () {
animationController!.reset();
tabController.currentvalueHour.value = 0;
tabController.currentvalueMin.value = 0;
tabController.currentvalueSec.value = 0;
setState(() {
isPlaying = false;
});
tabController.changeFirst2();
},
child: Container(
height: 35,
width: 90,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(Radius.circular(30)),
color: color.disabledColor.withOpacity(0.5)),
child: const Center(
child: LabelText(
text: "DELETE",
isColor: true,
color: Colors.white,
isBold: true,
)),
),
)
],
)
],
),
);
}
}

flutter display only a percentage of the picture

Hello i would like to show only percentage of the photo I take with my camera. This code allows me to take several photos with the camera of my phone. I can also film. What I would like to do is show me only a percentage of the photos I take. Example 70% on the sides and 80% above and below
Here is the code:
class NoGluecamera extends StatefulWidget {
#override
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<NoGluecamera>
with WidgetsBindingObserver {
CameraController? controller;
VideoPlayerController? videoController;
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;
// Current values
double _currentZoomLevel = 1.0;
double _currentExposureOffset = 0.0;
FlashMode? _currentFlashMode;
List<File> allFileList = [];
final resolutionPresets = ResolutionPreset.values;
ResolutionPreset currentResolutionPreset = ResolutionPreset.medium;
refreshAlreadyCapturedImages() async {
final directory = await getApplicationDocumentsDirectory();
List<FileSystemEntity> fileList = await directory.list().toList();
allFileList.clear();
List<Map<int, dynamic>> fileNames = [];
fileList.forEach((file) {
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 (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(() {});
}
}
Future<XFile?> takePicture(
) async {
final CameraController? cameraController = controller;
if (cameraController!.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
try {
XFile file = await cameraController.takePicture();
return file;
} on CameraException catch (e) {
print('Error occured while taking picture: $e');
return null;
}
}
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();
}
}
Future<void> startVideoRecording() async {
final CameraController? cameraController = controller;
if (controller!.value.isRecordingVideo) {
// A recording has already started, do nothing.
return;
}
try {
await cameraController!.startVideoRecording();
setState(() {
_isRecordingInProgress = true;
print(_isRecordingInProgress);
});
} on CameraException catch (e) {
print('Error starting to record video: $e');
}
}
Future<XFile?> stopVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// Recording is already is stopped state
return null;
}
try {
XFile file = await controller!.stopVideoRecording();
setState(() {
_isRecordingInProgress = false;
});
return file;
} on CameraException catch (e) {
print('Error stopping video recording: $e');
return null;
}
}
Future<void> pauseVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// Video recording is not in progress
return;
}
try {
await controller!.pauseVideoRecording();
} on CameraException catch (e) {
print('Error pausing video recording: $e');
}
}
Future<void> resumeVideoRecording() async {
if (!controller!.value.isRecordingVideo) {
// No video recording was in progress
return;
}
try {
await controller!.resumeVideoRecording();
} on CameraException catch (e) {
print('Error resuming video recording: $e');
}
}
void resetCameraValues() async {
_currentZoomLevel = 1.0;
_currentExposureOffset = 0.0;
}
void onNewCameraSelected(CameraDescription cameraDescription) async {
final previousCameraController = controller;
final CameraController cameraController = CameraController(
cameraDescription,
currentResolutionPreset,
imageFormatGroup: ImageFormatGroup.jpeg,
);
await previousCameraController?.dispose();
resetCameraValues();
if (mounted) {
setState(() {
controller = cameraController;
});
}
// 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 (mounted) {
setState(() {
_isCameraInitialized = controller!.value.isInitialized;
});
}
}
#override
void initState() {
// Hide the status bar in Android
SystemChrome.setEnabledSystemUIOverlays([]);
// Set and initialize the new camera
onNewCameraSelected(cameras[0]);
refreshAlreadyCapturedImages();
super.initState();
}
#override
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?.dispose();
videoController?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
body: _isCameraInitialized
? Column(
children: [
AspectRatio(
aspectRatio: 3 / 5,
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: 4.0,
right: 4.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: TextStyle(
color: Colors.white),
),
value: preset,
)
],
onChanged: (value) {
setState(() {
currentResolutionPreset = value!;
_isCameraInitialized = false;
});
onNewCameraSelected(
controller!.description);
},
hint: 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: TextStyle(color: Colors.black),
),
),
),
),
Expanded(
child: RotatedBox(
quarterTurns: 3,
child: Container(
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: 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: [
Icon(
Icons.circle,
color: Colors.black38,
size: 60,
),
_isRecordingInProgress
? controller!
.value.isRecordingPaused
? Icon(
Icons.play_arrow,
color: Colors.white,
size: 30,
)
: 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;
print(fileFormat);
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
? 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: 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: Text('IMAGE'),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 4.0, right: 8.0),
child: TextButton(
onPressed: () {
if (!_isVideoCameraSelected) {
setState(() {
_isVideoCameraSelected = true;
});

AutoScroll Text with pinch and zoom in flutter

I'm new to flutter and I'm trying to implement a page where I want to show Text Widget with AutoScroll Also users can Zoom in and Zoom Out the text. I have implemented AutoScroll using the help of this link (AutoScroll Text). I have also added GestureDetector but It's not working as expected. Here is the complete source code which I have right now...
import 'dart:async';
import 'dart:ui';
import 'package:ffmpeg_demo/Utility/util.dart';
import 'package:flutter/material.dart';
class PDFViewer extends StatefulWidget {
final Function() onDismissTapped;
final String pdfText;
const PDFViewer(
{Key? key, required this.onDismissTapped, required this.pdfText})
: super(key: key);
#override
State<PDFViewer> createState() => _PDFViewerState();
}
class _PDFViewerState extends State<PDFViewer> {
double scrollSpeed = 1.0;
Function? onDismissTapped;
//https://stackoverflow.com/questions/57138772/how-to-autoscroll-text-in-flutter
final ScrollController _scrollController = ScrollController();
bool scroll = false;
int speedFactor = 100;
//For Scaling Text Area
//https://stackoverflow.com/questions/55440184/flutter-gesturedetector-how-to-pinch-in-out-or-zoom-in-out-text-using-two-finge
double _fontSize = 20;
final double _baseFontSize = 20;
double _fontScale = 1;
double _baseFontScale = 1;
double _scaleFactor = 1.0;
double _baseScaleFactor = 1.0;
bool _stopScrolling = false;
_scroll() {
double maxExtent = _scrollController.position.maxScrollExtent;
double distanceDifference = maxExtent - _scrollController.offset;
double durationDouble = distanceDifference / speedFactor;
int duration = durationDouble.toInt();
if (duration <= 0) {
duration = 1;
}
print(
"maxScrollExtent = $maxExtent and distanceDifference = $distanceDifference and durationDouble = $durationDouble");
_scrollController.animateTo(_scrollController.position.maxScrollExtent,
duration: Duration(seconds: duration), curve: Curves.linear);
}
_toggleScrolling() {
/* if (_stopScrolling) {
print("Stop scrolling flag value is $_stopScrolling");
return;
} */
setState(() {
scroll = !scroll;
});
if (scroll) {
_scroll();
} else {
_scrollController.animateTo(_scrollController.offset,
duration: const Duration(seconds: 1), curve: Curves.linear);
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
Future.delayed(const Duration(seconds: 1), () {
_toggleScrolling();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
color: Colors.black.withOpacity(0.5),
child: Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 50, 10, 100),
child: NotificationListener(
onNotification: (notif) {
if (notif is ScrollEndNotification && scroll) {
Timer(const Duration(seconds: 1), () {
_scroll();
});
}
return true;
},
child: SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
padding: const EdgeInsets.all(20),
child: DefaultTextStyle(
style: const TextStyle(color: Colors.white),
child: GestureDetector(
onScaleStart: (ScaleStartDetails scaleStartDetails) {
setState(() {
//_stopScrolling = true;
_toggleScrolling();
});
//_baseScaleFactor = _scaleFactor;
_baseFontScale = _fontScale;
print("base scale factor = $_baseFontScale");
},
onScaleUpdate:
(ScaleUpdateDetails scaleUpdateDetails) {
// don't update the UI if the scale didn't change
/* if (scaleUpdateDetails.scale == 1.0) {
debugPrint("Scale is 1.0 returning");
setState(() {
_stopScrolling = false;
_toggleScrolling();
});
return;
} */
/* _scaleFactor =
_baseScaleFactor * scaleUpdateDetails.scale;
print("Final scale factor = $_scaleFactor");
if (_scaleFactor < 1.0) {
_scaleFactor = 1.0;
}
print("New Final scale factor = $_scaleFactor"); */
//setState(() {
_fontScale =
(_baseFontScale * scaleUpdateDetails.scale)
.clamp(0.5, 5.0);
_fontSize = _fontScale * _baseFontSize;
debugPrint("New Final font size = $_fontSize");
if (_fontSize < 20) {
_fontSize = 20;
}
//});
setState(() {
//_toggleScrolling();
_stopScrolling = false;
_toggleScrolling();
});
},
child: Text(
widget.pdfText,
/* textScaleFactor:
(_scaleFactor <= 0.1) ? 1.0 : _scaleFactor, */
style: TextStyle(
fontSize: (_fontSize < 20) ? 20 : _fontSize),
),
),
),
),
),
),
Positioned(
bottom: 0,
child: Container(
height: 100,
width: deviceWidth(context),
color: Colors.black.withAlpha(50),
child: Stack(
children: [
Positioned(
bottom: 0,
width: deviceWidth(context),
child: Center(
child: TextButton(
child: const Text(
"Dismiss",
style: TextStyle(color: Colors.white),
),
onPressed: () => widget.onDismissTapped(),
),
),
),
Positioned(
top: 0,
width: deviceWidth(context),
child: Container(
height: 50,
width: deviceWidth(context),
child: _buildSlider(context)),
),
],
),
),
),
],
),
),
),
),
);
}
Widget _buildSlider(BuildContext context) {
return Container(
width: deviceWidth(context),
child: SliderTheme(
data: SliderThemeData(
activeTrackColor: Colors.brown[700],
inactiveTrackColor: Colors.brown[300],
inactiveTickMarkColor: Colors.transparent,
activeTickMarkColor: Colors.transparent),
child: Slider(
divisions: 100,
min: 0,
max: 100,
value: speedFactor.toDouble(),
onChanged: (double value) {
setState(() {
//scrollSpeed = value;
_toggleScrolling();
});
speedFactor = value.toInt();
if (speedFactor == 0) {
speedFactor = 1;
}
Future.delayed(const Duration(seconds: 1), () {
setState(() {
//scrollSpeed = value;
_toggleScrolling();
});
});
}),
),
);
}
}

flutter:: RangeError (index): Invalid value: Not in inclusive range 0..14: 15

I made a function to delete saved recording files.
The delete function works fine.
However, if it is deleted, an error
'RangeError (index): Invalid value: Not in inclusive range 0..14: 15'
will occur and it will look like the picture above.
How can I solve this?
This is the delete function.
_delete() {
widget.file[widget.index].delete(recursive: true);
setState(() {
position = new Duration();
widget.file.removeAt(widget.index);
});
}
This is my listview code.
class _AudioViewerState extends State<AudioViewer> {
var audioPath;
var directory;
List file = [];
#override
void initState() {
super.initState();
getFiles();
}
void getFiles() async {
directory = (await getExternalStorageDirectory())!.path;
setState(() {
file = Directory("$directory").listSync();
});
}
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
elevation: 0,
title: Text(
"Audio List",
),
),
body: Container(
child: CustomPaint(
child: ListView.builder(
reverse: true,
itemCount: file.length,
itemBuilder: (BuildContext context, int index) {
return AudioViewerCustomTile(
text: file[index].path.split('/').last,
path: file[index].path.toString(),
height: height,
width: width,
file: file,
index: index,
);
},
),
),
),
);
}
}
This is the file with the delete function.
class DialogBuilder extends StatefulWidget {
final String path;
final int index;
final List file;
DialogBuilder({required this.path,required this.index,required this.file});
#override
_DialogBuilderState createState() => _DialogBuilderState();
}
class _DialogBuilderState extends State<DialogBuilder> {
late AudioPlayer audioPlayer;
bool _isplaying = false;
var _icon = Icons.play_arrow;
var _deleteicon = Icons.delete;
Color _color = Colors.deepOrangeAccent;
Duration position = Duration();
Duration duration = Duration(seconds: 1);
#override
Widget build(BuildContext context) {
return Container(
height: 200,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
Container(
child: IconButton(
iconSize: 100,
onPressed: () {
if (!_isplaying) {
_play();
} else {
_stop();
}
},
icon: Icon(_icon),
color: _color,
),
),
Container(
child: IconButton(
iconSize: 50,
onPressed: () {
_delete();
},
icon: Icon(_deleteicon),
),
),
]
)
),
Padding(
padding: const EdgeInsets.all(8.0),
child: LinearProgressIndicator(
backgroundColor: Colors.grey[300],
valueColor: AlwaysStoppedAnimation(Colors.deepOrangeAccent),
value: (position.inMilliseconds / duration.inMilliseconds) * 1.0,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
_getDuration(position),
style: TextStyle(color: Colors.black),
),
Text(
_getDuration(duration),
style: TextStyle(color: Colors.black),
)
],
),
)
],
),
);
}
String _getDuration(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}
_play() {
audioPlayer.resume();
setState(() {
_isplaying = true;
_icon = Icons.pause;
_color = Colors.blueGrey;
});
}
_stop() {
audioPlayer.release();
setState(() {
position = new Duration();
_isplaying = false;
_icon = Icons.play_arrow;
_color = Colors.deepOrangeAccent;
});
}
_delete() {
widget.file[widget.index].delete(recursive: true);
setState(() {
position = new Duration();
widget.file.removeAt(widget.index);
});
}
_stateListener(PlayerState state) {
print(" In _stateListener the state of AudioPlayer :- " +
state.toString() +
"\n");
if (state == PlayerState.COMPLETED) {
Navigator.pop(context);
_stop();
}
}
The List file = []; is an empty list, so when you try to do itemCount = file.length, it displays an error, try giving the List file; a value instead of a an empty list and im sure it will work

How to save page state on revisit in flutter

I have 2 screens and I am trying to achieve understand how to achieve page state. For example, in the below screen, I have 4 options, and all of them take the user to the same screen the only difference is API getting called for each of them is different to build a list. I am trying to handle back arrow action, and that's where I am having issues.
Use Case -
When user is on screen 2 he is playing the song, now on back press song continues to play. Now when the user again chooses the same option from screen 1 I want to show the same list without reload and selection. If user selects any other option it should behave normal, which is working.
Solution -
I can hardcode loaded songs list and send back to screen 1 and then again get that back with the selection but this will take lot of resources.
AutomaticKeepAliveClientMixin i tried this tutorial but that didn't help or kept state active.
PageStorage is 3rd option i saw but i found majority of the time this is being used on app where we have a tab.
Screen 1 -
class Dashboard extends StatefulWidget {
int playingId;
Dashboard({this.playingId});
#override
_DashboardState createState() => _DashboardState(playingId);
}
class _DashboardState extends State<Dashboard> {
String appname;
int playingId = 0;
_DashboardState(this.playingId);
// print('${playingId}');
#override
void initState() {
appname="";
// playingId=0;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: AppColors.darkBlue,
centerTitle: true,
title: Text("Tirthankar",
style: TextStyle(color: Colors.white),),
),
backgroundColor: AppColors.styleColor,
body: Column(
children: <Widget>[
// SizedBox(height: 5),
Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomGridWidget(
child: Icon(
Icons.file_download,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/bhaktambar.png',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
label: "Bhakti",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Kids",playingId: playingId,),
),
);
},
),
CustomGridWidget(
child: Icon(
Icons.file_download,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/bhaktambar.png',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
label: "Kids",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Kids",playingId: playingId,),
),
);
},
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 20,
right: 20,
bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomGridWidget(
child: Icon(
Icons.favorite,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/kids.jpg',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
label: "Favorite",
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Songs"),
),
);
},
),
Align(
alignment: Alignment.center,
child: CustomGridWidget(
child: Icon(
Icons.book,
size: 100,
color: AppColors.styleColor,
),
// image: 'assets/vidyasagar.jpg',
sizew: MediaQuery.of(context).size.width * .4,
sizeh: MediaQuery.of(context).size.width * .5,
borderWidth: 2,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ListPage(appname: "Bhajan"),
),
);
},
),
),
],
),
),
]
),
);
}
Material boxTiles(IconData icon, String name){
return Material(
color: AppColors.mainColor,
elevation: 14.0,
shadowColor: AppColors.styleColor,
borderRadius: BorderRadius.circular(24.0),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child:Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
InkWell(
onTap: (){print("tapped"); /* or any action you want */ },
child: Container(
width: 130.0,
height: 10.0,
color : Colors.transparent,
), // container
), //
//Text
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(name,
style:TextStyle(
color: AppColors.styleColor,
fontSize: 20.0,
)
),
),
//Icon
Material(
color: AppColors.styleColor,
borderRadius: BorderRadius.circular(50.0),
child: Padding(padding: const EdgeInsets.all(10.0),
child: Icon(icon, color: AppColors.lightBlue,size: 30,),
),
),
],
)
],
)
),
)
);
}
}
Screen 2 -
// import 'dart:js';
class ListPage extends StatefulWidget {
String appname;
int playingId;
ListPage({this.appname,this.playingId});
#override
_ListPageState createState() => _ListPageState(appname,playingId);
}
class _ListPageState extends State<ListPage>
with SingleTickerProviderStateMixin,AutomaticKeepAliveClientMixin<ListPage> {
String appname;
int playingId;
bool isPlaying = false;
_ListPageState(this.appname,playingId);
// List<MusicModel> _list1;
List<MusicData> _list;
var _value;
int _playId;
int _songId;
String _playURL;
bool _isRepeat;
bool _isShuffle;
bool _isFavorite;
String _startTime;
String _endTime;
AnimationController _controller;
Duration _duration = new Duration();
Duration _position = new Duration();
final _random = new Random();
AudioPlayer _audioPlayer = AudioPlayer();
#override
void initState() {
_playId = 0;
// _list1 = MusicModel.list;
this._fileUpdate();
// _list = _list1;
_controller =
AnimationController(vsync: this, duration: Duration(microseconds: 250));
_value = 0.0;
_startTime = "0.0";
_endTime = "0.0";
_isRepeat = false;
_isShuffle = false;
_isFavorite = false;
_audioPlayer.onAudioPositionChanged.listen((Duration duration) {
setState(() {
// _startTime = duration.toString().split(".")[0];
_startTime = duration.toString().split(".")[0];
_duration = duration;
// _position = duration.toString().split(".")[0];
});
});
_audioPlayer.onDurationChanged.listen((Duration duration) {
setState(() {
_endTime = duration.toString().split(".")[0];
_position = duration;
});
});
_audioPlayer.onPlayerCompletion.listen((event) {
setState(() {
isPlaying = false;
_position = _duration;
if (_isRepeat) {
_songId = _songId;
} else {
if (_isShuffle) {
var element = _list[_random.nextInt(_list.length)];
_songId = element.id;
} else {
_songId = _songId + 1;
}
}
_player(_songId);
});
});
super.initState();
}
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: AppColors.mainColor,
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: (){
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => Dashboard(playingId: _songId),
),
);
},
),
title: Text(
appname,
style: TextStyle(color: AppColors.styleColor),
),
),
backgroundColor: AppColors.mainColor,
body: Stack(
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomButtonWidget(
child: Icon(
Icons.favorite,
color: AppColors.styleColor,
),
size: 50,
onTap: () {},
),
CustomButtonWidget(
image: 'assets/logo.jpg',
size: 100,
borderWidth: 5,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => DetailPage(),
),
);
},
),
CustomButtonWidget(
child: Icon(
Icons.menu,
color: AppColors.styleColor,
),
size: 50,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => HomePage(),
),
);
},
)
],
),
),
slider(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(
_isRepeat ? Icons.repeat_one : Icons.repeat,
color: _isRepeat ? Colors.brown : AppColors.styleColor,
),
onPressed: () {
if (_isRepeat) {
_isRepeat = false;
} else {
_isRepeat = true;
}
},
),
IconButton(
icon: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
color: AppColors.styleColor,
),
onPressed: () {
if (isPlaying) {
_audioPlayer.pause();
setState(() {
isPlaying = false;
});
} else {
if (!isPlaying){
_audioPlayer.resume();
setState(() {
isPlaying = true;
});
}
}
}),
IconButton(
icon: Icon(
Icons.stop,
color: AppColors.styleColor,
),
onPressed: () {
if (isPlaying){
_audioPlayer.stop();
setState(() {
isPlaying = false;
_duration = new Duration();
});
}
// isPlaying = false;
}),
IconButton(
icon: Icon(
Icons.shuffle,
color:
_isShuffle ? Colors.brown : AppColors.styleColor,
),
onPressed: () {
if (_isShuffle) {
_isShuffle = false;
} else {
_isShuffle = true;
}
}),
],
),
),
Expanded(
//This is added so we can see overlay else this will be over button
child: ListView.builder(
physics:
BouncingScrollPhysics(), //This line removes the dark flash when you are at the begining or end of list menu. Just uncomment for
// itemCount: _list.length,
itemCount: _list == null ? 0 : _list.length,
padding: EdgeInsets.all(12),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
_songId = index;
_player(index);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
//This below code will change the color of sected area or song being played.
decoration: BoxDecoration(
color: _list[index].id == _playId
? AppColors.activeColor
: AppColors.mainColor,
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
//End of row color change
child: Padding(
padding: const EdgeInsets.all(
16), //This will all padding around all size
child: Row(
mainAxisAlignment: MainAxisAlignment
.spaceBetween, //This will allign button to left, else button will be infront of name
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
_list[index].title,
style: TextStyle(
color: AppColors.styleColor,
fontSize: 16,
),
),
Text(
_list[index].album,
style: TextStyle(
color: AppColors.styleColor.withAlpha(90),
fontSize: 16,
),
),
],
),
IconButton(
icon: Icon(_isFavorite
? Icons.favorite
: Icons.favorite_border),
onPressed: () {
if (_isFavorite) {
_isFavorite = false;
} else {
_isFavorite = true;
}
})
//Diabled Play button and added fav button.
// CustomButtonWidget(
// //This is Play button functionality on list page.
// child: Icon(
// _list[index].id == _playId
// ? Icons.pause
// : Icons.play_arrow,
// color: _list[index].id == _playId
// ? Colors.white
// : AppColors.styleColor,
// ),
// size: 50,
// isActive: _list[index].id == _playId,
// onTap: () async {
// _songId = index;
// _player(index);
// },
// )
],
),
),
),
);
},
),
)
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.mainColor.withAlpha(0),
AppColors.mainColor,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)),
),
)
],
),
// floatingActionButton: FloatingActionButton(
// child: Icon(Icons.music_note),
// onPressed: () async { // String filePath = await FilePicker.getFilePath();
// int status = await _audioPlayer.play("https://traffic.libsyn.com/voicebot/Jan_Konig_on_the_Jovo_Open_Source_Framework_for_Voice_App_Development_-_Voicebot_Podcast_Ep_56.mp3");
// if (status == 1){
// setState(() {
// isPlaying = true;
// });
// }
// },
// )
);
}
Widget slider() {
return Slider(
activeColor: AppColors.styleColor,
inactiveColor: Colors.lightBlue,
value: _duration.inSeconds.toDouble(),
min: 0.0,
max: _position.inSeconds.toDouble(),
divisions: 10,
onChangeStart: (double value) {
print('Start value is ' + value.toString());
},
onChangeEnd: (double value) {
print('Finish value is ' + value.toString());
},
onChanged: (double value) {
setState(() {
seekToSecond(value.toInt());
value = value;
});
});
}
Future<Void> _fileUpdate() async {
String url =
"azonaws.com/input.json";
String arrayObjsText = "";
try {
eos.Response response;
Dio dio = new Dio();
response = await dio.get(url,options: Options(
responseType: ResponseType.plain,
),);
arrayObjsText = response.data;
print(response.data.toString());
} catch (e) {
print(e);
}
var tagObjsJson = jsonDecode(arrayObjsText)['tags'] as List;
this.setState(() {
_list = tagObjsJson.map((tagJson) => MusicData.fromJson(tagJson)).toList();
});
// return _list;
// print(_list);
}
Future<void> _player(int index) async {
if (isPlaying) {
if (_playId == _list[index].id) {
int status = await _audioPlayer.pause();
if (status == 1) {
setState(() {
isPlaying = false;
});
}
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
_audioPlayer.stop();
int status = await _audioPlayer.play(_playURL);
if (status == 1) {
setState(() {
isPlaying = true;
});
}
}
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
int status = await _audioPlayer.play(_playURL);
if (status == 1) {
setState(() {
isPlaying = true;
});
}
}
}
String _printDuration(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}
void seekToSecond(int second) {
Duration newDuration = Duration(seconds: second);
_audioPlayer.seek(newDuration);
}
}
PageStorage is 3rd option i saw but i found majority of the time this is being used on app where we have a tab.
You still can use this approach with what you want, with or without Tab/ bottom navigation bar.
PageViewClass
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin{
PageController _pageController;
#override
void initState() {
super.initState();
_pageController = PageController();
}
#override
void dispose() {
super.dispose();
_pageController?.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: PageView(
controller: _pageController,
physics: NeverScrollableScrollPhysics(), // so the user cannot scroll, only animating when they select an option
children: <Widget>[
Dashboard(playingId: 1, key: PageStorageKey<String>('MyPlayList'), pageController: _pageController), //or the name you want, but you need to give them a key to all the child so it can save the Scroll Position
ListPage(appname: "FirstButton",playingId: 1, key: PageStorageKey<String>('FirstButton'), pageController: _pageController),
ListPage(appname: "SecondButton",playingId: 1, key: PageStorageKey<String>('SecondButton'), pageController: _pageController),
ListPage(appname: "ThirdButton",playingId: 1, key: PageStorageKey<String>('ThirdButton'), pageController: _pageController),
ListPage(appname: "FourthButton",playingId: 1, key: PageStorageKey<String>('FourthButton'), pageController: _pageController)
],
),
)
);
}
}
Now you pass the PageController to all children (add a key and PageController attribute to Screen 1 and 2) and in DashBoard (Screen 1) you can change the onTap of the buttons from the MaterialRoute to this
onTap: () => widget.pageController.jumpToPage(x), //where x is the index of the children of the PageView you want to see (between 0 and 4 in this case)
In Screen 2 you wrap all your Scaffold with a WillPopScope so when the user tap back instead of closing the route it goes back to the Dashboard Widget at index 0
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
widget.pageController.jumpToPage(0); // Move back to dashboard (index 0)
return false;
}
child: Scaffold(...)
);
}
You can use other methods of PageController if you want some effects like animations (animateTo, nextPage, previousPage, etc.)