I have an app which gets hours and minutes from the backend. Every time the app is called the data from the backend gets saved in shared preferences. When he user has no internet connection I show the same screen as the user saw when having internet just using the data saved in shared preferences instead. One of the parts of the data is a timer which I get the hours and minutes from the backend, and the hours and minutes get updated every time I make the API call. What I want now is to have the data from shared preferences to create a timer with the data saved in shared preferences. I need to have the minutes and hours update and work just like it would with the normal data with internet connection. So when there is no internet connection we display the data and I need to create the timer to update the minutes and the hours so the user can see the timer update even when there is no internet. So if the minutes and hours in shared preferences are saved like: 1hr : 6mins. I need to make it so the time keeps going and minutes and hours keep updating. So after 60 secs mins will be 7mins, and after 54mins hours will be 2hrs, And the process starts right when the screen is opened without any start button.
// Saving data from api in shared preferences
body: FutureBuilder<Response>(
future: futureDataForStatus,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
accountInfo = WorkingLocationStatus.fromJson(
json.decode(snapshot.data!.body),
);
final duration = IsoDuration.parse(
accountInfo!.duration.toString(),
);
prefs.setDouble('workingHours', duration.hours);
prefs.setDouble('workingMinutes', duration.minutes);
return Column(
// Displaying the saved data
class OffilneWorkingHoursMinutes extends StatefulWidget {
const OffilneWorkingHoursMinutes({Key? key}) : super(key: key);
#override
State<OffilneWorkingHoursMinutes> createState() =>
_OffilneWorkingHoursMinutesState();
}
class _OffilneWorkingHoursMinutesState
extends State<OffilneWorkingHoursMinutes> {
#override
Widget build(BuildContext context) {
return Center(
child: Text(
'${prefs.getDouble('workingHours')!.toStringAsFixed(0)} hrs - '
'${prefs.getDouble('workingMinutes')!.toStringAsFixed(0)} mins ',
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
);
}
}
Look at this example, I think it could help, just adjust it to your needs:
class OffilneWorkingHoursMinutes extends StatefulWidget {
const OffilneWorkingHoursMinutes({Key? key}) : super(key: key);
#override
State<OffilneWorkingHoursMinutes> createState() =>
_OffilneWorkingHoursMinutesState();
}
class _OffilneWorkingHoursMinutesState
extends State<OffilneWorkingHoursMinutes> {
var time = Time(hours: 1, minutes: 6, seconds: 0); // pass your time from backend here
#override
void initState() {
Timer.periodic(const Duration(seconds: 1), (timer) { // use timer when you are offline and cancel it when you are back online
setState(() {
if (time.seconds < 59) {
time.seconds++;
} else {
if (time.minutes < 59) {
time.minutes++;
} else {
time.minutes = 0;
time.hours++;
}
time.seconds = 0;
}
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Center(
child: Text(
'${time.hours} hrs - '
'${time.minutes} mins - '
'${time.seconds} sec ',
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
);
}
}
class Time {
int hours;
int minutes;
int seconds;
Time({
this.hours = 0,
this.minutes = 0,
this.seconds = 0,
});
}
Related
I am loading a crypto API from Coingecko in a ListView.builder widget.
This is the API: https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false
If I load it for the first time when I launch the emulator, the data shows almost instantly. Which is not bad.
However, if I make edits to my UI and try to do hot reloads, more often than not, it takes awhile for the data to appear on the emulator, approx 1 minutes to 5 minutes. Sometimes, nothing loads onto the screen and I do wonder if it is actually still running behind the scene. This makes it very hard for me to make adjustments/tweaks/design the UI. I will either have to :
Wait for it to appear on screen or
Close the emulator and restart again from the Virtual Device Manager for the data to appear right away on screen.
Is there a way for the data from the API to load faster to make the design experience much smoother? Or am I doing it the wrong way?
FYI, I use Android Studio.
Any help would be much appreciated. Thanks in advance.
The code is as follows:
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreen();
}
class _HomeScreenState extends State<HomeScreenScreen> {
Future<List<Asset>> fetchCoin() async {
assetList = [];
final response = await http.get(Uri.parse(
'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false'));
if (response.statusCode == 200) {
List<dynamic> values = [];
values = json.decode(response.body);
if (values.length > 0) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
Map<String, dynamic> map = values[i];
assetList.add(Asset.fromJson(map));
}
}
setState(() {
assetList;
});
}
return assetList;
} else {
throw Exception('Failed to load coins');
}
}
#override
void initState() {
fetchCoin();
Timer.periodic(Duration(seconds: 1), (timer) => fetchCoin());
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Asset', style: TextStyle(color: Colors.black),),
elevation: 0,
toolbarHeight: 50,
backgroundColor: Colors.white,
actions: <Widget>[
IconButton(
icon: Icon(Icons.search, color: Colors.black26),
onPressed: () {},
)
],
),
body: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: assetList.length,
itemBuilder: (context, index) {
return AssetCryptoCard(
name: assetList[index].name,
image: assetList[index].image,
);
},
),
);
}
}
Application doing too much work on main thread. This kind of application should be developed with the help of multi threading concept.so flutter also support multi threading with the help of Isolates. it will be give high performance.
it may be helpful below this tutorial
https://www.javatpoint.com/dart-isolates
https://pub.dev/documentation/flutter_isolate/
Code calls the rate limited API at every second. When the rate is exceeded, API stops answering with 200 responses until resumes after a while, when the threshold is reset.
Timer.periodic(Duration(seconds: 1), (timer) => fetchCoin());
first of all, sorry for the lack of information in the title, I just couldn't think of anything better to write.
My problem is that I'm parsing JSON data from an online API and sometimes it displays what I want it to display, and sometimes it doesn't... here is the code:
(The first code block is the class of what I'll be using in the second block, which is the important part and probably where I think the problem is coming from)
import 'dart:convert';
import 'package:http/http.dart' as http;
class CanteenInfo {
final String canteenUrl = 'https://sigarra.up.pt/feup/pt/mob_eme_geral.cantinas';
List canteenMenu = [];
var data;
String workingHours = "yes";
CanteenInfo() {
getWebsiteData();
}
Future getWebsiteData() async {
var response = await http.get(Uri.parse(canteenUrl));
if (response.statusCode == 200) {
data = json.decode(response.body);
workingHours = data[3]["horario"];
print("Hours: " + workingHours);
}
else {
throw Exception('Failed to read $canteenUrl');
}
}
}
class WorkingHours extends StatefulWidget {
const WorkingHours({Key? key}) : super(key: key);
#override
State<WorkingHours> createState() => _WorkingHours();
}
class _WorkingHours extends State<WorkingHours> {
String hours = "yes";
CanteenInfo canteen = CanteenInfo();
void getHours() {
setState(() {
hours = canteen.getHours();
});
}
#override
Widget build(BuildContext context) {
getHours();
return Scaffold(
appBar: AppBar(
title: Text('EasyFood'),
),
body: Center(
child: Container (
margin: const EdgeInsets.only(top: 100),
child: Column(
children: <Widget>[
Text(
'Lunch: ' + hours,
style: const TextStyle(
fontSize: 20
)
),
],
),
),
),
);
}
}
If I haven't made my problem clear, when I run the code, sometimes it displays the text as "Lunch: yes" and sometimes as the correct thing, which is "Lunch: 11:30 às 14:00".
By my understanding, I think what's happening is that sometimes the code can get the API information and time and has time to change the variable before the page loads, and sometimes it doesn't. Either way, I don't know how to fix it so if anyone has any idea I would relly appreciate the help :)
Thanks alot for taking the time to read this
I'm not sure if getHours method exists in CanteenInfo.
But you probably need to override initState method in your stateful widget and call
canteen.getWebsiteData().then((value) {
setState(() => hours = canteen.workingHours);
});
or something like that depending on which method returns data from API
A good decision will be to wait CanteenInfo object "canteen" to be initialized, smth like this:
var flag = false;
loadData()async{
flag = true;
canteen = CanteenInfo();
await getHours();
setState((){flag=false;});
}
Widget build(BuildContext context) {
if (hours == "yes" && !flag)
loadData();
if (flag)
return Scaffold(body:CircularProgressIndicator());
return Scaffold(
appBar: AppBar(
title: Text('EasyFood'),
),
body: Center(
child: Container (
margin: const EdgeInsets.only(top: 100),
child: Column(
children: <Widget>[
Text(
'Lunch: ' + hours,
style: const TextStyle(
fontSize: 20
)
),
],
),
),
),
);
}
I'm not sure if class object initialization works correct here, maybe you also need to add async/await
class CanteenInfo {
final String canteenUrl = 'https://sigarra.up.pt/feup/pt/mob_eme_geral.cantinas';
List canteenMenu = [];
var data;
String workingHours = "yes";
getWebsiteData() async {
var response = await http.get(Uri.parse(canteenUrl));
if (response.statusCode == 200) {
data = json.decode(response.body);
workingHours = data[3]["horario"];
print("Hours: " + workingHours);
}
else {
throw Exception('Failed to read $canteenUrl');
}
}
getHours() {
return workingHours;
}
}
// ------------------------
class WorkingHours extends StatefulWidget {
const WorkingHours({Key? key}) : super(key: key);
#override
State<WorkingHours> createState() => _WorkingHours();
}
// ------------------------
class _WorkingHours extends State<WorkingHours> {
String hours = "yes";
CanteenInfo canteen = CanteenInfo();
#override
void initState() {
super.initState();
canteen.getWebsiteData().then(() {
hours = canteen.getHours();
});
}
}
I want my flutter web page display a photo , and change for every 3 seconds to display another photo ..
here is my code
class _contactPageState extends State<contactPage> {
List<String> randomPics= ['profiles/github.png', 'profiles/linkedIn.png', 'profiles/stack.png'];
Timer timer;
var random= Random();
String photo;
#override
void initState() {
photo = randomPics[random.nextInt(randomPics.length)];
openLink = new linksToOpen();
super.initState();
timer = new Timer(new Duration(seconds: 1), ( ){
setState(() {
photo= randomPics[random.nextInt(randomPics.length)];
print('${photo}');
});
});
}
#override
Widget build(BuildContext context) {
Container(
child: Image(image : AssetImage(photo),gaplessPlayback: true,),
width: 400, height: 400,
),
What is the problem with my code ?
Please , can anyone help me !
I did some test and I think this could work for you:
import 'dart:async';
import 'dart:math';
import 'package:flutter/widgets.dart';
class ContactPageState extends StatefulWidget {
ContactPageState({Key key}) : super(key: key);
#override
_ContactPageState createState() => _ContactPageState();
}
class _ContactPageState extends State<ContactPageState> {
List<String> randomPics = ['assets/a.jpg', 'assets/b.jpg', 'assets/c.jpg'];
String photo;
final random = new Random();
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => configureTimer());
super.initState();
}
configureTimer() {
Timer.periodic(Duration(milliseconds: 3000), (timer) {
final int index = random.nextInt(randomPics.length);
setState(() {
photo = randomPics[index];
debugPrint('Select picture $photo in index $index');
});
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Image(
image: AssetImage(photo != null ? photo : 'assets/a.jpg'),
gaplessPlayback: true,
),
width: 400,
height: 400,
);
}
}
For a quick fix, you need to add function setStatus ... as a callback function so it can call itself infinitely.
void _nextPic(){
setState(() {
photo= randomPics[random.nextInt(randomPics.length)];
print('${photo}');
});
timer = new Timer(new Duration(seconds: 1), _nextPic);
}
This function create another timer after this one is done. So you can just need to create the first timer in initState...
#override
void initState() {
photo = randomPics[random.nextInt(randomPics.length)];
super.initState();
timer = new Timer(new Duration(seconds: 1), _nextPic);
}
For one thing, you will need to specify 3 seconds instead of 1. Another, you may be looking for timer.periodic to have this execute multiple times (until you tell it to stop) instead of a single timer countdown that happens starting from state initialization.
An example, see selected answer here:
Flutter Countdown Timer
Documentation:
https://api.flutter.dev/flutter/dart-async/Timer/Timer.periodic.html
I'm using a RestartableTimer (subclass of Timer) as a countdown timer, to "kick people out" of a form after a certain duration.
I would like to display the progress of that timer, and I like the idea of a circular progress slowly filling up.
I'm not showing my code because I don't really have anything to show. I have a completely static progress indicator and a working timer, in a widget (stateful or stateless, whichever works best).
I face two issues and this is where I need help for :
I don't know how to check every x milliseconds for the timer progress. How can I do that? I don't need copy-pasta code, but more of "what object / which direction" should I go for?
The timer progress in ticks is not implemented (NotImplementedException) ; is there any way to have an equivalent somewhere else? That object works really well for me, except for that part.
Am I SOL or is there a way to make it?
There's nothing to be implemented in the getter tick, since RestartableTimer is not periodic. What you want is a much more complex thing, and RestartableTimer is not able to help you with that.
First, you need something to control the progress of the CircularProgressIndicator:
class ProgressController {
static const double smoothnessConstant = 250;
final Duration duration;
final Duration tickPeriod;
Timer _timer;
Timer _periodicTimer;
Stream<void> get progressStream => _progressController.stream;
StreamController<void> _progressController = StreamController<void>.broadcast();
Stream<void> get timeoutStream => _timeoutController.stream;
StreamController<void> _timeoutController = StreamController<void>.broadcast();
double get progress => _progress;
double _progress = 0;
ProgressController({#required this.duration})
: assert(duration != null),
tickPeriod = _calculateTickPeriod(duration);
void start() {
_timer = Timer(duration, () {
_cancelTimers();
_setProgressAndNotify(1);
_timeoutController.add(null);
});
_periodicTimer = Timer.periodic(
tickPeriod,
(Timer timer) {
double progress = _calculateProgress(timer);
_setProgressAndNotify(progress);
},
);
}
void restart() {
_cancelTimers();
start();
}
Future<void> dispose() async {
await _cancelStreams();
_cancelTimers();
}
double _calculateProgress(Timer timer) {
double progress = timer.tick / smoothnessConstant;
if (progress > 1) return 1;
if (progress < 0) return 0;
return progress;
}
void _setProgressAndNotify(double value) {
_progress = value;
_progressController.add(null);
}
Future<void> _cancelStreams() async {
if (!_progressController.isClosed) await _progressController.close();
if (!_timeoutController.isClosed) await _timeoutController.close();
}
void _cancelTimers() {
if (_timer?.isActive == true) _timer.cancel();
if (_periodicTimer?.isActive == true) _periodicTimer.cancel();
}
static Duration _calculateTickPeriod(Duration duration) {
double tickPeriodMs = duration.inMilliseconds / smoothnessConstant;
return Duration(milliseconds: tickPeriodMs.toInt());
}
}
Then you can implement a CircularProgressIndicator that listens to the Streams from ProgressController:
class RestartableCircularProgressIndicator extends StatefulWidget {
final ProgressController controller;
final VoidCallback onTimeout;
RestartableCircularProgressIndicator({
Key key,
#required this.controller,
this.onTimeout,
}) : assert(controller != null),
super(key: key);
#override
_RestartableCircularProgressIndicatorState createState() =>
_RestartableCircularProgressIndicatorState();
}
class _RestartableCircularProgressIndicatorState
extends State<RestartableCircularProgressIndicator> {
ProgressController get controller => widget.controller;
VoidCallback get onTimeout => widget.onTimeout;
#override
void initState() {
super.initState();
controller.progressStream.listen((_) => updateState());
controller.timeoutStream.listen((_) => onTimeout());
}
#override
Widget build(BuildContext context) {
return CircularProgressIndicator(
value: controller.progress,
);
}
void updateState() => setState(() {});
}
You can also pass some of the paramers of CircularProgressIndicator to RestartableCircularProgressIndicator, so you can customize it.
A usage example:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ProgressController controller;
#override
void initState() {
super.initState();
controller = ProgressController(
duration: Duration(seconds: 5),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RestartableCircularProgressIndicator(
controller: controller,
onTimeout: () => print('timeout'),
),
RaisedButton(
onPressed: controller.start,
child: Text('Start'),
),
RaisedButton(
onPressed: controller.restart,
child: Text('Restart'),
),
],
),
),
),
);
}
}
I'll convert this into a library someday, but until then I cannot provide the tests and documentation to this code, so you have to study it if you want to understand what's going on here (I'm sorry...).
I have a analog clock which shows current time. I have wrapped the clock dial with flutter Gestures. after wrapping the clock dial with I get the co-ordinates of the clock by dragging inside the clock dial. I can able drag clock hour and minute hand but the problem is both the hour and minute hand rotates same time while dragging. I would like to move hour and minute hand separately.
My code looks like this
class RedTimerDial extends StatefulWidget {
//timer dial parameters, constructors,... goes here
#override
_RedTimerDialState createState() => _RedTimerDialState();
}
class _RedTimerDialState extends State<RedTimerDial> {
#override
Widget build(BuildContext context) {
return new DialTurnGestureDetector(
// here goes dial container code
//I call the clock from here(as a child)
new ClockHands(rotationPercent: _rotationPercent(),),
);
}
}
My DialTurnGestureDetector looks like this
class DialTurnGestureDetector extends StatefulWidget {
// here goes gesuture detecotrs parameters and constructors
#override
_DialTurnGestureDetectorState createState() => _DialTurnGestureDetectorState();
}
class _DialTurnGestureDetectorState extends State<DialTurnGestureDetector> {
PolarCoord startDragCoord;
Duration startDragTime;
_onRadialDragStart(PolarCoord coord){
startDragCoord = coord;
startDragTime = widget.currentTime;
}
_onRadialDragUpdate(PolarCoord coord){
/*
final coordination = new SnackBar(content: new Text('Radial Drag Coord: $coord'));
Scaffold.of(context).showSnackBar(coordination);
print('Radial Drag Coord: $coord'); */
if (startDragCoord != null){
final angleDiff = coord.angle - startDragCoord.angle;
final anglePercent = angleDiff / (2*pi);
final timeDiffInSeconds = (anglePercent * widget.maxTime.inSeconds).round();
// final newTime = new Duration(seconds: widget.currentTime.inSeconds + timeDiffInSeconds);
final newTime = new Duration(seconds: startDragTime.inSeconds + timeDiffInSeconds);
print('New Time: ${newTime.inMinutes}');
widget.onTimeSelected(newTime);
}
}
_onRadialDragEnd(){
}
#override
Widget build(BuildContext context) {
return new RadialDragGestureDetector(
onRadialDragStart: _onRadialDragStart,
onRadialDragUpdate: _onRadialDragUpdate,
onRadialDragEnd: _onRadialDragEnd,
child:widget.child,
);
}
}
My clock hands class looks like this
class ClockHands extends StatefulWidget {
// here goes some declarations and constructors
#override
_ClockHandsState createState() => _ClockHandsState();
}
class _ClockHandsState extends State<ClockHands> {
#override
Widget build(BuildContext context) {
return new AspectRatio(
// here goes some clock hands container code
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
new CustomPaint( painter: new HourHandPainter(
rotationPercent: widget.rotationPercent,
// rotationPercent: 0.00,
hours: dateTime.hour, minutes: dateTime.minute),
),
new CustomPaint(painter: new MinuteHandPainter(
rotationPercent: widget.rotationPercent,
minutes: dateTime.minute, seconds: dateTime.second),
),
new CustomPaint(painter: new SecondHandPainter(seconds: dateTime.second),
),
]
)
);
}
}