I want to make a really basic app. Just load a website and refresh every let's say 1 hour. I use webview, and i've seen i can do this with timer. My webview code:
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
Timer timer;
void main() => runApp(WebViewExample());
class WebViewExample extends StatefulWidget {
#override
WebViewExampleState createState() => WebViewExampleState();
}
class WebViewExampleState extends State<WebViewExample> {
#override
void initState() {
super.initState();
// Enable hybrid composition.
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
#override
Widget build(BuildContext context) {
return WebView(
initialUrl: 'https://mywebsite.com',
);
}
}
timer(){
Timer _timer = new Timer.periodic(
oneDecimal,
(Timer timer) =>
setState(() {
if (_start < 100) {
_timer.cancel();
} else {
_start = _start - 100;
callRefresh();
}
}));
}
void initState() {
super.initState();
timer();
}
Related
i have a custom hook timer_hook.dart
The way this hooks works is whenever the user open's up the page, the counter begins to count down, so i am trying to access the method "restartTimer()"
from the main class where i am using the hook. but i dont know how to achieve that, sorry i am still learning flutter. and i don't want to use the stateful widget.
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Duration useCountDownTimer() {
return use(const CountdownTimer());
}
class CountdownTimer extends Hook<Duration> {
const CountdownTimer();
#override
_CountdownTimerState createState() => _CountdownTimerState();
}
class _CountdownTimerState extends HookState<Duration, CountdownTimer> {
Timer? _timer;
Duration _duration = const Duration(seconds: 6);
void countDownTime() {
const reduceSeconds = 1;
setState(() {
final seconds = _duration.inSeconds - reduceSeconds;
_duration = Duration(seconds: seconds);
if (_duration.inSeconds == 0) {
_timer?.cancel();
}
});
}
void restartTimer() {
setState(() {
_duration = const Duration(seconds: 90);
_timer =
Timer.periodic(const Duration(seconds: 1), (_) => countDownTime());
});
}
#override
void initHook() {
super.initHook();
_timer = Timer.periodic(const Duration(seconds: 1), (_) => countDownTime());
}
#override
Duration build(BuildContext context) {
return _duration;
}
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
}
How can I monitor the life cycle states of the app from a particular page using HookWidget the way you can with a Stateful widget?
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
...
}
if (state == AppLifecycleState.resumed) {
...
}
if (state == AppLifecycleState.detached) {
...
}
}
First make a class:
class MyObserver implements WidgetsBindingObserver {
}
Then create it and register it with:
Widget build(BuildContext) {
useEffect(() {
final observer = MyObserver();
WidgetsBinding.instance.addObserver(observer);
return () => WidgetsBinding.instance.removeObserver(observer);
}, const []);
...
}
Flutter hooks is shipped with an inbuilt didchangeapplifecycle
access it as follows
final appLifecycleState = useAppLifecycleState();
useEffect(() {
print("current app state");
print(appLifecycleState);
if (appLifecycleState == AppLifecycleState.paused || appLifecycleState == AppLifecycleState.inactive) {
//...
} else if (appLifecycleState == AppLifecycleState.resumed) {
//...
}
return null;
}, [appLifecycleState]);
In the docs here search for "ways to create a hook". You'll see there are 2 ways of creating a hook, using a function or using a class. You are going for the "using a class" one. Then use initHook override as your initState and dispose works the same. Thats how I implemented it on my end.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
useWidgetLifecycleObserver(BuildContext context) {
return use(const _WidgetObserver());
}
class _WidgetObserver extends Hook<void> {
const _WidgetObserver();
#override
HookState<void, Hook<void>> createState() {
return _WidgetObserverState();
}
}
class _WidgetObserverState extends HookState<void, _WidgetObserver> with WidgetsBindingObserver {
#override
void build(BuildContext context) {}
#override
void initHook() {
super.initHook();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
print("app state now is $state");
super.didChangeAppLifecycleState(state);
}
}
Then
class Root extends HookWidget {
#override
Widget build(BuildContext context) {
useWidgetLifecycleObserver(context);
I've just had to deal with the same problem. And here is my solution using custom hooks:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
AppLifecycleState useAppLifecycleState() {
return use(const _LifeCycleState());
}
class _LifeCycleState extends Hook<AppLifecycleState> {
const _LifeCycleState();
#override
__LifeCycleState createState() => __LifeCycleState();
}
class __LifeCycleState extends HookState<AppLifecycleState, _LifeCycleState>
with WidgetsBindingObserver {
AppLifecycleState _theState;
#override
void initHook() {
super.initHook();
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
setState(() {
_theState = state;
});
}
#override
AppLifecycleState build(BuildContext context) {
return _theState;
}
#override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
}
And in the HookWidget that you want to access the app lifecycle state use the useEffect :
final appLifecycleState = useAppLifecycleState();
useEffect(() {
print("current app state");
print(appLifecycleState);
if (appLifecycleState == AppLifecycleState.paused ||
appLifecycleState == AppLifecycleState.inactive) {
//...
} else if (appLifecycleState == AppLifecycleState.resumed) {
//...
}
return null;
}, [appLifecycleState]);
Appreciate your time,so in this code i included the print statements to see what is going on....
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
void main() {
return runApp(
Refresher(),
);
}
class Refresher extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _RefresherState();
}
}
class _RefresherState extends State<Refresher> {
Timer timer;
#override
void initState() {
super.initState();
timer = Timer.periodic(
Duration(hours: 24),
(t) => setState(() {}),
);
}
#override
void dispose() {
timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
print('im the refresher');
return ScreenTimer();
}
}
class ScreenTimer extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ScreenTimerState();
}
}
class _ScreenTimerState extends State<ScreenTimer> {
_ScreenTimerState(){
print('state called');
}
Timer timer;
Future<List<String>> runningShow;
#override
void initState() {
super.initState();
// Reset urls every 24hrs
print('im screen timer ');
runningShow = _getScreen1();
timer = Timer.periodic(
Duration(seconds: 30),
(t) => setState(
() {},
),
);
}
#override
void dispose() {
timer.cancel();
super.dispose();
}
}
This is my output
I/flutter ( 4584): im the refresher
I/flutter ( 4584): state called
I/flutter ( 4584): im screen timer
I/Choreographer( 4584): The application may be doing too much work on its main thread.
I/flutter ( 4584): im the refresher
I/flutter ( 4584): im the refresher
Is there a way i can get the initstate called again each time the class is recalled?because i need the _getscreen(//this function checks database for any changes) recalled after 24hrs...
Simply move create a new method that updates you member variable
void _updateShowRunning () {
runningShow = _getScreen1();
}
and now call that method in initState and periodically.
_updateShowRunning();
timer = Timer.periodic(
Duration(seconds: 30),
(t) => setState(
_updateShowRunning,
),
);
I've been developing an application for both iOS and Android.
The code below makes real-time screen which shows current time.
1.I confirmed that DateTime.now() is working on both OS.
2.Also i confirmed that it is working at the actual Android device and Android emulator.
But everytime when i am trying to test on iOS(both emulator and actual device), _timeString always get a null.
What is wrong with this code? I don't get it.
Here is my environment in advance for solving my question.
VMware workstation 15 Player, Xcode 10.3, Android Studio 3.4.2
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:intl/intl.dart';
class TableLayout extends StatefulWidget {
#override
_TableLayoutState createState() => _TableLayoutState();
}
class _TableLayoutState extends State<TableLayout> {
String _timeString;
String _formatDateTime(DateTime dateTime) {
return DateFormat('MM/dd/yyyy HH:mm:ss').format(dateTime);
}
#override
void initState() {
_timeString = _formatDateTime(DateTime.now());
Timer.periodic(Duration(milliseconds: 1000), (Timer t) => _getTime());
super.initState();
}
void _getTime() {
DateTime _now = DateTime.now();
final String formattedDateTime = _formatDateTime(_now);
if (this.mounted) {
setState(() {
_timeString = formattedDateTime;
});
}
}
#override
Widget build(BuildContext context) {
return
Scaffold(
body:
Container(
child:
Text("\n\n\n\n${_timeString}")
)
);
}
}
Try this by changing date format
To
MM/dd/yyyy hh:mm:ss
I ran your code without any change. (Added a entry point however)
and it works as expected.
Any additional information regarding the intl package will be helpful. Here is my entire app and the result of that below.
My dependency is intl: ^0.15.8.
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:intl/intl.dart';
void main()
{
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
home: TableLayout(),
);
}
}
class TableLayout extends StatefulWidget {
#override
_TableLayoutState createState() => _TableLayoutState();
}
class _TableLayoutState extends State<TableLayout> {
String _timeString;
String _formatDateTime(DateTime dateTime) {
return DateFormat('MM/dd/yyyy HH:mm:ss').format(dateTime);
}
#override
void initState() {
_timeString = _formatDateTime(DateTime.now());
Timer.periodic(Duration(milliseconds: 1000), (Timer t) => _getTime());
super.initState();
}
void _getTime() {
DateTime _now = DateTime.now();
final String formattedDateTime = _formatDateTime(_now);
if (this.mounted) {
setState(() {
_timeString = formattedDateTime;
});
}
}
#override
Widget build(BuildContext context) {
return
Scaffold(
body:
Container(
child:
Text("\n\n\n\n${_timeString}")
)
);
}
}
App running on IOS simulator
I want to log a user out after a specific amount time the user has not interacted with the app.
I've wrapped the whole child widget in GestureDetector().
Please suggest if this is the best optimised way of doing this.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () {
// duration reset's to a specific time
startTimeout([int milliseconds]) { return new Timer(duration, handleTimeout); }
},
child: new HomeWidget(),);
}
void handleTimeOut {
// Log User Out
}
}
You should cancel previous timers before initializing a new one
static Timer _sessionTimer;
#override
Widget build(BuildContext context) {
...
onTap: () {
_sessionTimer?.cancel();
// duration reset's to a specific time
_sessionTimer = new Timer(duration, handleTimeout);
},
If you need something for the web target then better setup a key-up and a mouse-click listener on your index.html's 'body' as follows.
...
<body id = 'myapp-main-content'>
...
Then implement the listeners, here is an example borrowed from Task Tracker (https://github.com/botorabi/TaskTracker/tree/master/src/flutter-app/TaskTracker/lib).
import 'dart:async';
import 'dart:html';
import 'package:TaskTracker/service/authstatus.dart';
import 'package:flutter/material.dart';
import 'config.dart';
import 'navigation.links.dart';
import 'service/service.login.dart';
/// Logout user after long inactivity period.
class SessionTimeoutHandler {
static const MAIN_CONTAINER_ID = 'myapp-main-content';
final GlobalKey<NavigatorState> _navigator;
Timer _sessionTimer;
int _timeoutInSeconds;
static DateTime _timeLeft;
SessionTimeoutHandler(this._navigator, this._timeoutInSeconds);
void installLogoutHandler() {
var body = document.getElementById(MAIN_CONTAINER_ID);
body.addEventListener("click", (event) => resetLogoutTimer());
body.addEventListener("keyup", (event) => resetLogoutTimer());
resetLogoutTimer();
}
/// Return the time left to logout in seconds.
/// If user is not authenticated then 0 is returned.
static int timeLeftInSeconds() {
if ((_timeLeft == null) || !Config.authStatus.authenticated) {
return 0;
}
return ((DateTime.now().millisecondsSinceEpoch - _timeLeft.millisecondsSinceEpoch) / 1000).floor();
}
void resetLogoutTimer() {
_timeLeft = DateTime.now();
_sessionTimer?.cancel();
_sessionTimer = Timer(Duration(seconds: _timeoutInSeconds), _logout);
}
void _logout() {
if (Config.authStatus.authenticated) {
ServiceLogin().logoutUser().then((result) {
Config.authStatus = AuthStatus();
_navigator.currentState.pushNamedAndRemoveUntil(
NavigationLinks.NAV_HOME, (Route<dynamic> route) => false);
});
}
}
}
Then use the SessionTimeoutHandler above in your main widget setup (see initState below).
class AppTaskTracker extends StatefulWidget {
#override
_AppTaskTrackerState createState() => _AppTaskTrackerState();
}
class _AppTaskTrackerState extends State<AppTaskTracker> {
final GlobalKey<NavigatorState> _navigator = GlobalKey<NavigatorState>();
#override
void initState() {
super.initState();
SessionTimeoutHandler(_navigator, Config.LOGOUT_TIMEOUT).installLogoutHandler();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
...
Take into account that SessionTimeoutHandler gets the navigator in order to redirect to home after automatic logout.