Timer starts over when next_question selected - flutter

I'm making a quiz app using Flutter.
I have created a timer of 30 seconds which should start when a new question comes up.
It works fine and it restarts after the next question comes.
But when I click on "Next Question" without choosing an option, it restarts the timer.
I have created that button in such a way that you cannot go to the next question without choosing an option first.
Code:
#override
void initState() {
starttimer();
_questions = getData();
super.initState();
}
int index = 0;
int score = 0;
bool isPressed = false;
bool isAlreadySelected = false;
int timer = 30;
String showtimer = "30";
bool canceltimer = false;
void starttimer() async {
const onesec = Duration(seconds: 1);
Timer.periodic(onesec, (Timer t) {
setState(() {
if (timer < 1) {
t.cancel();
nextQuestion(index++);
} else if (canceltimer == true) {
t.cancel();
} else {
timer = timer - 1;
}
showtimer = timer.toString();
});
});
}
void nextQuestion(int questionLength) {
canceltimer = false;
timer = 30;
if (index == 19 || score == 12) {
showDialog(
context: context,
barrierDismissible: false,
builder: (ctx) => ResultBox(
result: score,
questionLength: questionLength,
));
} else {
if (isPressed) {
setState(() {
index++;
isPressed = false;
isAlreadySelected = false;
starttimer();
});
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('Please select any option'),
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.symmetric(vertical: 20.0),
));
starttimer();
}
}
}
void checkAnswerAndUpdate(bool value) {
if (isAlreadySelected) {
return;
} else {
if (value == true) {
score++;
setState(() {
isPressed = true;
isAlreadySelected = false;
canceltimer = true;
});
} else if (value == false) {
setState(() {
isPressed = true;
isAlreadySelected = false;
canceltimer = true;
});
}
}
}
void startOver() {
setState(() {
Text('You have already attempted the LL Test');
});
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _questions as Future<List<Question>>,
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Center(
child: Text('${snapshot.error}'),
);
} else if (snapshot.hasData) {
var extractedData = snapshot.data as List<Question>;
return Scaffold(
backgroundColor: background,
appBar: AppBar(
title: const Text('LL Test'),
backgroundColor: background,
shadowColor: Colors.transparent,
actions: [
Padding(
padding: const EdgeInsets.all(18.0),
child: Text(
'Score: $score',
style: TextStyle(fontSize: 18.0),
),
)
],
),
body: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
children: [
// Countdown(
// animation: StepTween(begin: limitTime, end: 0)
// .animate(_controller),
// ),
QuestionWidget(
question: extractedData[index].title,
indexAction: index,
totalQuestions: extractedData.length,
),
const Divider(
color: neutral,
),
const SizedBox(height: 25.0),
for (int i = 0;
i < extractedData[index].options.length;
i++)
GestureDetector(
onTap: () => checkAnswerAndUpdate(
extractedData[index].options.values.toList()[i]),
child: OptionCard(
option: extractedData[index].options.keys.toList()[i],
color: isPressed
? extractedData[index]
.options
.values
.toList()[i] ==
true
? correct
: incorrect
: neutral,
),
),
Expanded(
child: Container(
alignment: Alignment.topCenter,
child: Center(
child: Text(
showtimer,
style: TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.w700,
fontFamily: 'Times New Roman',
color: neutral,
),
),
),
),
),
],
),
),
floatingActionButton: GestureDetector(
onTap: () => nextQuestion(extractedData.length),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: NextButton(),
),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
);
}
} else {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 20.0),
Text(
'Please Wait While Questions Are Loading..',
style: TextStyle(
backgroundColor: Color.fromARGB(255, 4, 82, 6),
color: neutral),
),
],
),
);
}
return const Center(
child: Text('NoData'),
);
},
);
}
}
Is it something wrong in my code? What should I do so that timer doesn't startover?

did you call the starttimer() inside else condition?
try to remove it.
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('Please select any option'),
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.symmetric(vertical: 20.0),
));
// starttimer(); this call the function even its not selected yet
}

Related

Video player play image and video looping 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,
),
),
],
),
),
);
}
}

Another exception was thrown: type 'Null' is not a subtype of type 'String'

