Cannot add new events after calling close - flutter

Flutter
i am using video_trimmer: ^0.5.2 plugin , which allows cutting and editing the video before uploading ,on pressed so everything work fine from the first time but if i reopen the editor again by clicking the bottom, video will be shown without the TrimmerEditor widget with this error Cannot add new events after calling close but if left the page and re-entered again, everything will work fine except if i click again and again with keeping in the same UI page !
i already know that happen because of the widget which is close again from the stack IF (true or false ) but i need a solution with same my code cuse i have big complicated project in the same code
class VideoTrimmer extends StatefulWidget {
const VideoTrimmer({Key key}) : super(key: key);
#override
_VideoTrimmerState createState() => _VideoTrimmerState();
}
class _VideoTrimmerState extends State<VideoTrimmer> {
Trimmer _trimmer = Trimmer(); // this object from the package its self
double _startValue = 0.0;
double _endValue = 0.0;
bool _isPlaying = false;
bool displayWidget = false;
_loadVideo() {
_trimmer.loadVideo(videoFile: file);
}
#override
void dispose() {
super.dispose();
_trimmer.dispose(); // i tried to remove this too , but same issue
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Stack(
children: [
TextButton(
onPressed: (){
final result = await FilePicker.platform.pickFiles(allowMultiple: false );
if (result == null) return;
final path = result.files.single.path;
setState(() => file = File(path));
_loadVideo();
setState(() => displayWidget = true);
},
child: Text("selectVideo")
),
displayWidget ?
Center(
child: Container(
padding: EdgeInsets.only(bottom: 30.0),
color: Colors.black,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: VideoViewer(trimmer: _trimmer),
),
Center(
child: TrimEditor( // here is the issue happen
trimmer: _trimmer,
viewerHeight: 50.0,
viewerWidth: MediaQuery.of(context).size.width,
maxVideoLength: Duration(seconds: 60),
onChangeStart: (value) {
_startValue = value;
},
onChangeEnd: (value) {
_endValue = value;
},
onChangePlaybackState: (value) {
if(!mounted) {
setState(() =>
_isPlaying = value);
}
},
),
),
TextButton(
child: _isPlaying
? Icon(
Icons.pause,
size: 80.0,
color: Colors.white,
)
: Icon(
Icons.play_arrow,
size: 80.0,
color: Colors.white,
),
onPressed: () async {
bool playbackState = await _trimmer.videPlaybackControl(
startValue: _startValue,
endValue: _endValue,
);
if(!mounted) {
setState(() =>
_isPlaying = playbackState);
}
},
),
ElevatedButton(
child: Text("SAVE"),
onPressed: () {
uploadVideo();
setState(() => displayWidget = false);
},
),
],
),
),
),
],
),
);
}
}

Related

Flutter - Want to show camera Tabbarview in full screen

I am building WhatsApp clone for demo. I want to hide AppBar and Tabbar when I click on Camera tab and I want CameraScreen in full screen. I hope, I could made very clear. I have also included whole code of the CameraScreen page thus you can understand(just edited to add whole code).
Sorry for uploading code late.
Thank you in advance.
Here is scree shot:Click here
class _CameraScreenState extends State<CameraScreen> {
CameraController? _cameraController;
// bool _isvideoRecording = false;
Future<void>? cameravalue;
bool isrecording = false;
String videopath = '';
XFile? videorecording;
bool flash = false;
bool isCameraFront = false;
double trasform = 0;
#override
void initState() {
// TODO: implement initState
super.initState();
_cameraController = CameraController(camera![0], ResolutionPreset.high);
cameravalue = _cameraController!.initialize();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
_cameraController!.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
FutureBuilder(
future: cameravalue,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(_cameraController!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
Positioned(
bottom: 0,
child: Container(
padding: const EdgeInsets.only(
top: 5,
bottom: 5,
),
color: Colors.black,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
setState(() {
flash = !flash;
});
flash
? _cameraController!.setFlashMode(FlashMode.torch)
: _cameraController!.setFlashMode(FlashMode.off);
},
icon: Icon(
flash ? Icons.flash_on : Icons.flash_off,
color: Colors.white,
size: 28,
),
),
GestureDetector(
onLongPress: () async {
await _cameraController!.startVideoRecording();
setState(() {
isrecording = true;
});
},
onLongPressUp: () async {
XFile videopath =
await _cameraController!.stopVideoRecording();
setState(() {
isrecording = false;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => VideoView(
path: videopath.path,
)));
},
onTap: () {
if (!isrecording) {
takePhoto(context);
}
},
child: isrecording
? const Icon(
Icons.radio_button_on,
color: Colors.red,
size: 80,
)
: const Icon(
Icons.panorama_fish_eye,
color: Colors.white,
size: 70,
),
),
IconButton(
onPressed: () async {
setState(() {
isCameraFront = !isCameraFront;
trasform = trasform + pi;
});
int cameraPos = isCameraFront ? 0 : 1;
_cameraController = CameraController(
camera![cameraPos], ResolutionPreset.high);
cameravalue = _cameraController!.initialize();
},
icon: Transform.rotate(
angle: trasform,
child: const Icon(
Icons.flip_camera_ios,
color: Colors.white,
size: 30,
),
),
),
],
),
const SizedBox(
height: 4,
),
const Text(
'Hold for Video, tap for photo',
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
],
),
),
)
],
);
}
void takePhoto(BuildContext context) async {
final path =
join((await getTemporaryDirectory()).path, "${DateTime.now()}.png");
XFile picture = await _cameraController!.takePicture();
picture.saveTo(path);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CameraView(
path: path,
),
),
);
}
You can implement this features by doing the following simple steps. There will be another possible solutions.
First of all, create a new route ( new widget ) that contains camera view in fullscreen.
When you tap the camera tab, go to that route with fade-in or scale animation with faster duration that will make UI more realistic.

