0

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),
                        ),
                      )
                    ],
                  ),
                ),
              ),
            ),
          );
  }
}

0

1 Answer 1

0

You can use background_fetch or workmanager package for implementing this type of case in your Flutter application

Some basic example of both of them are mentioned below

  1. Background Fetch:

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.

void backgroundFetchHeadlessTask(String taskId) async {
  print('[BackgroundFetch] Headless event received.');
  // Perform your API call or other tasks here
  BackgroundFetch.finish(taskId);
}

void main() {
 runApp(MyApp());
 // Register to receive BackgroundFetch events after app is terminated.
 // Requires additional setup for iOS in AppDelegate.swift
 BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
  1. Workmanager

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.

   void callbackDispatcher() {
     Workmanager().executeTask((task, inputData) {
     print("Native called background task: $task");
     // Perform your API call or other tasks here
     return Future.value(true);
    });
   }

   void main() {
     Workmanager().initialize(
      callbackDispatcher, // The top level function, aka 
      callbackDispatcher
      isInDebugMode: true // If enabled it will post a notification 
      whenever the task is running. Handy for debugging tasks
     );
    Workmanager().registerOneOffTask(
       "1", 
       "simpleTask", 
       initialDelay: Duration(seconds: 10),
       constraints: Constraints(
       networkType: NetworkType.connected,
      )
     );
    runApp(MyApp());
   }
Sign up to request clarification or add additional context in comments.

1 Comment

Hey Ahmad hassan, I do not wish to use third party plugins/packages.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.