getting this error showing in red screen I update my packages
_TypeError Another exception was thrown: type 'Null' is not a subtype of type 'String' I tried to update the sdk and I'm not understanding where the problem is in this code. I added CircularProgressIndicator with condition statements
The relevant error-causing widget was:
quizpage
quizpage:file:///C:/Users/flutt/Downloads/quiz/flutter-quizstar-master/lib/quizpage.dart:57:18
class getjson extends StatefulWidget {
String langname;
getjson(this.langname);
#override
State<getjson> createState() => _getjsonState();
}
class _getjsonState extends State<getjson> {
late String assettoload;
// a function
setasset() {
if (widget.langname == "Science") {
assettoload = "assets/Science.json";
} else if (widget.langname == "Maths") {
assettoload = "assets/Maths.json";
} else if (widget.langname == "History") {
assettoload = "assets/History.json";
}
}
#override
Widget build(BuildContext context) {
setasset();
// and now we return the FutureBuilder to load and decode JSON
return FutureBuilder(
future:
DefaultAssetBundle.of(context).loadString(assettoload, cache: false),
builder: (context, snapshot) {
if (snapshot.hasData) {
List mydata = json.decode(snapshot.data.toString());
if (mydata == null) {
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
} else {
return quizpage(mydata: mydata);
}}
return CircularProgressIndicator();
},
);
}
}
class quizpage extends StatefulWidget {
final List mydata;
quizpage({required this.mydata});
#override
_quizpageState createState() => _quizpageState(mydata);
}
class _quizpageState extends State<quizpage> {
final List mydata;
_quizpageState(this.mydata);
Color colortoshow = Colors.indigoAccent;
Color right = Colors.green;
Color wrong = Colors.red;
int marks = 0;
int i = 1;
bool disableAnswer = false;
int j = 1;
int timer = 30;
String showtimer = "30";
var random_array;
Map<String, Color> btncolor = {
"a": Colors.indigoAccent,
"b": Colors.indigoAccent,
"c": Colors.indigoAccent,
"d": Colors.indigoAccent,
};
bool canceltimer = false;
genrandomarray(){
var distinctIds = [];
var rand = new Random();
for (int i = 0;; ) {
distinctIds.add(rand.nextInt(10));
random_array = distinctIds.toSet().toList();
if(random_array.length < 10){
continue;
}else{
break;
}
}
print(random_array);
}
#override
void initState() {
starttimer();
genrandomarray();
super.initState();
}
#override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
void starttimer() async {
const onesec = Duration(seconds: 1);
Timer.periodic(onesec, (Timer t) {
setState(() {
if (timer < 1) {
t.cancel();
nextquestion();
} else if (canceltimer == true) {
t.cancel();
} else {
timer = timer - 1;
}
showtimer = timer.toString();
});
});
}
void nextquestion() {
canceltimer = false;
timer = 30;
setState(() {
if (j < 10) {
i = random_array[j];
j++;
} else {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => resultpage(marks: marks),
));
}
btncolor["a"] = Colors.indigoAccent;
btncolor["b"] = Colors.indigoAccent;
btncolor["c"] = Colors.indigoAccent;
btncolor["d"] = Colors.indigoAccent;
disableAnswer = false;
});
starttimer();
}
void checkanswer(String k) {
// in the previous version this was
// mydata[2]["1"] == mydata[1]["1"][k]
// which i forgot to change
// so nake sure that this is now corrected
if (mydata[2][i.toString()] == mydata[1][i.toString()][k]) {
// just a print sattement to check the correct working
// debugPrint(mydata[2][i.toString()] + " is equal to " + mydata[1][i.toString()][k]);
marks = marks + 5;
// changing the color variable to be green
colortoshow = right;
} else {
// just a print sattement to check the correct working
// debugPrint(mydata[2]["1"] + " is equal to " + mydata[1]["1"][k]);
colortoshow = wrong;
}
setState(() {
btncolor[k] = colortoshow;
canceltimer = true;
disableAnswer = true;
});
// nextquestion();
// changed timer duration to 1 second
Timer(Duration(seconds: 2), nextquestion);
}
Widget choicebutton(String k) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: MaterialButton(
onPressed: () => checkanswer(k),
child: Text(
mydata[1][i.toString()][k],
style: TextStyle(
color: Colors.white,
fontFamily: "Alike",
fontSize: 16.0,
),
maxLines: 1,
),
color: btncolor[k],
splashColor: Colors.indigo[700],
highlightColor: Colors.indigo[700],
minWidth: 200.0,
height: 45.0,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),
);
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
return WillPopScope(
onWillPop: () async{
return await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
"Welcome",
),
content: Text("You Can't Go Back At This Stage."),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Ok',
),
)
],
));
},
child: Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
mydata[0][i.toString()] ,
style: TextStyle(
fontSize: 16.0,
fontFamily: "Quando",
),
),
),
),
Expanded(
flex: 6,
child: AbsorbPointer(
absorbing: disableAnswer,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
choicebutton('a'),
choicebutton('b'),
choicebutton('c'),
choicebutton('d'),
],
),
),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.topCenter,
child: Center(
child: Text(
showtimer,
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.w700,
fontFamily: 'Times New Roman',
),
),
),
),
),
],
),
),
);
}
}
In your code some place your are show some variable in Text widget that might be null so change them to these:
Text(
mydata[1][i.toString()][k] ?? "",//<--- add this
style: TextStyle(
color: Colors.white,
fontFamily: "Alike",
fontSize: 16.0,
),
maxLines: 1,
)
and
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
mydata[0][i.toString()] ?? "",//<--- add this
style: TextStyle(
fontSize: 16.0,
fontFamily: "Quando",
),
),
),
),
Can you check if your expression is evaluating to null in Text() Widget,
just put some constant strings in text widgets and try to run.
if expression is evaluating to null, use ?? operation to provide default string value to Text Widget.