tcard is not resetting, getting null in tcardController.state Flutter

I am tcard pkg in my project. i want reset the card in every 5 seconds automatically the length of the list is changing but the UI is not resetting, getting null in tcardController.state
Is there any other pkg, so we can achieve same functionality of this tcard pkg.
Refer to below code:-
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:tcard/tcard.dart';
List<Color> colors = [
Colors.blue,
Colors.yellow,
Colors.red,
Colors.orange,
Colors.pink,
Colors.amber,
Colors.cyan,
Colors.purple,
Colors.brown,
Colors.teal,
];
class TCardPage extends StatefulWidget {
const TCardPage({Key? key}) : super(key: key);
#override
_TCardPageState createState() => _TCardPageState();
}
class _TCardPageState extends State<TCardPage> {
final TCardController _controller = TCardController();
int _index = 0;
Timer? timer;
bool changeColor = false;
#override
void initState() {
super.initState();
timer = Timer.periodic(
const Duration(seconds: 5), (Timer t) => sequenceApiCall());
print(_controller.state.toString());
// _controller.reset;
}
void sequenceApiCall() {
changeColor = !changeColor;
Future.delayed(const Duration(seconds: 10), () {
print('sequenceApiCall');
if (changeColor) {
print('changeColor');
colors.removeAt(0);
print('color count = ${colors.length}'); //9
} else {
colors.insert(0, Colors.black);
print('black');
print('color count = ${colors.length}'); //10
}
print(_controller.state.toString());
_controller.reset();
setState(() {});
});
}
#override
void dispose() {
super.dispose();
timer?.cancel();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
const SizedBox(height: 200),
TCard(
cards: cardsColor,
controller: _controller,
onForward: (index, info) {
_index = index;
print(info.direction);
setState(() {});
},
onBack: (index, info) {
_index = index;
setState(() {});
},
onEnd: () {
print('end');
},
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
OutlineButton(
onPressed: () {
_controller.back();
},
child: const Text('Back'),
),
OutlineButton(
onPressed: () {
_controller.forward();
},
child: const Text('Forward'),
),
OutlineButton(
onPressed: () {
_controller.reset();
},
child: const Text('Reset'),
),
],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Text(_index.toString()),
),
);
}
List<Widget> cardsColor = List.generate(
colors.length,
(int index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: colors[index],
),
child: Text(
'${index + 1}',
style: const TextStyle(fontSize: 100.0, color: Colors.white),
),
);
},
);
}
Thanks in advance.!!!

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.)

Flutter: Multiple Instances of Shared Preferences?

