I have created an Android home screen widget with a button that executes Dart code (specifically, an API call) when clicked. Currently, this functionality only works when the app is in the background. I am looking to make it work even when the app is not in the background. Additionally, I prefer not to use any third-party plugins.
// Java code
public class NewAppWidget extends AppWidgetProvider {
public static String valueToBeDisplayed = "temperature_value";
private static boolean isAlarmStarted = false;
public static int no_of_widget=0;
PendingIntent pendingIntent;
public static FlutterEngine flutterEngine;
public static final String ACTION_AUTO_UPDATE = "AUTO_UPDATE";
public static NewAppWidget appwidgetObj = new NewAppWidget();
public static MethodChannel MethodChannel;
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId){
CharSequence widgetText = valueToBeDisplayed;
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
views.setTextViewText(R.id.appwidget_text, widgetText);
Intent buttonIntent = new Intent(context, NewAppWidget.class);
buttonIntent.setAction("com.example.weather_widget2.BUTTON_CLICK");
PendingIntent buttonPendingIntent = PendingIntent.getBroadcast(context, 0,
buttonIntent, PendingIntent.FLAG_IMMUTABLE);
views.setOnClickPendingIntent(R.id.buttonOk, buttonPendingIntent);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, NewAppWidget.class));
for (int w : appWidgetIds) {
appWidgetManager.updateAppWidget(w, views);
}
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
public void onReceive(Context context, Intent intent) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, NewAppWidget.class));
if (intent != null && Objects.equals(intent.getAction(), ACTION_AUTO_UPDATE)) {
System.out.println(valueToBeDisplayed + "----");
System.out.println("action auto update has received");
callFlutterMethod(context);
}else{
assert intent != null;
if("com.example.weather_widget2.BUTTON_CLICK".equals(intent.getAction())){
callFlutterMethod(context);
}
}
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
super.onReceive(context, intent);
}
@Override
public void onEnabled(Context context) {
no_of_widget++;
System.out.print("called onEnabled at present no fo widgets are "+no_of_widget);
super.onEnabled(context);
}
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
System.out.println("called on 7777777777777 Deleted at present no fo widgets are "+no_of_widget);
super.onDeleted(context, appWidgetIds);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
if (!isAlarmStarted) {
isAlarmStarted = true;
scheduleAlarm(context);
}
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
private static void callFlutterMethod(Context context) {
System.out.print("call method flutter method has been called");
if(flutterEngine==null){
System.out.print("again flutter cached++++++++++++++++++++++");
flutterEngine = FlutterEngineCache.getInstance().get("my_engine_id");
}
if(flutterEngine!=null)
{
if(MethodChannel==null) {
MethodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "my_channel123");
}
MethodChannel.setMethodCallHandler((call, result) -> {
if (call.method.equals("InvokedWidget")){
String arg=call.argument("message");
valueToBeDisplayed=arg;
result.success("sample text has been sent from flutter to android");
System.out.println(valueToBeDisplayed);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, NewAppWidget.class));
appwidgetObj.onUpdate(context,appWidgetManager,appWidgetIds);
}
});
MethodChannel.invokeMethod("requestLatestData", "");
}
else {
System.out.print("flutter engine has been null");
}
}
private void scheduleAlarm(Context context) {
System.out.println("schedule alarm method is triggered");
Intent intent = new Intent(context, NewAppWidget.class);
intent.setAction(ACTION_AUTO_UPDATE);
pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
long intervalMillis = 1800000;
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), intervalMillis, pendingIntent);
}
}
public class MainActivity extends FlutterActivity {
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine);
}
}
// Dart Code
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const channelName = 'my_channel123';
late MethodChannel methodChannel;
void configureChannel() {
methodChannel = const MethodChannel(channelName);
methodChannel.setMethodCallHandler(methodHandler);
}
List<dynamic> lst = [];
Future<void> apiCall() async {
apiData = await http.get(Uri.parse('http://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&hourly=temperature_2m'));
final body1 = apiData.body;
final json = jsonDecode(body1);
if (apiData.statusCode == 200) {
lst = json['hourly']['temperature_2m'];
}
setState(() {});
}
Future<void> methodHandler(MethodCall call) async {
switch (call.method) {
case "requestLatestData":
display();
break;
default:
print('no method handler for method ${call.method}');
}
}
@override
void initState() {
super.initState();
apiCall();
configureChannel();
}
dynamic apiData;
var channel = const MethodChannel("ChannelName");
dynamic result1;
int num = 0;
late String np;
Future<void> display() async {
np = lst[num].toString();
var dt = DateTime.now();
print("${dt.hour}:${dt.minute}:${dt.second}");
await methodChannel.invokeMethod("InvokedWidget", {'message': np});
setState(() {});
}
void updateData() {
np = lst[num].toString();
num++;
setState(() {});
}
@override
Widget build(BuildContext context) {
return lst.isEmpty
? Container(
color: Colors.blue,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
)
: Scaffold(
body: Center(
child: Container(
color: Colors.black,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Current Temperature",
textDirection: TextDirection.rtl,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 48, fontWeight: FontWeight.bold),
),
const SizedBox(
height: 30,
),
Text(
lst[num].toString(),
style: const TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.bold),
),
const SizedBox(
height: 100,
),
ElevatedButton(
onPressed: updateData,
child: const Text(
'Update Temperature',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
)
],
),
),
),
),
);
}
}
You can use
background_fetch
orworkmanager
package for implementing this type of case in your Flutter applicationSome basic example of both of them are mentioned below
The
background_fetch
package is commonly used for tasks that need to be performed periodically in the background, like updating content or fetching data from an API.The
workmanager
package is used for background tasks that need more precise control or need to be executed under certain conditions, like network availability or charging status.