Error during flutter null safety migration : type 'Null' is not a subtype of type 'Cpu'

Hello I upgrade my old working code to the last flutter null safety, but now I have this error when I press my RawMaterialButton.
type 'Null' is not a subtype of type 'Cpu'
new RawMaterialButton(
onPressed: () {
Navigator.pushNamed(
context,
'/match',
arguments: {
'mode': Mode.PVC,
'cpu': DumbCpu(
Random().nextBool() ? Player.RED : Player.YELLOW),
},
);
},
child: Text("Faible",style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: SizeConfig.safeBlockHorizontal! * 5),
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(40.0),
),
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: '',
home: HomePage(),
onGenerateRoute: (settings) {
final args = settings.arguments as Map<String, dynamic>;
if (settings.name == '/match') {
return MaterialPageRoute(
builder: (context) => MatchPage(
mode: args['mode'],
cpu: args['cpu'],
cpu2: args['cpu2'],
),
);
} else if (settings.name == '/cpu-level') {
return MaterialPageRoute(
builder: (context) => CpuLevelPage(),
);
}
return null;
},
);
}
}
-----------------------------------------------
class MatchPage extends StatefulWidget {
final Mode mode;
final Cpu cpu;
final Cpu cpu2;
const MatchPage({
Key? key,
required this.mode,
required this.cpu,
required this.cpu2,
}) : super(key: key);
#override
_MatchPageState createState() => _MatchPageState();
}
class _MatchPageState extends State<MatchPage> with TickerProviderStateMixin {
final board = Board();
late Player turn;
late Player winner;
List<List<Animation<double>?>> translations = List.generate(
7,
(i) => List.generate(
7,
(i) => null,
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xff61d3cb),
appBar: AppBar(
title: Text('Que le meilleur gagne '),
backgroundColor: Color(0xff61d3cb),
leading: IconButton(icon:Icon(Icons.arrow_back),
onPressed: () {
Navigator.pushAndRemoveUntil(context, MaterialPageRoute(
builder: (context) => home(),
), (e) => false);
}
)
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Flexible(
flex: 2,
child: Padding(
padding: const EdgeInsets.only(top: 1.0),
child: Stack(
children: <Widget>[
Positioned.fill(
child: Container(
color: Colors.white,
),
),
buildPieces(),
buildBoard(),
],
),
),
),
Flexible(
flex: 1,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: winner != null
? Text(
'Les ${winner == Player.RED ? 'Rouges' : 'Jaunes'} Gagnent',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: SizeConfig.safeBlockHorizontal! * 7),
)
: ListView(
children: <Widget>[
Text(
'Au tour des ${turn == Player.RED ? 'Rouges' : 'Jaunes'} ',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: SizeConfig.safeBlockHorizontal! * 6),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: GameChip(color: turn),
),
_buildPlayerName(context),
],
),
),
),
],
),
),
);
}
Text _buildPlayerName(BuildContext context) {
String name;
if (widget.mode == Mode.PVC) {
if (turn == widget.cpu.player) {
name = 'Ordinateur';
} else {
name = 'Toi';
}
} else if (widget.mode == Mode.PVP) {
if (turn == Player.RED) {
name = 'Joueur1';
} else {
name = 'Joueur2';
}
} else {
if (turn == widget.cpu.player) {
name = 'Ordinateur';
} else {
name = 'Ordinateur';
}
}
return Text(
name,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: SizeConfig.safeBlockHorizontal! * 6),
);
}
#override
void initState() {
super.initState();
if (widget.mode == Mode.PVC && turn == widget.cpu.player) {
cpuMove(widget.cpu);
} else if (widget.mode == Mode.DEMO) {
if (turn == widget.cpu.player) {
cpuMove(widget.cpu);
} else {
cpuMove(widget.cpu2);
}
}
}
GridView buildPieces() {
return GridView.custom(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 7),
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
final col = i % 7;
final row = i ~/ 7;
if (board.getBox(Coordinate(col, row)) == null) {
return SizedBox();
}
return GameChip(
translation: translations[col][row],
color: board.getBox(Coordinate(col, row)),
);
},
childCount: 49,
),
);
}
GridView buildBoard() {
return GridView.custom(
padding: const EdgeInsets.all(0),
physics: NeverScrollableScrollPhysics(),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 7),
shrinkWrap: true,
childrenDelegate: SliverChildBuilderDelegate(
(context, i) {
final col = i % 7;
return GestureDetector(
onTap: () {
if (winner == null) {
userMove(col);
}
},
child: CustomPaint(
size: Size(50, 50),
willChange: false,
painter: HolePainter(),
),
);
},
childCount: 49,
),
);
}
void userMove(int col) {
putChip(col);
if (winner == null && widget.mode == Mode.PVC) {
cpuMove(widget.cpu);
}
}
void cpuMove(Cpu cpu) async {
int col = await cpu.chooseCol(board);
putChip(col);
if (winner == null && widget.mode == Mode.DEMO) {
if (turn == widget.cpu.player) {
cpuMove(widget.cpu);
} else {
cpuMove(widget.cpu2);
}
}
}
void putChip(int col) {
final target = board.getColumnTarget(col);
final player = turn;
if (target == -1) {
return;
}
final controller = AnimationController(
vsync: this,
duration: Duration(seconds: 1),
)..addListener(() {
if (mounted) {
setState(() {});
}
});
if (mounted) {
setState(() {
board.setBox(Coordinate(col, target), turn);
turn = turn == Player.RED ? Player.YELLOW : Player.RED;
});
}
translations[col][target] = Tween(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
curve: Curves.bounceOut,
parent: controller,
))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.dispose();
}
});
controller.forward().orCancel;
if (board.checkWinner(Coordinate(col, target), player)) {
showWinnerDialog(context, player);
}
}
void showWinnerDialog(BuildContext context, Player player) {
setState(() {
winner = player;
});
Future.delayed(
Duration(seconds: 5),
() => mounted ? Navigator.popUntil(context, (r) => r.isFirst) : null,
);
}
void resetBoard() {
setState(() {
// winner = null;
board.reset();
});
}
}
Allow null for cpu2 or pass it for '/match' route.
MatchPage(
mode: args['mode'],
cpu: args['cpu'],
cpu2: args['cpu2'],
),
``
I think you are passing the data
arguments: {
'mode': Mode.PVC,
'cpu': DumbCpu(
Random().nextBool() ? Player.RED : Player.YELLOW),
},
but you are trying to access
return MaterialPageRoute(
builder: (context) => MatchPage(
mode: args['mode'],
cpu: args['cpu'],
cpu2: args['cpu2'], /// args['cpu2'] is not passed but you are accenting it.
),
);
So I think fixing this will resolve your issue.
I'll be looking forward to resolve your query :)

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