I am having a problem with SharedPreferences and multiple Modules I am generating on a Form using ListView.builder
The form is basically asking for some parents details and their childs details - by default the form assumes the parent has one child, but more can be added by clicking a button. The ChildModule has the ability to "close" but when "re-opened" the data doesn't persist, hence using SharedPreferences, it works fine with one Child, but once a second child is added it seems to be creating multiple Instances of SharedPreferences.
I have cut out everything to show what I am trying to achieve. NOTE this is being used as a Web App if it matters.
Oh and ChildModule needs to have its own state because it has a widget which requires it (not shown)
ENQUIRY FORM
final GlobalKey<FormBuilderState> _enquiryFormKey = GlobalKey<FormBuilderState>();
class EnquiryForm extends StatefulWidget {
static List<int> numberOfChildren = [1];
#override
_EnquiryFormState createState() => _EnquiryFormState();
}
class _EnquiryFormState extends State<EnquiryForm> {
int defaultNumberOfChildren = 1;
removeModule(){
setState(() {});
}
#override
Widget build(BuildContext context) {
return FormBuilder(
key: _enquiryFormKey,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: CustomTextField(
label: 'Parent First Name',
isRequired: true,
),
),
),
//ChildModuleList
ListView.builder(
shrinkWrap: true,
itemCount: EnquiryForm.numberOfChildren.length,
itemBuilder: (context,int index){
return ChildModule(EnquiryForm.numberOfChildren[index], removeModule);
}
),
SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
ThemedButton(
onPressed: (){
setState(() {
defaultNumberOfChildren++;
EnquiryForm.numberOfChildren.add(defaultNumberOfChildren);
});
},
child: Text(
'Add Additional Child',
style: TextStyle(color: Colors.white),)
),
SizedBox(width: 10,),
ThemedButton(
onPressed: (){},
child: Text('Enquire Now', style: TextStyle(color: Colors.white),))
],
)
],
));
}
}
CHILD MODULE
class ChildModule extends StatefulWidget {
final int number;
final Function() callback;
ChildModule(this.number,this.callback);
#override
_ChildModule createState() => _ChildModule();
}
class _ChildModule extends State<ChildModule> {
SharedPreferences childModuleData;
String firstName;
bool loading = true;
bool isOpen;
#override
void initState() {
print('this module number is ${widget.number}');
_spInstance();
isOpen = true;
super.initState();
}
Future<void> _spInstance() async {
if(childModuleData == null && widget.number == 1) {
childModuleData = await SharedPreferences.getInstance();
print('got instance');
} else {
print('broken');
print(childModuleData);
};
String _testValue = childModuleData.getString('Child First Name');
if(_testValue == null){
childModuleData.setString('Child First Name', '');
loading = false;
} else {
childModuleData.clear();
_spInstance();
}
}
#override
Widget build(BuildContext context) {
return loading ? Loading() : Column(
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
if (isOpen == false) {
isOpen = true;
} else {
isOpen = false;
}
});
},
child: Container(
height: 40,
padding: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4.0)),
color: Colors.blue[50],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Child Details',
style: TextStyle(color: Colors.blue[300], fontSize: 16),
),
Row(
children: <Widget>[
isOpen
? Icon(
Icons.arrow_drop_down,
size: 30,
)
: Transform.rotate(
angle: math.pi / 2,
child: Icon(
Icons.arrow_drop_down,
size: 30,
),
),
widget.number > 1
? IconButton(icon: Icon(Icons.clear), onPressed: () async {
await FormFunctions().removeModule(widget.number, EnquiryForm.numberOfChildren);
widget.callback();
})
: Container(),
],
),
],
),
),
),
AnimatedContainer(
duration: Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
padding: EdgeInsets.fromLTRB(10, 5, 10, 5),
height: isOpen ? null : 0,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
CustomTextField(
label: fieldFirstName,
isRequired: true,
initalValue: childModuleData.getString(fieldFirstName) ?? '',
onChanged: (value){
childModuleData.setString(fieldFirstName, value);
},
),
],
),
],
),
),
],
);
}
}
CONSOLE ERROR AFTER SECOND MODULE IS CREATED
Launching lib/main.dart on Chrome in debug mode...
Syncing files to device Chrome...
Debug service listening on ws://127.0.0.1:58490/EkMAy9CGY74=
Debug service listening on ws://127.0.0.1:58490/EkMAy9CGY74=
this module number is 1
got instance
Instance of 'SharedPreferences'
null
this module number is 2 //Number 2 Module is created
broken
null
null
TypeError: Cannot read property 'getString' of null
at child_module._ChildModule.new._spInstance$ (http://localhost:58433/packages/webenrol/shared/widgets/day_button.dart.lib.js:3704:47)
at _spInstance$.next (<anonymous>)
at runBody (http://localhost:58433/dart_sdk.js:43121:34)
at Object._async [as async] (http://localhost:58433/dart_sdk.js:43149:7)
at child_module._ChildModule.new.[_spInstance] (http://localhost:58433/packages/webenrol/shared/widgets/day_button.dart.lib.js:3694:20)
at child_module._ChildModule.new.initState (http://localhost:58433/packages/webenrol/shared/widgets/day_button.dart.lib.js:3689:24)
at framework.StatefulElement.new.[_firstBuild] (http://localhost:58433/packages/flutter/src/widgets/widget_span.dart.lib.js:41219:58)
at framework.StatefulElement.new.mount (http://localhost:58433/packages/flutter/src/widgets/widget_span.dart.lib.js:12605:24)
at framework.SingleChildRenderObjectElement.new.inflateWidget (http://localhost:58433/packages/flutter/src/widgets/widget_span.dart.lib.js:11420:16)
in your case the concern is at the level of recording data in shared preferences. one solution would be to add the widget number in the key to save like this (await SharedPreferences.getInstance()).setString("Your Key${widgetNumber}");
and edit your function _spInstance
class ChildModule extends StatefulWidget {
final int number;
final Function() callback;
ChildModule(this.number,this.callback);
#override
_ChildModule createState() => _ChildModule();
}
class _ChildModule extends State<ChildModule> {
SharedPreferences childModuleData;
...
Future<void> _spInstance() async {
if(childModuleData == null) {
childModuleData = await SharedPreferences.getInstance();
print('got instance');
}
String _testValue = childModuleData.getString('Child First Name${widget.number}');
//NB: do not clear data on chlidModuleData any more.
...
}
...
}

Want to Stop Audio of previous tap When I tap on another Image in flutter on TAP?

Want to Stop Audio of the previous tap When I tap on another Image in a flutter on TAP?
Please have a look at the code below and let me know how to stop music when we tap on another bird.
import 'package:flutter/material.dart';
import 'package:audiofileplayer/audiofileplayer.dart';
child: InkWell(
onTap: () {
Audio.load('assets/audios/' + audio)
..play()
..dispose();
},
you can use official example
https://github.com/google/flutter.plugins/tree/master/packages/audiofileplayer/example/lib
code snippet
static Widget _transportButtonWithTitle(
String title, bool isPlaying, VoidCallback onTap) =>
Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: RaisedButton(
onPressed: onTap,
child: isPlaying
? Image.asset("assets/icons/ic_pause_black_48dp.png")
: Image.asset(
"assets/icons/ic_play_arrow_black_48dp.png")),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Text(title)),
],
));
_transportButtonWithTitle('play from start', false, () {
_audio.play();
setState(() => _audioPlaying = true);
}),
_transportButtonWithTitle(
_audioPlaying ? 'pause' : 'resume', _audioPlaying, () {
_audioPlaying ? _audio.pause() : _audio.resume();
setState(() => _audioPlaying = !_audioPlaying);
}),
working demo
full example code
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:audiofileplayer/audiofileplayer.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// Preloaded audio data for the first card.
Audio _audio;
bool _audioPlaying = false;
double _audioDurationSeconds;
double _audioPositionSeconds;
double _audioVolume = 1.0;
double _seekSliderValue = 0.0; // Normalized 0.0 - 1.0.
// On-the-fly audio data for the second card.
int _spawnedAudioCount = 0;
ByteData _audioByteData;
// Remote url audio data for the third card.
Audio _remoteAudio;
bool _remoteAudioPlaying = false;
bool _remoteAudioLoading = false;
String _remoteErrorMessage;
// The iOS audio category dropdown item in the fourth card.
IosAudioCategory _iosAudioCategory = IosAudioCategory.playback;
#override
void initState() {
super.initState();
_audio = Audio.load('assets/audio/printermanual.m4a',
onComplete: () => setState(() => _audioPlaying = false),
onDuration: (double durationSeconds) =>
setState(() => _audioDurationSeconds = durationSeconds),
onPosition: (double positionSeconds) => setState(() {
_audioPositionSeconds = positionSeconds;
_seekSliderValue = _audioPositionSeconds / _audioDurationSeconds;
}));
_loadAudioByteData();
_loadRemoteAudio();
}
#override
void dispose() {
_audio.dispose();
if (_remoteAudio != null) {
_remoteAudio.dispose();
}
super.dispose();
}
static Widget _transportButtonWithTitle(
String title, bool isPlaying, VoidCallback onTap) =>
Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: RaisedButton(
onPressed: onTap,
child: isPlaying
? Image.asset("assets/icons/ic_pause_black_48dp.png")
: Image.asset(
"assets/icons/ic_play_arrow_black_48dp.png")),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Text(title)),
],
));
// convert double seconds to minutes:seconds
static String _stringForSeconds(double seconds) {
if (seconds == null) return null;
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
void _loadAudioByteData() async {
_audioByteData = await rootBundle.load('assets/audio/sinesweep.mp3');
setState(() {});
}
void _loadRemoteAudio() {
_remoteErrorMessage = null;
_remoteAudioLoading = true;
_remoteAudio = Audio.loadFromRemoteUrl('https://streams.kqed.org/kqedradio',
onDuration: (_) => setState(() => _remoteAudioLoading = false),
onError: (String message) => setState(() {
_remoteErrorMessage = message;
_remoteAudio.dispose();
_remoteAudio = null;
_remoteAudioPlaying = false;
_remoteAudioLoading = false;
}));
}
// Creates a card, out of column child widgets. Injects vertical padding
// around the column children.
Widget _cardWrapper(List<Widget> columnChildren) => Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: columnChildren
.map((Widget child) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: child,
))
.toList())));
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: const Color(0xFFCCCCCC),
appBar: AppBar(
title: const Text('Audio file player example'),
),
body: ListView(children: <Widget>[
// A card controlling a pre-loaded (on app start) audio object.
_cardWrapper(<Widget>[
const Text('Preloaded asset audio, with transport controls.'),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
_transportButtonWithTitle('play from start', false, () {
_audio.play();
setState(() => _audioPlaying = true);
}),
_transportButtonWithTitle(
_audioPlaying ? 'pause' : 'resume', _audioPlaying, () {
_audioPlaying ? _audio.pause() : _audio.resume();
setState(() => _audioPlaying = !_audioPlaying);
}),
_transportButtonWithTitle(
_audioPlaying ? 'pause' : 'play 0:05 to 0:10', _audioPlaying,
() async {
if (_audioPlaying) {
_audio.pause();
} else {
await _audio.seek(5);
_audio.resume(10);
}
setState(() => _audioPlaying = !_audioPlaying);
}),
]),
Row(
children: <Widget>[
Text(_stringForSeconds(_audioPositionSeconds) ?? ''),
Expanded(child: Container()),
Text(_stringForSeconds(_audioDurationSeconds) ?? ''),
],
),
Slider(
value: _seekSliderValue,
onChanged: (double val) {
setState(() => _seekSliderValue = val);
final double positionSeconds = val * _audioDurationSeconds;
_audio.seek(positionSeconds);
}),
const Text('drag to seek'),
Slider(
value: _audioVolume,
onChanged: (double val) {
setState(() => _audioVolume = val);
_audio.setVolume(_audioVolume);
}),
const Text('volume (linear amplitude)'),
]),
_cardWrapper(<Widget>[
const Text(
'Spawn overlapping one-shot audio playback.\n(tap multiple times)',
textAlign: TextAlign.center,
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
_transportButtonWithTitle('audio from assets', false, () {
Audio.load('assets/audio/sinesweep.mp3',
onComplete: () => setState(() => --_spawnedAudioCount))
..play()
..dispose();
setState(() => ++_spawnedAudioCount);
}),
_transportButtonWithTitle(
'audio from ByteData',
false,
_audioByteData == null
? null
: () {
Audio.loadFromByteData(_audioByteData,
onComplete: () =>
setState(() => --_spawnedAudioCount))
..play()
..dispose();
setState(() => ++_spawnedAudioCount);
})
]),
Text('Spawned audio count: $_spawnedAudioCount'),
]),
_cardWrapper(<Widget>[
const Text('Play remote stream'),
_transportButtonWithTitle(
'resume/pause NPR (KQED) live stream',
_remoteAudioPlaying,
_remoteAudioLoading
? null
: () {
if (!_remoteAudioPlaying) {
// If remote audio loading previously failed with an
// error, attempt to reload.
if (_remoteAudio == null) _loadRemoteAudio();
// Note call to resume(), not play(). play() attempts to
// seek to the start of a file, which, for streams, will
// fail with an error on Android platforms, so streams
// should use resume() to begin playback.
_remoteAudio.resume();
setState(() => _remoteAudioPlaying = true);
} else {
_remoteAudio.pause();
setState(() => _remoteAudioPlaying = false);
}
}),
_remoteErrorMessage != null
? Text(_remoteErrorMessage,
style: const TextStyle(color: const Color(0xFFFF0000)))
: Text(_remoteAudioLoading ? 'loading...' : 'loaded')
]),
_cardWrapper(<Widget>[
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
const Text('Enable background playback:'),
Checkbox(
value: Audio.shouldPlayWhileAppPaused,
onChanged: (bool isOn) =>
setState(() => Audio.shouldPlayWhileAppPaused = isOn))
]),
const Text('(iOS only) iOS audio category:'),
DropdownButton<IosAudioCategory>(
value: _iosAudioCategory,
onChanged: (IosAudioCategory newValue) {
setState(() {
_iosAudioCategory = newValue;
Audio.setIosAudioCategory(_iosAudioCategory);
});
},
items: IosAudioCategory.values.map((IosAudioCategory category) {
return DropdownMenuItem<IosAudioCategory>(
value: category,
child: Text(category.toString()),
);
}).toList(),
)
]),
]),
));
}
}