Flutter - Want to show camera Tabbarview in full screen - flutter

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.

Related

Why does Flutter Camera Plugin return video content type as Application/octet-stream

hello guys!
i am using flutter story view package to implement story functionality in my app and i am using camera plugin to record video and upload, however the content type of the uploaded video is application/octet-stream when i receive it on my server and only when it is sent from ios. for android everything is fine and i get the content type as video/mp4.
can you guys please help me.
i have not done anything fancy i have just used the basic functionality of start recording and stop recording of the camra plugin.
// ignore_for_file: use_build_context_synchronously
List<CameraDescription>? cameras;
class CameraScreen extends StatefulWidget {
const CameraScreen({Key? key}) : super(key: key);
#override
State<CameraScreen> createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
CameraController? _cameraController;
Future<void>? cameraValue;
bool flash = false;
bool iscamerafront = true;
static const _maxSeconds = 30;
ValueNotifier<bool> visible = ValueNotifier(false);
ValueNotifier<bool> isRecoring = ValueNotifier(false);
TimerProvider? _timerProvider;
#override
void initState() {
super.initState();
_timerProvider = Provider.of(context, listen: false);
_cameraController = CameraController(cameras![0], ResolutionPreset.veryHigh,
imageFormatGroup: ImageFormatGroup.bgra8888);
cameraValue = _cameraController?.initialize();
_cameraController?.prepareForVideoRecording();
lockCamera();
}
lockCamera() async {
await _cameraController!.lockCaptureOrientation();
}
#override
void dispose() {
super.dispose();
visible.dispose();
isRecoring.dispose();
_cameraController?.dispose();
}
#override
Widget build(BuildContext context) {
print("camera_screen");
final size = MediaQuery.of(context).size;
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
centerTitle: true,
title: ValueListenableBuilder<bool>(
valueListenable: visible,
builder: (context, value, child) {
return Visibility(
visible: value,
child: Container(
padding: const EdgeInsets.all(10),
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: Center(
child: Consumer<TimerProvider>(
builder: (context, value, child) {
if (value.seconds == 0) {
if (_cameraController!.value.isRecordingVideo) {
stopRecording();
}
}
return Text(
value.seconds.toString(),
style: CustomStyles.fixAppBarTextStyle
.copyWith(color: Colors.white),
);
}),
),
));
}),
backgroundColor: Colors.transparent,
leadingWidth: 100,
leading: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Cancel",
style: CustomStyles.fixMediumBodyTextStyle
.copyWith(color: Colors.white),
),
)),
),
),
body: SafeArea(
top: false,
child: Stack(
children: [
FutureBuilder(
future: cameraValue,
builder: (context, snapshot) {
_cameraController?.lockCaptureOrientation();
if (snapshot.connectionState == ConnectionState.done) {
return Transform.scale(
scale: size.aspectRatio *
_cameraController!.value.aspectRatio <
1
? 1 /
(size.aspectRatio *
_cameraController!.value.aspectRatio)
: size.aspectRatio *
_cameraController!.value.aspectRatio,
child: Center(child: CameraPreview(_cameraController!)),
);
} else {
return const Center(
child: CircularProgressIndicator.adaptive(),
);
}
}),
Positioned(
bottom: 0.0,
child: Container(
padding: const EdgeInsets.only(top: 5, bottom: 5),
width: Dimensions.screenWidth,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: Center(
child: IconButton(
icon: Icon(
flash ? Icons.flash_on : Icons.flash_off,
color: Colors.white,
size: 28,
),
onPressed: () {
setState(() {
flash = !flash;
});
flash
? _cameraController!
.setFlashMode(FlashMode.torch)
: _cameraController!
.setFlashMode(FlashMode.off);
}),
),
),
ValueListenableBuilder<bool>(
valueListenable: isRecoring,
builder: (context, value, child) {
return GestureDetector(
onLongPress: () {
startRecording();
},
onLongPressUp: () {
stopRecording();
},
onTap: () {
if (value == false) {
takePhoto(context);
}
},
child: value == true
? const CircleAvatar(
radius: 50,
backgroundColor: Colors.white30,
child: CircleAvatar(
radius: 35,
backgroundColor: Colors.red,
),
)
: const CircleAvatar(
radius: 35,
backgroundColor: Colors.white30,
child: CircleAvatar(
radius: 25,
backgroundColor: Colors.white,
),
));
}),
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: IconButton(
icon: const Icon(
Icons.flip_camera_ios,
color: Colors.white,
size: 28,
),
onPressed: () async {
setState(() {
iscamerafront = !iscamerafront;
});
int cameraPos = iscamerafront ? 0 : 1;
_cameraController = CameraController(
cameras![cameraPos], ResolutionPreset.high);
cameraValue = _cameraController?.initialize();
}),
),
],
),
addVerticalSpace(6),
const Text(
"Hold for Video, tap for photo",
style: TextStyle(
color: Colors.white,
),
textAlign: TextAlign.center,
),
addVerticalSpace(5),
],
),
),
),
],
),
),
);
}
void startRecording() async {
visible.value = true;
_timerProvider?.startCountDown(_maxSeconds);
isRecoring.value = true;
await _cameraController?.startVideoRecording();
}
void stopRecording() async {
XFile videopath = await _cameraController!.stopVideoRecording();
_timerProvider?.stopTimer();
isRecoring.value = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => CameraResult(
image: File(videopath.path),
isImage: false,
))).then((value) {
visible.value = false;
});
}
void takePhoto(BuildContext context) async {
XFile file = await _cameraController!.takePicture();
SystemSound.play(SystemSoundType.click);
final img.Image? capturedImage =
img.decodeImage(await File(file.path).readAsBytes());
final img.Image orientedImage = img.flipHorizontal(capturedImage!);
File finalImage =
await File(file.path).writeAsBytes(img.encodeJpg(orientedImage));
Navigator.push(
context,
CupertinoPageRoute(
builder: (builder) => CameraResult(
image: finalImage,
isImage: true,
))).then((value) {
visible.value = false;
});
}
}
the upload function is as bellow
{File? shouts,
File? thumbnail,
int? duration,
bool? isImage,
required String userId,
required OnUploadProgressCallback onUploadProgress}) async {
assert(shouts != null);
try {
var url = Constants.POSTSHOUTURL;
final httpClient = FileService.getHttpClient();
final request = await httpClient.postUrl(Uri.parse(url));
int byteCount = 0;
var multipart;
if (isImage == false) {
multipart = await http.MultipartFile.fromPath(
'story-media', shouts!.path,
contentType: MediaType("video", "mp4"));
} else {
multipart = await http.MultipartFile.fromPath(
'story-media',
shouts!.path,
);
}
var multipart2 =
await http.MultipartFile.fromPath('thumbnail', thumbnail!.path);
var requestMultipart = http.MultipartRequest('POST', Uri.parse(url));
requestMultipart.fields["userid"] = userId.toString();
requestMultipart.fields["duration"] = duration.toString();
requestMultipart.headers['Content-type'] = 'multipart/form-data';
requestMultipart.files.add(multipart);
requestMultipart.files.add(multipart2);
var msStream = requestMultipart.finalize();
var totalByteLength = requestMultipart.contentLength;
request.contentLength = totalByteLength;
request.headers.add(HttpHeaders.authorizationHeader,
"Bearer ${Hive.box<UserData>(Constants.userDb).get(Hive.box<UserData>(Constants.userDb).keyAt(0))!.token}");
request.headers.set(HttpHeaders.contentTypeHeader,
requestMultipart.headers[HttpHeaders.contentTypeHeader]!);
Stream<List<int>> streamUpload = msStream.transform(
StreamTransformer.fromHandlers(
handleData: (data, sink) {
sink.add(data);
byteCount += data.length;
if (onUploadProgress != null) {
onUploadProgress(byteCount, totalByteLength);
// CALL STATUS CALLBACK;
}
},
handleError: (error, stack, sink) {
throw error;
},
handleDone: (sink) {
sink.close();
// UPLOAD DONE;
},
),
);
await request.addStream(streamUpload);
final httpResponse = await request.close();
var statusCode = httpResponse.statusCode;
if (statusCode ~/ 100 == 2) {
onUploadProgress(0, 0);
return await FileService.readResponseAsString(httpResponse);
}
return "";
} on SocketException {
throw FetchDataException("No internet to upload Shout!");
}
}```
i have used necessary info.plist setting also

Cannot add new events after calling close

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);
},
),
],
),
),
),
],
),
);
}
}

Stop Play Music after leaving the page in Flutter

In my Flutter app I'm using audioplayers: ^0.17.0 to play music. When I play music and want to leave the page I want to stop playing music. For that purpose I'm using dispose(). And it looks like it is working. The problem that I'm getting is that I've got an error in the console. I think it is because I'm using async, but I'm not sure how to fix it. Or maybe there is a different solution that I can use for this purpose. Any suggestions?
Here are my code and error:
class _MyAppState extends State<HomeApp> {
var currentPageValue = 0;
double currentPage = 0;
PageController pageController = PageController(initialPage: 0);
AudioPlayer audioPlayer = new AudioPlayer();
#override
void initState() {
super.initState();
pageController.addListener(() {
if (pageController.page == pageController.page.roundToDouble()) {
setState(() {
currentPage = pageController.page;
});
}
});
}
bool playing = false;
#override
Future<void> dispose() async {
await audioPlayer.stop();
super.dispose();
}
#override
Widget build(BuildContext context) {
final itemsData = Provider.of<ItemsList>(context);
_nextCardHandler() async {
if (pageController.page.toInt() < itemsData.items.length - 1) {
pageController.animateToPage(pageController.page.toInt() + 1,
duration: Duration(milliseconds: 400), curve: Curves.easeIn);
} else {
pageController.animateToPage(0,
duration: Duration(milliseconds: 400), curve: Curves.easeIn);
}
if (playing) {
var res = await audioPlayer.stop();
if (res == 1) {
setState(() {
playing = false;
});
}
}
}
_prevCardHandler() async {
if (pageController.page.toInt() == 0) {
pageController.animateToPage(itemsData.items.length - 1,
duration: Duration(milliseconds: 400), curve: Curves.easeIn);
} else {
pageController.animateToPage(pageController.page.toInt() - 1,
duration: Duration(milliseconds: 400), curve: Curves.easeIn);
}
if (playing) {
var res = await audioPlayer.stop();
if (res == 1) {
setState(() {
playing = false;
});
}
print('stop here');
}
}
void _getAudio(audio) async {
if (playing) {
var res = await audioPlayer.pause();
if (res == 1) {
setState(() {
playing = false;
});
}
print('pause uuuu');
} else {
var res = await audioPlayer.play(audio, isLocal: true);
await audioPlayer.setReleaseMode(ReleaseMode.LOOP);
if (res == 1) {
print('play 999');
setState(() {
playing = true;
});
}
}
}
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
title: Text(
itemsData.items[currentPage.round()].title,
),
iconTheme: IconThemeData(color: Theme.of(context).accentColor),
),
drawer: AppDrawer(),
body: PageView.builder(
controller: pageController,
itemCount: itemsData.items.length,
itemBuilder: (ctx, int itemIndex) {
return Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: NetworkImage(itemsData.items[itemIndex].image),
fit: BoxFit.cover,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
child: IconButton(
icon: Icon(Icons.favorite_border),
color: Theme.of(context).accentColor,
iconSize: 40,
onPressed: () {},
),
),
],
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(20),
),
),
Container(
color: Colors.black54,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(
child: IconButton(
icon: Icon(Icons.arrow_left_sharp),
color: Theme.of(context).accentColor,
iconSize: 80,
onPressed: _prevCardHandler,
),
),
SizedBox(
child: IconButton(
icon: Icon(playing == false
? Icons.play_circle_fill_outlined
: Icons.pause_circle_filled_outlined),
color: Theme.of(context).accentColor,
iconSize: 80,
onPressed: () =>
_getAudio(itemsData.items[itemIndex].audio),
),
),
SizedBox(
child: IconButton(
icon: Icon(Icons.arrow_right_sharp),
color: Theme.of(context).accentColor,
iconSize: 80,
onPressed: _nextCardHandler,
),
),
],
),
),
],
),
);
},
),
);
}
}
enter image description here
According to this link.
You can run asynchronous events after calling super.dispose() first. So in your case
#override
Future<void> dispose() async {
super.dispose(); //change here
await audioPlayer.stop();
}

Updating state in flutter

I have a dialog box that sorts a list on my home screen. When I press update the list will sort properly.
What I need to do is run setState for the entire home screen so that the other widgets will reflect the change for the sort order.
Specifically, I have two widgets that I need to rebuild when the update button is pressed.
I have tried to pass a function from the button to the home screen in the constructor and created a function to setState but that didn't work.
Please let me know if I'm being clear enough. Thanks
Home:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 30,
),
HpHeader(),
SizedBox(height: 30),
QuoteBox(),
SizedBox(height: 30),
//parent
MainDebtDebt(),
SizedBox(height: 30),
AdCard(),
//child
AllDebtsCard(),
All debts card (where the button is):
import 'package:debt_zero_2/classes/min_debt_class.dart';
//import 'package:debt_zero_2/classes/icon_class.dart';
import 'package:debt_zero_2/widgets/provider_widget.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AllDebtsCard extends StatefulWidget {
#override
_AllDebtsCardState createState() => _AllDebtsCardState();
}
class _AllDebtsCardState extends State<AllDebtsCard> {
int debtValue = 1;
int group;
void setValues() async {
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
final uid = await Provider.of(context).auth.getUidPref();
final db = Firestore.instance;
setState(() {
sharedPrefs.setInt('sortBySnowOrAva', debtValue);
SetMainDebt().setMainDebt();
});
db
.collection('userPreferences')
.document(uid)
.updateData({'sortBySnowOrAva': debtValue});
}
getValues() async {
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
int intValue = sharedPrefs.getInt('sortBySnowOrAva');
return intValue;
}
restore() async {
final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
setState(() {
group = (sharedPrefs.getInt('sortBySnowOrAva') ?? false);
});
}
final dbPath = Firestore.instance.collection('userDebts');
Stream<QuerySnapshot> dbStream(BuildContext context) async* {
final uid = await Provider.of(context).auth.getUidPref();
final intValues = await getValues();
yield* intValues == 1
? dbPath
.document(uid)
.collection('debts')
.orderBy('balance')
.snapshots()
: dbPath
.document(uid)
.collection('debts')
.orderBy('interest', descending: true)
.snapshots();
}
#override
void initState() {
super.initState();
restore();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RaisedButton(
child: Text('SORT'),
onPressed: () {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text('Sort Debts By:'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
value: 1,
secondary: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Debt Snowball:'),
content: Text(
'This is an explanation of debt snowball'),
actions: <Widget>[
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.pop(context);
},
)
],
);
});
},
icon: Icon(Icons.help_outline),
),
title: Text('Snowball'),
groupValue: group,
onChanged: (T) {
setState(() {
group = T;
debtValue = 1;
});
},
),
RadioListTile(
value: 2,
title: Text('Avalanche'),
secondary: IconButton(
onPressed: () {},
icon: Icon(Icons.help_outline),
),
groupValue: group,
onChanged: (T) {
setState(() {
group = T;
debtValue = 2;
});
},
)
],
),
actions: <Widget>[
FlatButton(
onPressed: () async {
setState(() {
setValues();
});
Navigator.pop(context);
},
child: Text('UPDATE'),
),
FlatButton(
child: Text(
'CANCEL',
style: TextStyle(color: Colors.red),
),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
}).then((value) => setState(() {}));
},
),
StreamBuilder<QuerySnapshot>(
stream: dbStream(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
final debts = snapshot.data.documents;
List<Widget> debtWidgets = [];
for (var debt in debts) {
final debtName = debt.data['name'];
final debtType = debt.data['type'];
final debtBalance = debt.data['balance'];
final debtDue = debt.data['due'].toDate();
final debtWidget = Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('$debtType'),
Text('$debtName'),
Text(DateFormat.MMMd().format(debtDue)),
Text('$debtBalance'),
FlatButton(
child: Text(
'DELETE',
style: TextStyle(color: Colors.red),
),
onPressed: () {
//When I delete a debt I need to update the 'debtMinimum' field in prefs and firestore
},
)
],
),
);
debtWidgets.add(debtWidget);
}
return Column(children: debtWidgets);
}
return Container();
}),
],
);
}
}
One of the two widgets that I need updated:
import 'dart:async';
//import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:debt_zero_2/classes/icon_class.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class MainDebtDebt extends StatefulWidget {
#override
_MainDebtDebtState createState() => _MainDebtDebtState();
}
class _MainDebtDebtState extends State<MainDebtDebt> {
bool thenum = true;
static DebtModel debtIcons = DebtModel();
bool getGoalType = true;
double balance = 0.0;
String name = '';
String type = '';
int numOfDebts = 0;
double safetyBalance = 0.0;
double mainSnowballOpeningBalance = 0.0;
Future<void> getGoalTypeFlag() async {
final SharedPreferences preferences = await SharedPreferences.getInstance();
final sortBy = preferences.getInt('sortBySnowOrAva');
setState(() {
if (sortBy == 1){
balance = preferences.getDouble('mainDebtFieldSnowball');
type = preferences.getString('mainDebtFieldSnowballType');
name = preferences.getString('mainDebtFieldSnowballName');}
if (sortBy == 2){
balance = preferences.getDouble('mainAvalancheBalance');
type = preferences.getString('mainAvalancheBalanceType');
name = preferences.getString('mainAvalancheBalanceName');
}
mainSnowballOpeningBalance = preferences.getDouble('openingBalance');
safetyBalance = preferences.getDouble('safetyBalance');
getGoalType = preferences.getBool('mainGoalIsDebts');
numOfDebts = preferences.getInt('numberOfDebts');
});
}
#override
void initState() {
super.initState();
getGoalTypeFlag();
}
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: BoxDecoration(border: Border.all()),
child: Column(
children: <Widget>[
Text(getGoalType
? 'I\'m Knocking Out This Payment:'
: 'I\'m Building My Safety Fund'),
SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
getGoalType
? debtIcons.getDebtIcon(type)
: '🧯',
style: TextStyle(fontSize: 30),
),
SizedBox(
width: 20,
),
Text(getGoalType ? name : 'Safety Fund'),
],
),
SizedBox(
height: 15,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(getGoalType ? 'Remaining Balance:' : 'Saved:'),
SizedBox(
width: 15,
),
Text(getGoalType
? '\$' + balance.toStringAsFixed(0)
: safetyBalance.toStringAsFixed(0))
],
),
SizedBox(
height: 15,
),
Column(
children: <Widget>[
Text('Current Progress:'),
SizedBox(
height: 10,
),
Container(
decoration: BoxDecoration(border: Border.all()),
height: 22,
width: 202,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Flexible(
child: FractionallySizedBox(
widthFactor: .75,
//fix this
// 1 - mainSnowballBalance / mainSnowballOpeningBalance,
child: Container(
color: Colors.green,
),
),
)
],
),
),
SizedBox(
height: 15,
),
RaisedButton(
child: Text(getGoalType ? 'MAKE A PAYMENT' : 'MAKE A DEPOSIT'),
onPressed: () {
Navigator.of(context).pushNamed('/makePayment');
},
),
SizedBox(
height: 30,
),
RaisedButton(
child: Text('GET DATA'),
onPressed: ()async {
SharedPreferences pref = await SharedPreferences.getInstance();
String thenum = pref.getString('mainAvalancheBalanceType1');
//pref.setBool('isInOb', null);
print(thenum);
},
),
],
)
],
),
);
}
}

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