How to fix variable input delay when assigning a variable value?

I'm making a screen to upload a PDF file that shows a Page count of the PDF file. But whenever I get the PDF file. The screen doesn't show the Page count immediately. Here's the view of the screen.
The first picture is when I have not yet choose a PDF file, the second picture is when I already choose the file but not pressed the upload button yet (you can see the page count is still 0), and the last picture is when i pressed the upload button (the page count changed to 16)
Here is my code :
class _UploadPageState extends State<UploadPage>{
PdfDocument docFromFile;
String _fileName;
FileType _fileType;
String _path;
String _extension = "pdf";
bool _loadingPath = false;
int _pageCount = 0;
bool _visible = false;
#override
void initState() {
super.initState();
_fileType = FileType.CUSTOM;
}
void toggle() {
setState(() {
if(_loadingPath == false){
_visible = true;
}
else{
_visible = true;
}
});
_getDocument();
}
#override
Widget build(BuildContext context) {
if(_path != null){
_visible = true;
}
else{
_visible = false;
}
return Scaffold(
appBar: AppBar(
title: Text(
"Upload PDF"
),
),
body: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Column(
children: <Widget>[
SizedBox(height: 50),
Center(
child: _selectFile(),
),
SizedBox(height: 10),
_uploadButton(),
SizedBox(height: 40),
Visibility(
visible: _visible,
child: fileDescription(),
),
],
),
)
);
}
Widget _selectFile() {
return Container(
width: 300,
height: 150,
alignment: Alignment.center,
child: GestureDetector(
onTap: () {
print("Upload File");
_openFileExplorer();
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_loadingPath ? _fileSelectedLogo(_fileName) : _chooseFileLogo(),
//_chooseFileLogo(),
],
),
),
);
}
Widget _chooseFileLogo() {
return InkWell(
child: Column(
children: <Widget>[
Icon(Icons.file_upload, size: 50, color: Colors.blueGrey,),
Text("Click here to select a PDF file.", style: TextStyle(fontSize: 18)),
SizedBox(height: 20),
],
),
);
}
Widget _fileSelectedLogo(String fileName) {
return Text(
"File : $fileName",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
);
}
Widget _uploadButton() {
return RaisedButton(
onPressed: () {
print("Upload File");
_getDocument();
if(_path != null){
showDialog(
context: context,
builder: (context) {
return UploadDialog(
title: _pageCount.toString(),
description: "Test"
);
}
);
} else{
showDialog(
context: context,
builder: (context) {
return CancelDialog();
}
);
}
},
color: Colors.blue,
textColor: Colors.white,
child: Text("Upload File"),
);
}
Widget fileDescription() {
String fn;
String pc;
if(_path != null){
fn = "File Name : $_fileName" ;
pc = "Page Count : ${_pageCount.toString()}";
}else{
fn = "File Name :";
pc = "Page Count : ";
}
return Container(
width: MediaQuery.of(context).size.width,
child: Wrap(
direction: Axis.vertical,
children: <Widget>[
Text(fn),
SizedBox(height: 10),
Text(pc),
SizedBox(height: 10),
],
),
);
}
void _openFileExplorer() async{
_path = await FilePicker.getFilePath(type: _fileType, fileExtension: _extension);
if(!mounted) return;
setState(() {
if (_path != null) {
_loadingPath = true;
_fileName = _path.split('/').last;
}
});
}
void _getDocument() async{
if(_path != null){
docFromFile = await PdfDocument.openFile(_path);
setState(() {
_pageCount = docFromFile.pageCount;
});
print(docFromFile.pageCount.toString());
Toast.show("Page Count : ${docFromFile.pageCount.toString()}",
context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM
);
}
else{
print("Document not yet fetched.");
Toast.show("Document not yet fetched.",
context,
duration: Toast.LENGTH_SHORT,
gravity: Toast.BOTTOM
);
}
}
}
I've been looking for answers but have yet to found one. I'm new to flutter as well so I'm not sure where did I do wrong. If you need more code I'll provide it to you, and any answers are appreciated. Thank you.
You can copy paste run full code below
You need to await _openFileExplorer(); and await _getDocument();
code snippet
Widget _selectFile() {
return Container(
width: 300,
height: 150,
alignment: Alignment.center,
child: GestureDetector(
onTap: () async {
print("Upload File");
await _openFileExplorer();
await _getDocument();
},
working demo
full code
import 'package:flutter/material.dart';
import 'package:toast/toast.dart';
import 'package:pdf_render/pdf_render.dart';
import 'package:file_picker/file_picker.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: UploadPage(),
);
}
}
class UploadPage extends StatefulWidget {
#override
_UploadPageState createState() => _UploadPageState();
}
class _UploadPageState extends State<UploadPage> {
PdfDocument docFromFile;
String _fileName;
FileType _fileType;
String _path;
String _extension = "pdf";
bool _loadingPath = false;
int _pageCount = 0;
bool _visible = false;
#override
void initState() {
super.initState();
_fileType = FileType.CUSTOM;
}
void toggle() {
setState(() {
if (_loadingPath == false) {
_visible = true;
} else {
_visible = true;
}
});
_getDocument();
}
#override
Widget build(BuildContext context) {
if (_path != null) {
_visible = true;
} else {
_visible = false;
}
return Scaffold(
appBar: AppBar(
title: Text("Upload PDF"),
),
body: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Column(
children: <Widget>[
SizedBox(height: 50),
Center(
child: _selectFile(),
),
SizedBox(height: 10),
_uploadButton(),
SizedBox(height: 40),
Visibility(
visible: _visible,
child: fileDescription(),
),
],
),
));
}
Widget _selectFile() {
return Container(
width: 300,
height: 150,
alignment: Alignment.center,
child: GestureDetector(
onTap: () async {
print("Upload File");
await _openFileExplorer();
await _getDocument();
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_loadingPath ? _fileSelectedLogo(_fileName) : _chooseFileLogo(),
//_chooseFileLogo(),
],
),
),
);
}
Widget _chooseFileLogo() {
return InkWell(
child: Column(
children: <Widget>[
Icon(
Icons.file_upload,
size: 50,
color: Colors.blueGrey,
),
Text("Click here to select a PDF file.",
style: TextStyle(fontSize: 18)),
SizedBox(height: 20),
],
),
);
}
Widget _fileSelectedLogo(String fileName) {
return Text(
"File : $fileName",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
);
}
Widget _uploadButton() {
return RaisedButton(
onPressed: () {
print("Upload File");
_getDocument();
if (_path != null) {
/*showDialog(
context: context,
builder: (context) {
return UploadDialog(
title: _pageCount.toString(),
description: "Test"
);
}
);*/
} else {
/*showDialog(
context: context,
builder: (context) {
return CancelDialog();
}
);*/
}
},
color: Colors.blue,
textColor: Colors.white,
child: Text("Upload File"),
);
}
Widget fileDescription() {
String fn;
String pc;
if (_path != null) {
fn = "File Name : $_fileName";
pc = "Page Count : ${_pageCount.toString()}";
} else {
fn = "File Name :";
pc = "Page Count : ";
}
return Container(
width: MediaQuery.of(context).size.width,
child: Wrap(
direction: Axis.vertical,
children: <Widget>[
Text(fn),
SizedBox(height: 10),
Text(pc),
SizedBox(height: 10),
],
),
);
}
void _openFileExplorer() async {
_path = await FilePicker.getFilePath(
type: _fileType, fileExtension: _extension);
if (!mounted) return;
setState(() {
if (_path != null) {
_loadingPath = true;
_fileName = _path.split('/').last;
}
});
}
void _getDocument() async {
if (_path != null) {
docFromFile = await PdfDocument.openFile(_path);
setState(() {
_pageCount = docFromFile.pageCount;
});
print(docFromFile.pageCount.toString());
Toast.show("Page Count : ${docFromFile.pageCount.toString()}", context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
} else {
print("Document not yet fetched.");
Toast.show("Document not yet fetched.", context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
}
}
}