I have a function called control in the StateFull Widget. I want to run this function with WorkManager every 15 minutes.
How can I call the control function from the callbackDispatcher function?
I added a Stream statically to the Statefull widget and then listened to it but it didn't work.
HomeScreen.dart file
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
const taskKontrol = "control";
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Container();
}
#override
void initState() {
super.initState();
setupWorkManager();
}
void control() async
{
//... my code control is here
}
}
void setupWorkManager() async {
await Workmanager.initialize(callbackDispatcher, isInDebugMode: true);
Workmanager.registerPeriodicTask(taskKontrol, taskKontrol,
frequency: Duration(seconds: 10),
existingWorkPolicy: ExistingWorkPolicy.append
);
}
void callbackDispatcher() {
Workmanager.executeTask((taskName, inputData) async {
switch(taskName)
{
case taskKontrol:
// How can I call the control function from here?
print("control from workmanager");
break;
}
return Future.value(true);
});
}
For those who still looking for an answer:
From the official docs:
The callbackDispatcher needs to be either a static function or a top level function to be accessible as a Flutter entry point.
I had this same problem and I solved it by moving the function callbackDispatcher to the file: main.dart
Also, the code that initializes callbackDispatcher must be in main() before the App() widget loads.
To call your control code, create a class with static function control()
Note: You cannot call the widget's method from callbackDispatcher!
Reason: Widgets are UI bound. As long as the screen remains active, the widget that is visible remains active. Once you close the app or move on to next screen, the widgets' memory gets recycled. But this callbackDispatcher gets executed even when your app is closed. So, it has to be isolated from UI code.
Here's the code:
main.dart:
import 'package:flutter/material.dart';
import 'package:workmanager/workmanager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
runApp(App());
}
void callbackDispatcher() {
Workmanager.executeTask((taskName, inputData) async {
switch(taskName)
{
case ScheduledTask.taskName:
ScheduledTask.control(); // calls your control code
break;
}
return Future.value(true);
});
}
class ScheduledTask {
const static String taskName = "control";
static void control() {
// add your control here
}
}
All you can do from HomeScreen widget is to call setupWorkManager() that schedules the task
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Container();
}
#override
void initState() {
super.initState();
setupWorkManager();
}
}
void setupWorkManager() async {
Workmanager.registerPeriodicTask(taskKontrol, taskKontrol,
frequency: Duration(minutes: 15),
existingWorkPolicy: ExistingWorkPolicy.append
);
}
Note: The minimum frequency for the recurring task is 15 minutes
Related
I've started using flutter_bloc package instead of redux to try it out, but I'm not entirely sure how I'm going to call flutter bloc events when receiving things from native (Android/iOS). It was easier with redux because in my parent MyApp widget of my main.dart file, I passed in the redux store to a custom class I created, and dispatched methods from the said class (called MethodChannelHandler).
main.dart:
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final Store<AppState> store = Store<AppState>(
// ... redux stuff ...
);
#override
void initState() {
// sauce
MethodChannelHandler(store);
super.initState();
}
}
methodChannelHandler.dart:
class MethodChannelHandler {
Store<AppState> store;
MethodChannelHandler(this.store) {
methodChannel.setMethodCallHandler(_handleMethod);
}
// Handle method calls from native
Future _handleMethod(MethodCall call) async {
if (call.method == A_METHOD) {
store.dispatch("something from native")
}
}
}
NOTE: I'm inept when it comes to programming vocabulary so please, if possible, please give me a small snippet of example code like I have or link me to some GitHub repo I can refer to instead of giving me a block of text I'm probably not going to understand.
In very simple way it's look like this:
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider<SomeBloc>(
create: (_) {
final bloc = SomeBloc(); //Create bloc
MethodChannelHandler(bloc); //Add method handler
return bloc;
},
lazy: false,
child: Text("Content"),
);
}
}
class SomeBloc extends Bloc {
SomeBloc() : super(SomeInitState());
#override
Stream mapEventToState(event) async* {
if (event is SomeEvent) {
//Handle SomeEvent
}
}
}
class MethodChannelHandler {
final SomeBloc someBloc;
MethodChannelHandler(this.someBloc) {
methodChannel.setMethodCallHandler(_handleMethod);
}
// Handle method calls from native
Future _handleMethod(MethodCall call) async {
if (call.method == A_METHOD) {
someBloc.add(SomeEvent("something from native"));
}
}
}
I am receiving the error "Missing concrete implementation of State.build" when attempting to run this code for Angela Yu's FLutter course:
import 'package:flutter/material.dart';
import 'package:clima/services/location.dart';
import 'package:http/http.dart';
class LoadingScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _LoadingScreenState();
}
}
class _LoadingScreenState extends State<LoadingScreen> {
#override
void initState() {
super.initState();
getLocation();
}
}
void getLocation() async {
Location location = Location();
await location.getCurrentLocation();
print(location.latitude);
print(location.longitude);
}
void getData() async {
Response response = await get(
'https://samples.openweathermap.org/data/2.5/weather?lat=35&lon=139&appid=439d4b804bc8187953eb36d2a8c26a02');
print(response);
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
I have tried the responses related to this question:
missing concrete implementation of state.build
...but have not had any success. Any insight as to what I'm doing wrong would be very much appreciated.
enter code hereYou have a extra } after
#override
void initState() {
super.initState();
getLocation();
}
Delete it
And another missed above
#override
Widget build(BuildContext context) {
return Scaffold();
}
Check your { }
Check your {}. This error mainly comes due to the missing of {} or using extra {}.
Im doing a splashscreen first app, to be followed by a walkthrough page if the user first used the app, else go to a welcome page to sign in/ sign up if already saw the walkthrough screen.
My code came from this projects main.dart file: https://github.com/instaflutter/flutter-login-screen-firebase-auth-facebook-login and modified it to this code(from splashscreen tutorial FlutterKart)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:binder/ui/screens/root_screen.dart';
import 'package:binder/ui/screens/walk_screen.dart';
void main() {
Firestore.instance.settings(timestampsInSnapshotsEnabled: true);
SharedPreferences.getInstance().then((prefs) {
SplashScreen(prefs: prefs);
});
}
class SplashScreen extends StatefulWidget {
final SharedPreferences prefs;
SplashScreen({Key key,this.prefs}): super(key: key);
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
Timer(Duration(seconds: 3), () => _handleCurrentScreen(context));
}
#override
Widget build(BuildContext context) {
final logowhite = Hero(
tag: 'hero',
child: //code insert flutterkart splashscreen
)
],
),
)
],
)
],
),
);
}
Widget _handleCurrentScreen (BuildContext context) {
bool seen = (widget.prefs.getBool('seen') ?? false);
if (seen) {
return new RootScreen();
} else {
return new WalkthroughScreen(prefs: widget.prefs);
}
}
}
I want it to show the splashscreen first and directed to the rootscreen if already seen and to the walkthrough screen if first use.
You'd probably want to use shared_preferences or something similar. Something like this:
// add this static variable somewhere
// could technically be initialized during splash screen and added to a Provider or something similar after
static SharedPreferences prefs;
// make `main` async if it is not already
Future<void> main() async {
prefs = await SharedPreferences.getInstance();
...
}
Future<void> onSplashScreenDone() async {
if (prefs.getBool('isFirstTime') ?? true) {
// you might want to put this at the end of your walkthrough, so they don't miss it if they close the app, for example
await prefs.setBool('isFirstTime', false);
// this is their first time, show walkthrough, etc.
...
} else {
// this is not their first time, do normal things.
}
}
I am trying to build a countdown app for special days for example 11 d 2 h 30 m 23s to the new year but I can't reload my state every second so it just shows me the second that I loaded the page I don't know how to dynamically reload the page.
import 'package:flutter/material.dart';
class RopSayac extends StatefulWidget {
_RopSayacState createState() => _RopSayacState();
}
class _RopSayacState extends State<RopSayac> {
var now = DateTime.now().second.toString();
String asd(){
setState(() {
now = DateTime.now().second.toString();
});
return now;
}
#override
Widget build(BuildContext context) {
return Container(
child: new Text(asd()),
);
}
}
This is what I got and it doesn't reload time. I am pretty new on the flutter.
As pskink and Günter mentioned, use a Timer. You can even use the periodic constructor that would fit well your scenario.
Note you don't need the asd() function. When you call setState(), the build method will be called automatically passing the new now property value.
If you want, use initState to set an initial value and, as in this example, setup the Timer.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Timer Periodic Demo', home: RopSayac());
}
}
class RopSayac extends StatefulWidget {
_RopSayacState createState() => _RopSayacState();
}
class _RopSayacState extends State<RopSayac> {
String _now;
Timer _everySecond;
#override
void initState() {
super.initState();
// sets first value
_now = DateTime.now().second.toString();
// defines a timer
_everySecond = Timer.periodic(Duration(seconds: 1), (Timer t) {
setState(() {
_now = DateTime.now().second.toString();
});
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: new Text(_now),
),
);
}
}
This recursive method should be enough for what you want. The seconds set as 1 will keep triggering setState each second, thus refreshing your widget tree.
void _timer() {
Future.delayed(Duration(seconds: 1)).then((_) {
setState(() {
print("1 second closer to NYE!");
// Anything else you want
});
_timer();
});
}
There's this excellent library called timer_builder. I think it'll help you out.
Example from the pub page:
import 'package:timer_builder/timer_builder.dart';
class ClockWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return TimerBuilder.periodic(Duration(seconds: 1), //updates every second
builder: (context) {
return Text("${DateTime.now()}");
}
);
}
}
Here's what I do. In my case I'm polling a webservice in _liveUpdate()
void startUpdates(AppState appState) async {
await new Future.delayed(const Duration(milliseconds: 100));
while (true) {
_liveUpdate();
appState.setState(() {});
await new Future.delayed(const Duration(seconds : 15));
}
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.