I am studying flutter while making a pomodoro app.
After setting this app to 25 minutes, press the middle button to decrease the time by seconds and press the button again to pause.
I am getting the following error while configuring the app using the Timer class.
The Timer class takes a repeating period and a callback function as arguments.
However, the 'tickDown' function receives the Timer class as an argument, but I don't know why the error pops up.
Below is the code I wrote. I'd like to hear your thoughts on what the problem is.
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
var totalSeconds = 1500;
Timer _timer = Timer.periodic(Duration(seconds: 1), tickDown);
bool isRunning = false;
void playTimer() {
setState(() {
isRunning = true;
_timer;
});
}
void pause() {
_timer.cancel();
setState(() {
isRunning = false;
});
}
void tickDown(Timer _timer) {
setState(() {
totalSeconds -= 1;
});
}
String showRemainTime() {
var hour = (totalSeconds ~/ 60).toString();
var minute = (totalSeconds % 60).toString();
String time;
minute.length == 1 ? time = '$hour : 0$minute' : time = '$hour : $minute';
return time;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: Column(
children: [
Flexible(
flex: 3,
fit: FlexFit.tight,
child: Container(
alignment: const Alignment(0, 0),
child: Text(
showRemainTime(),
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).cardColor,
fontSize: 80,
fontWeight: FontWeight.w700,
),
),
),
),
Flexible(
flex: 4,
fit: FlexFit.tight,
child: Container(
alignment: const Alignment(0, -0.5),
child: IconButton(
iconSize: 100,
padding: EdgeInsets.zero,
onPressed: isRunning == true ? pause : playTimer,
icon: Icon(
isRunning == true
? Icons.pause_circle_outline_rounded
: Icons.play_circle_outlined,
color: Theme.of(context).cardColor,
),
),
),
),
Flexible(
flex: 2,
fit: FlexFit.tight,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
color: Theme.of(context).cardColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Pomodoro',
style: TextStyle(
fontSize: 23,
color: Theme.of(context).textTheme.headline1!.color,
fontWeight: FontWeight.w700,
),
),
Text(
'0',
style: TextStyle(
fontSize: 52,
color: Theme.of(context).textTheme.headline1!.color,
),
),
],
),
],
),
),
),
],
),
);
}
}
I also searched the flutter official documentation, but I couldn't find anything suitable for my situation. I want to fix this error with minimal modifications to my current code.
tickDown is a method of _HomeScreenState. Object methods cannot be accessed in initializers, because the object has not been fully constructed yet. This includes object properties with assignment definitions (like what you have) as well as initializers specified in constructors.
There are two ways you can overcome this.
Change your declaration of _timer to be late:
late var _timer = Timer.periodic(Duration(seconds: 1), tickDown);
However, for your particular situation, I don't recommend this approach.
Instantiate the timer later. This would look like
Timer? _timer;
void playTimer() {
setState(() {
_timer = Timer.periodic(Duration(seconds: 1), tickDown);
isRunning = true;
});
}
Note: To prevent issues, be sure to cancel the timer in the State's dispose() method.
Related
*facing error in timer functionality in flutter project
*please any one can help me to get rid of this timer functionality error
import 'dart:async';
import 'package:flutter/material.dart';
class StopwatchHome extends StatefulWidget {
StopwatchHome({Key? key}) : super(key: key);
#override
State<StopwatchHome> createState() => _StopwatchHomeState();
}
class _StopwatchHomeState extends State<StopwatchHome> {
int secounds=0, minutes=0, hours=0;
String digitsecounds="00", digitminutes ="00", digithours="00";
Timer? timer;
bool started= false;
List laps=[];
void stop(){
timer!.cancel();
setState(() {
started=false;
});
}
void reset(){
timer!.cancel();
setState(() {
secounds=0;
minutes=0;
hours=0;
digitsecounds="00";
digitminutes="00";
digithours="00";
started=false;
});
}
void addlaps(){
String lap="$digithours:$digitminutes:$digitsecounds";
setState(() {
laps.add(lap);
});
}
void start(){
started=true;
timer=Timer.periodic(Duration(seconds: 1), (timer) {
int localSecounds=secounds+1;
int localMinutes= minutes=0;
int localHours= hours;
if (localSecounds>59) {
if (localMinutes>59) {
localHours++;
localMinutes==0;
}else{localMinutes++;localSecounds==0;}
}
setState(() {
secounds= localSecounds;
minutes= localMinutes;
hours= localHours;
digitsecounds=(secounds>=10)?"$secounds":"0$secounds";
digithours=(hours>=10)?"$hours":"0$hours";
digitminutes=(minutes>=10)?"$minutes":"0$minutes";
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor:
// const Color(0xff213a20),
Color.fromARGB(255, 138, 11, 87),
body: SafeArea(
child: Padding(
padding: EdgeInsets.all(18),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: Text("Stopwatch",
style: TextStyle(color: Colors.white,
fontWeight: FontWeight.bold,fontSize: 40,
fontStyle: FontStyle.italic),),
),
SizedBox(height: 20.0),
Center(child: Text("$digithours:$digitminutes:$digitsecounds",style: TextStyle(color: Colors.white,fontSize: 73.0,fontWeight: FontWeight.bold),),),
Container(
height: 300.0,
decoration: BoxDecoration(color: Color.fromARGB(255, 162, 109, 145),
borderRadius: BorderRadius.circular(10)),
child: ListView.builder(
itemCount: laps.length,
itemBuilder: (context,index){
return Padding(
padding: const EdgeInsets.all(18.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Lap Number = ${index+1}",style: TextStyle(color: Colors.white,fontSize: 16 ),),
Text("${laps[index]}",style:TextStyle(color: Colors.white,fontSize: 16 ) )
],
),
);
}
),
),
SizedBox(height: 23),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: RawMaterialButton(onPressed: (){
(!started) ? start():stop();
},
fillColor: Color.fromARGB(255, 73, 119, 4),
shape:StadiumBorder(side: BorderSide(color: Colors.blue)),
child: Text((! started)? "START":"PAUSE",style: TextStyle(color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic
),
),
)
),
SizedBox(width: 8),
IconButton(
onPressed: (){addlaps();}, icon: Icon(Icons.timelapse_rounded,color: Colors.red,)),
Expanded(child: RawMaterialButton(onPressed: (){reset();},
fillColor: Color.fromARGB(255, 132, 9, 23),
shape:StadiumBorder(side: BorderSide(color: Color.fromARGB(255, 40, 129, 11))),
child: Text("RESET",style: TextStyle(color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic
),
),
)
)
],
)
],
),
),
),
);
}
}
please any one can help me to get rid of this timer functionality error
#the digitsecound is not restarting after completing sixty secounds
#Anyone who can help? I'm definitely missing something.
The problem lies in this part:
else{localMinutes++;localSecounds==0;}
You are checking if localSecounds is equal to 0, not actually giving it the value 0(Same thing with minutes). Try:
if (localSecounds>59) {
if (localMinutes>59) {
localHours++;
localMinutes=0;
}else{
localMinutes++;
localSecounds=0;
}
}
Check this start Timer Function, it may help you out
late Timer _timer;
int seconds = 00;
int minutes = 00;
int hours = 00;
void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(
() {
seconds++;
if (seconds > 59) {
minutes += 1;
seconds = 0;
if (minutes > 59) {
hours += 1;
minutes = 0;
}
}
},
),
);
}
Or User can use this simple but powerful package : stop_watch_timer from pub.dev
I am using the package extended_image to load images from the network and display a shimmer on loading or on error.
I am getting this error setState() or markNeedsBuild() called during build when I am trying to call setState inside the loadStateChanged
In fact, I have two widgets, one VideoThumbnail responsible for loading a thumbnail from the network, and another one VideoDesc that should display the thumbnail description.
But I would like the description to display a shimmer when the image fails to load or is taking longer to load.
I created two states variables, on the VideoThumbnail widget, that should be passed to the VideoDesc widget
videoLoading = true;
videoError = false;
Here is my code following the repo example:
VideoThumbnail State
class _VideoThumbnailState extends State<VideoThumbnail>
with SingleTickerProviderStateMixin {
bool videoLoading;
bool videoError;
AnimationController _controller;
#override
void initState() {
videoLoading = true;
videoError = false;
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
);
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
print("Build Process Complete");
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
width: widget.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(4.0),
child: ExtendedImage.network(
widget.videoUrl,
width: widget.width,
height: (widget.width) * 3 / 4,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
_controller.reset();
setState(() {
videoError = false;
videoLoading = true;
});
return Shimmer.fromColors(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.0),
),
),
baseColor: Colors.black12,
highlightColor: Colors.white24,
);
break;
case LoadState.completed:
_controller.forward();
setState(() {
videoError = false;
videoLoading = false;
});
return FadeTransition(
opacity: _controller,
child: ExtendedRawImage(
image: state.extendedImageInfo?.image,
width: widget.width,
height: (widget.width) * 3 / 4,
),
);
break;
case LoadState.failed:
_controller.reset();
state.imageProvider.evict();
setState(() {
videoError = true;
videoLoading = false;
});
return Container(
width: widget.width,
height: (widget.width) * 3 / 4,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/img/not-found.png"),
fit: BoxFit.fill,
),
),
);
break;
default:
return Container();
}
},
),
),
VideoDesc(
desc: widget.desc,
videoError: videoError,
videoLoading: videoLoading,
)
],
),
);
}
}
Video widget
class VideoDesc extends StatelessWidget {
final String desc;
final bool videoLoading;
final bool videoError;
const VideoDesc({
Key key,
#required this.desc,
this.videoLoading = true,
this.videoError = false,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: videoError || videoLoading
? Shimmer.fromColors(
baseColor: Colors.grey[700],
highlightColor: Colors.white24,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 12.0),
Container(
width: double.infinity,
height: 8.0,
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: BorderRadius.circular(2.0),
),
),
SizedBox(height: 12.0),
Container(
width: 80.0,
height: 8.0,
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: BorderRadius.circular(2.0),
),
),
],
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 12.0),
Text(
desc,
style: TextStyle(
color: Colors.white,
fontSize: 11.0,
),
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 5.0),
Text(
"361,143,203 views",
style: TextStyle(
color: Colors.white54,
fontSize: 12.0,
),
),
],
),
);
}
}
Can anyone help me with this problem? Or if there is a better way to get the extendedImageLoadState value and pass it to another widget without calling the setState inside loadStateChanged
You can't call setState during build process.
If you actually need to, you can do so by using instead:
WidgetsBinding.instance.addPostFrameCallback(() => setState((){}));
However, have in mind, that having this on your switch-case will schedule an infinite loop of rebuilds which you don't want as well.
I suggest you to re-structure your UI logic or at least make it conditional:
if(!videoLoading) {
WidgetsBinding.instance.addPostFrameCallback(() => setState((){
videoError = false;
videoLoading = true;
}));
}
I made a radio player app with the code below. Everything works fine when the mobile screen is turned on. But when i turn off my mobile screen the radio stops playing at about 5-8 minutes. I got some tips about using flutter audio_service. (https://pub.dev/packages/audio_service) But i am confused from where should i start. Should i recode again or i can modify this code. Somebody please help me. It would be a grace. Thankyou in advance.
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
class Radio1 extends StatefulWidget {
#override
_Radio1State createState() => _Radio1State();
}
class _Radio1State extends State<Radio1> {
AudioPlayer audioPlayer = AudioPlayer();
#override
void initState() {
super.initState();
AudioPlayer.logEnabled = true;
}
bool _isPlaying = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
//new
SizedBox(
height: 50,
),
//Icon(
// Icons.arrow_drop_down,
//size: 40,
//),
//new
Container(
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 50),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Color(0x46000000),
offset: Offset(0, 20),
spreadRadius: 0,
blurRadius: 30,
),
BoxShadow(
color: Color(0x11000000),
offset: Offset(0, 10),
spreadRadius: 0,
blurRadius: 30,
),
],
),
//new
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Image(
image: AssetImage("assets/radiologo.jpg"),
width: MediaQuery.of(context).size.width * 0.7,
height: MediaQuery.of(context).size.width * 0.7,
fit: BoxFit.cover,
),
),
),
Text(
"sample text",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.w500),
),
Text(
"(sample text)",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
),
/* Slider(
value: 10,
onChanged: (v) {},
max: 170,
min: 0,
activeColor: Color(0xFF5E35B1),
), */
Text(
"sample text.",
style: TextStyle(fontSize: 10, fontWeight: FontWeight.w500),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: _isPlaying == false
? Icon(Icons.play_circle_outline)
: Icon(Icons.pause_circle_outline),
iconSize: 60.0,
onPressed: () {
getAudio();
},
),
IconButton(
icon: Icon(Icons.stop),
iconSize: 40,
onPressed: () {
stopAudio();
},
),
//new line
],
),
],
),
),
),
);
}
void getAudio() async {
var url = "http://ia802708.us.archive.org/3/items/count_monte_cristo_0711_librivox/count_of_monte_cristo_001_dumas.mp3";
if (_isPlaying) {
var res = await audioPlayer.pause();
if (res == 1) {
setState(() {
_isPlaying = false;
});
}
} else {
var res = await audioPlayer.play(url);
if (res == 1) {
setState(() {
_isPlaying = true;
});
}
}
}
void stopAudio() async {
int res = await audioPlayer.stop();
if (res == 1) {
setState(() {
_isPlaying = false;
});
}
}
void releaseAUdio() async {
await audioPlayer.stop();
await audioPlayer.release();
}
#override
void dispose() {
super.dispose();
releaseAUdio();
}
}
So as you use the audioplayers package, you'll need to implement the audio_service one to achieve what you want (playing audio in background). Indeed, the audioplayers package is only responsible for playing audio files, and does not handle the background behaviours.
The audio_service is designed to be the only source of truth in your application. So you'll need to re-architecture your code to fit.
But don't delete your code, you might not need many changes in it for the audio.
The package is cut in multiple parts. For example, one for the background tasks, one for the UI to tell the background tasks what you want to do (play, pause, seekTo, ...), so the only changes you might need to do in your code will be to call this part, called AudioService (check the API reference for more informations: https://pub.dev/documentation/audio_service/latest/audio_service/AudioService-class.html).
Once you did that, of course you'll have to implement your background task to achieve your needs.
In summary:
Your code is good, but does not handle the background behaviours.
You may need to implement the audio_service package (or a similar one) to handle the background behaviours.
Please take a look at the audio_session package too to handle the interactions between your app and the different audio interactions on the phone. (For example, handle the notifications received and decrease the volume of your app in consequence).
Hope this answer is helpful for you, good luck :)
I have a button and it should appear in 30 seconds. The countdown starts from 30 seconds. When it reaches 0, the resend code button should appear/enable.
You can do it like this using Timer from dart:async..
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
int secondsRemaining = 30;
bool enableResend = false;
Timer timer;
#override
initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 1), (_) {
if (secondsRemaining != 0) {
setState(() {
secondsRemaining--;
});
} else {
setState(() {
enableResend = true;
});
}
});
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TextField(),
const SizedBox(height: 10),
FlatButton(
child: Text('Submit'),
color: Colors.blue,
onPressed: () {
//submission code here
},
),
const SizedBox(height: 30),
FlatButton(
child: Text('Resend Code'),
onPressed: enableResend ? _resendCode : null,
),
Text(
'after $secondsRemaining seconds',
style: TextStyle(color: Colors.white, fontSize: 10),
),
],
);
}
void _resendCode() {
//other code here
setState((){
secondsRemaining = 30;
enableResend = false;
});
}
#override
dispose(){
timer.cancel();
super.dispose();
}
}
Link to the code on Dartpad -
https://dartpad.dev/a59c751c4f6b4721a7af1cc27c67650b
For someone searching for this solution for new Versions of Flutter, which has null safety.
You need to make some changes to the verified code shared by Jigar Patel above.
Timer? timer;
timer?.cancel();
Reason: adding ? will help you to bypass null safety.
Now coming to use Resend OTP Functionality
I've arranged two containers in a Column,
first Container for Resend OTP Button and second Container to show timer like this:
children:[
Container(
width: 150,
margin: EdgeInsets.only(left: 410),
child: InkWell(**onTap: () => enableResend ? _resendCode() : null,**
child: Text(
"Resend OTP",
style: TextStyle(
fontSize: 13,
**color: enableResend
? Color(0xff3E64FF)
: Colors.grey,**
fontWeight: FontWeight.bold,
fontFamily:'Raleway'),
),
),
),
const SizedBox(height: 5),
Container(
width: 150,
margin: EdgeInsets.only(left: 390),
child: InkWell(
**child: Text(
'(after $secondsRemaining seconds)',
style: TextStyle( color: Color(0xff3E64FF), fontSize: 10),**
),
),
),
], )
so here with respect to Jigar Patel's code, I've changed:
_resendCode() instead of just using _resendCode, because on clicking resend button function should be called.
**onTap: () => enableResend ? _resendCode() : null,**
In _resendCode() function,
I've called my own function named "send code on email" - you can put your own resend Code function here.
//other code here
**SendCodeOnEmail ();**
setState(() {
secondsRemaining = 30;
enableResend = false;
});
}
color: enableResend? Color(0xff3E64FF): Colors.grey,
so that Resend OTP text
is disabled for 30 seconds and it's colour changes to grey, and when it is enabled it changes to blue
I have one screen to display data from shared preferences. I already success save and get data from shared preferences. Then I have a flow in one screen like this:
If the user clicks that screen, it will check the data from shared preferences.
If data is not null / not empty, it will display the data login like user profile, etc.
If data is null/empty, it will show a button login.
I get the logic for that flow, but the problem is, before data showing in the screen (number 2), it shows button login first for a few milliseconds then show the data. Why did it happen? It's not got data from API / internet and I'm not using FutureBuilder, I just using Shared Preferences. How to kill this delayed? Below is my full code:
class MorePage extends StatefulWidget {
#override
_MorePageState createState() => _MorePageState();
}
class _MorePageState extends State<MorePage> {
bool isLoading = false;
SessionManager _sessionManager = SessionManager();
int status;
#override
void initState() {
super.initState();
_sessionManager.getStatusLogin().then((value) { //i use this for get status code login success
setState(() {
status = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: color_grey_bg,
body: SafeArea(
child: getMorePage(),
),
);
}
Widget getMorePage() {
return ListView(
physics: ClampingScrollPhysics(),
children: <Widget>[
Container(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.width / 20,
),
height: MediaQuery.of(context).size.width / 4,
width: MediaQuery.of(context).size.width,
color: color_white,
child: setProfile(),
),
],
);
}
Widget setProfile() {
if (status == 200) { // i use this logic to show widget with status login, but it's have delayed like show data from API. How to kill it? Because I using SharedPreferences, not hit the API
return profileUser();
} else {
return notSignIn();
}
}
Widget profileUser() {
return Row(
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
style: TextStyle(
color: color_grey_text,
fontSize: MediaQuery.of(context).size.width / 26,
fontWeight: FontWeight.bold,
),
),
Text(
email,
style: TextStyle(
color: color_grey_text,
fontSize: MediaQuery.of(context).size.width / 30,
fontWeight: FontWeight.normal,
),
),
Text(
role,
style: TextStyle(
color: color_grey_text,
fontSize: MediaQuery.of(context).size.width / 35,
fontWeight: FontWeight.normal,
),
),
],
),
Spacer(),
IconButton(
icon: Icon(
Icons.arrow_forward_ios,
size: MediaQuery.of(context).size.height / 40,
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => DetailUserPage()));
},
),
],
);
}
Widget notSignIn() {
return Padding(
padding:
EdgeInsets.only(left: 50.0, right: 50.0, top: 30.0, bottom: 30.0),
child: RaisedGradientButton(
child: Text(
'Login',
style: TextStyle(
color: color_white,
fontSize: MediaQuery.of(context).size.width / 25),
),
gradient: LinearGradient(
colors: <Color>[color_blue, color_green],
),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => LoginPage()));
},
),
);
}
}
And this is class SessionManager for create function of shared_preferences:
class SessionManager {
.....
getStatusLogin() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
int status = preferences.getInt("status");
return status;
}
....
}
the getprofile function is actually is a future , you used the async await keywords .
it's true that retrieving the data from the sharedpref doesn't take time ,but getting the instance of the sharedpref is the root of the cause . so you have to options to solve this solution .
1-getting the instance of the shared pref in the main function. you can get the instance of the shared pref and pass it as argument to the whole application.
Example :
void main ()async{
final instance = await sharedPreference.getInstance();
runApp(MyApp(instance));}
now in your MorePage widget
class _MorePageState extends State<MorePage> {
LoginStatus _loginStatus = LoginStatus.notSignIn;
SessionManager _sessionManager = SessionManager();
String name, email, role;
//no need for the async keyword
getProfile() { //this func just for show the data
name = widget.preferences.getString("fullname");
email = widget.preferences.getString("email");
role = widget.preferences.getString("role");
}
#override
void initState() {
super.initState();
getProfile();
_sessionManager.getLoginStatus().then((value) { //this code for get the status of login
setState(() {
_loginStatus = value;
});
});
}
now the getProfile function is not async which means there's no milliseconds that makes that weird behavior at the beginning .
2-Make another enum value 'busy' (simpler solution) . simply you can leave you code as it's but add a new enum value which is busy to give hint to the user that the app is checking if he has logined before or not ,you simply will give him that hint in the set profile function , you'll create another condition if ( _loginStatus == LoginStatus.busy ) return Text('checking user Info').
hope that helps !
Edit :
You this package get_it to make a singleton instance of the session manager class and you can access it anywhere .
GetIt locator = GetIt();
void setUpLocator() {
locator.registerLazySingleton(() => SessionManager());
}
void main() async {
setUpLocator();
await locator.get<SessionManager>().getStatusLogin();
runApp(MyApp());
}
class MorePage extends StatefulWidget {
#override
_MorePageState createState() => _MorePageState();
}
class _MorePageState extends State<MorePage> {
bool isLoading = false;
final _sessionManager = locator.get<SessionManager>();
int status;
#override
void initState() {
super.initState();
//make property of statuesif you don't have property of the statues
//in the session manager class
status = _sessionManager.statues;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: color_grey_bg,
body: SafeArea(
child: getMorePage(),
),
);
}
Widget getMorePage() {
return ListView(
physics: ClampingScrollPhysics(),
children: <Widget>[
Container(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.width / 20,
),
height: MediaQuery.of(context).size.width / 4,
width: MediaQuery.of(context).size.width,
color: color_white,
child: setProfile(),
),
],
);
}
Widget setProfile() {
if (status == 200) {
// i use this logic to show widget with status login, but it's have delayed like show data from API. How to kill it? Because I using SharedPreferences, not hit the API
return profileUser();
} else {
return notSignIn();
}
}
Widget profileUser() {
return Row(
children: <Widget>[
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
style: TextStyle(
color: color_grey_text,
fontSize: MediaQuery.of(context).size.width / 26,
fontWeight: FontWeight.bold,
),
),
Text(
email,
style: TextStyle(
color: color_grey_text,
fontSize: MediaQuery.of(context).size.width / 30,
fontWeight: FontWeight.normal,
),
),
Text(
role,
style: TextStyle(
color: color_grey_text,
fontSize: MediaQuery.of(context).size.width / 35,
fontWeight: FontWeight.normal,
),
),
],
),
Spacer(),
IconButton(
icon: Icon(
Icons.arrow_forward_ios,
size: MediaQuery.of(context).size.height / 40,
),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailUserPage()));
},
),
],
);
}
Widget notSignIn() {
return Padding(
padding:
EdgeInsets.only(left: 50.0, right: 50.0, top: 30.0, bottom: 30.0),
child: RaisedGradientButton(
child: Text(
'Login',
style: TextStyle(
color: color_white,
fontSize: MediaQuery.of(context).size.width / 25),
),
gradient: LinearGradient(
colors: <Color>[color_blue, color_green],
),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => LoginPage()));
},
),
);
}
}
I think show Circle Progress once getting values form shared pref then show main container.
Please check below code:-
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: color_grey_bg,
body: SafeArea(
child: LoginStatus.notSignIn ? const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Color(colorPrimary))),
) : getMorePage()
),
);
}
I think you should create a class and then use reference -:
Future<void> main() async{
ShareP.preferences = await SharedPreferences.getInstance();
}
class ShareP {
static SharedPreferences preferences;
}
Now you can refer it ("ShareP.preferences") and get your SharedPreference values