diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index b6ffe5bd..ec226a5c 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -28,6 +28,10 @@
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
+
diff --git a/android/app/src/main/kotlin/com/ccextractor/taskwarriorflutter/TaskWarriorWidgetProvider.kt b/android/app/src/main/kotlin/com/ccextractor/taskwarriorflutter/TaskWarriorWidgetProvider.kt
index d57d4cb0..5eeb70ec 100644
--- a/android/app/src/main/kotlin/com/ccextractor/taskwarriorflutter/TaskWarriorWidgetProvider.kt
+++ b/android/app/src/main/kotlin/com/ccextractor/taskwarriorflutter/TaskWarriorWidgetProvider.kt
@@ -1,239 +1,237 @@
package com.ccextractor.taskwarriorflutter
+
import android.annotation.TargetApi
+import android.app.PendingIntent
import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
import android.content.Context
+import android.content.Intent
import android.net.Uri
+import android.os.Build
import android.widget.RemoteViews
-import es.antonborri.home_widget.HomeWidgetBackgroundIntent
+import android.widget.RemoteViewsService
import es.antonborri.home_widget.HomeWidgetLaunchIntent
-import es.antonborri.home_widget.HomeWidgetProvider
import es.antonborri.home_widget.HomeWidgetPlugin
+import org.json.JSONArray as OrgJSONArray
import org.json.JSONException
-import android.content.Intent
-import android.widget.RemoteViewsService
import org.json.JSONObject
-import org.json.JSONArray as OrgJSONArray
-import android.os.Bundle
-import android.app.PendingIntent
-import android.appwidget.AppWidgetProvider
-import android.os.Build
-
@TargetApi(Build.VERSION_CODES.CUPCAKE)
class TaskWarriorWidgetProvider : AppWidgetProvider() {
- override fun onReceive(context: Context, intent: Intent) {
- // Handle the custom action from your Widget buttons/list
- if (intent.action == "TASK_ACTION") {
- val uuid = intent.getStringExtra("uuid") ?: ""
- val launchedFor = intent.getStringExtra("launchedFor")
-
- // 1. Construct the URI exactly as Flutter expects it
- // Scheme: taskwarrior://
- // Host: cardclicked OR addclicked
- val deepLinkUri = if (launchedFor == "ADD_TASK") {
- Uri.parse("taskwarrior://addclicked")
- } else {
- // For list items, we attach the UUID
- Uri.parse("taskwarrior://cardclicked?uuid=$uuid")
- }
-
- // 2. Create the Intent to open MainActivity
- val launchIntent = Intent(context, MainActivity::class.java).apply {
- action = Intent.ACTION_VIEW
- data = deepLinkUri
- // These flags ensure the app opens correctly whether running or not
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
- }
-
- context.startActivity(launchIntent)
- }
- super.onReceive(context, intent)
- }
- fun getLayoutId(context: Context) : Int{
- val sharedPrefs = HomeWidgetPlugin.getData(context)
- val theme = sharedPrefs.getString("themeMode", "")
- val layoutId = if (theme.equals("dark")) {
- R.layout.taskwarrior_layout_dark // Define a dark mode layout in your resources
- } else {
- R.layout.taskwarrior_layout
- }
- return layoutId
- }
-@TargetApi(Build.VERSION_CODES.DONUT)
-override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
- appWidgetIds.forEach { widgetId ->
- // 1. Get the latest data from HomeWidget/SharedPrefs
+ fun getLayoutId(context: Context): Int {
val sharedPrefs = HomeWidgetPlugin.getData(context)
- val tasks = sharedPrefs.getString("tasks", "")
-
- // 2. Create the Intent for the ListView service
- // We add the widgetId to the data URI to make it unique, preventing caching issues
- val intent = Intent(context, ListViewRemoteViewsService::class.java).apply {
- putExtra("tasksJsonString", tasks)
- data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME) + widgetId)
- }
-
- // 3. Initialize RemoteViews with the THEMED layout (getLayoutId handles dark/light logic)
- val views = RemoteViews(context.packageName, getLayoutId(context)).apply {
-
- // Set up the Logo click (Open App)
- val pendingIntent: PendingIntent = HomeWidgetLaunchIntent.getActivity(
- context,
- MainActivity::class.java
- )
- setOnClickPendingIntent(R.id.logo, pendingIntent)
-
- // Set up the Add Button click (Custom Action)
- val intent_for_add = Intent(context, TaskWarriorWidgetProvider::class.java).apply {
- action = "TASK_ACTION"
- putExtra("launchedFor", "ADD_TASK")
- // Unique data to ensure the broadcast is fresh
- data = Uri.parse("taskwarrior://addtask/$widgetId")
- }
-
- val pendingIntentAdd: PendingIntent = PendingIntent.getBroadcast(
- context,
- widgetId,
- intent_for_add,
- PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- )
- setOnClickPendingIntent(R.id.add_btn, pendingIntentAdd)
-
- // Attach the adapter to the ListView
- setRemoteAdapter(R.id.list_view, intent)
- }
-
- // 4. Set up the Click Template for List Items (Deep Linking)
- val clickPendingIntent: PendingIntent = Intent(
- context,
- TaskWarriorWidgetProvider::class.java
- ).run {
- action = "TASK_ACTION"
- // Important: Use widgetId as requestCode to keep it unique
- PendingIntent.getBroadcast(
- context,
- widgetId,
- this,
- PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- )
+ val theme = sharedPrefs.getString("themeMode", "")
+ val layoutId =
+ if (theme.equals("dark")) {
+ R.layout.taskwarrior_layout_dark // Define a dark mode layout in your resources
+ } else {
+ R.layout.taskwarrior_layout
+ }
+ return layoutId
+ }
+ @TargetApi(Build.VERSION_CODES.DONUT)
+ override fun onUpdate(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray
+ ) {
+ appWidgetIds.forEach { widgetId ->
+ // 1. Get the latest data from HomeWidget/SharedPrefs
+ val sharedPrefs = HomeWidgetPlugin.getData(context)
+ val tasks = sharedPrefs.getString("tasks", "")
+
+ // 2. Create the Intent for the ListView service
+ // We add the widgetId to the data URI to make it unique, preventing caching issues
+ val intent =
+ Intent(context, ListViewRemoteViewsService::class.java).apply {
+ putExtra("tasksJsonString", tasks)
+ data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME) + widgetId)
+ }
+
+ // 3. Initialize RemoteViews with the THEMED layout (getLayoutId handles dark/light
+ // logic)
+ val views =
+ RemoteViews(context.packageName, getLayoutId(context)).apply {
+
+ // Set up the Logo click (Open App)
+ val pendingIntent: PendingIntent =
+ HomeWidgetLaunchIntent.getActivity(
+ context,
+ MainActivity::class.java
+ )
+ setOnClickPendingIntent(R.id.logo, pendingIntent)
+
+ // Set up the Add Button click (Direct to MainActivity)
+ val intentForAdd =
+ Intent(context, MainActivity::class.java).apply {
+ action = Intent.ACTION_VIEW
+ data = Uri.parse("taskwarrior://addclicked")
+ flags =
+ Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_CLEAR_TOP or
+ Intent.FLAG_ACTIVITY_SINGLE_TOP
+ }
+
+ val pendingIntentAdd: PendingIntent =
+ PendingIntent.getActivity(
+ context,
+ widgetId,
+ intentForAdd,
+ PendingIntent.FLAG_IMMUTABLE or
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ setOnClickPendingIntent(R.id.add_btn, pendingIntentAdd)
+
+ // Attach the adapter to the ListView
+ setRemoteAdapter(R.id.list_view, intent)
+ }
+
+ // 4. Set up the Click Template for List Items (Deep Linking)
+ val clickIntentTemplate =
+ Intent(context, MainActivity::class.java).apply {
+ action = Intent.ACTION_VIEW
+ flags =
+ Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_CLEAR_TOP or
+ Intent.FLAG_ACTIVITY_SINGLE_TOP
+ }
+ val clickPendingIntentTemplate: PendingIntent =
+ PendingIntent.getActivity(
+ context,
+ widgetId,
+ clickIntentTemplate,
+ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ views.setPendingIntentTemplate(R.id.list_view, clickPendingIntentTemplate)
+
+ // 5. THE THEME FIX: Notify the manager that the list data/layout needs a refresh
+ appWidgetManager.notifyAppWidgetViewDataChanged(widgetId, R.id.list_view)
+
+ // 6. Push the update to the widget
+ appWidgetManager.updateAppWidget(widgetId, views)
}
- views.setPendingIntentTemplate(R.id.list_view, clickPendingIntent)
-
- // 5. THE THEME FIX: Notify the manager that the list data/layout needs a refresh
- appWidgetManager.notifyAppWidgetViewDataChanged(widgetId, R.id.list_view)
-
- // 6. Push the update to the widget
- appWidgetManager.updateAppWidget(widgetId, views)
+ super.onUpdate(context, appWidgetManager, appWidgetIds)
}
- super.onUpdate(context, appWidgetManager, appWidgetIds)
-} }
+}
+
class ListViewRemoteViewsFactory(
- private val context: Context,
- private val tasksJsonString: String?
+ private val context: Context,
+ private val tasksJsonString: String?
) : RemoteViewsService.RemoteViewsFactory {
private val tasks = mutableListOf()
- override fun onCreate() {}
-
- override fun onDataSetChanged() {
- tasks.clear() // Add this!
- val sharedPrefs = HomeWidgetPlugin.getData(context)
- val latestTasksJson = sharedPrefs.getString("tasks", "")
-
- if (!latestTasksJson.isNullOrEmpty()) {
- try {
- val jsonArray = OrgJSONArray(latestTasksJson)
- for (i in 0 until jsonArray.length()) {
- tasks.add(Task.fromJson(jsonArray.getJSONObject(i)))
- }
- } catch (e: JSONException) {
- e.printStackTrace()
- }
- }
- }
-
- override fun onDestroy() {}
+ override fun onCreate() = Unit
+
+ override fun onDataSetChanged() {
+ val newTasks = mutableListOf()
+ val sharedPrefs = HomeWidgetPlugin.getData(context)
+ val latestTasksJson = sharedPrefs.getString("tasks", "")
+
+ if (!latestTasksJson.isNullOrEmpty()) {
+ try {
+ val jsonArray = OrgJSONArray(latestTasksJson)
+ for (i in 0 until jsonArray.length()) {
+ newTasks.add(Task.fromJson(jsonArray.getJSONObject(i)))
+ }
+ } catch (e: JSONException) {
+ e.printStackTrace()
+ }
+ }
+ // Atomic swap
+ tasks.clear()
+ tasks.addAll(newTasks)
+ }
+
+ override fun onDestroy() = Unit
override fun getCount(): Int = tasks.size
- fun getListItemLayoutId(): Int{
- val sharedPrefs = HomeWidgetPlugin.getData(context)
- val theme = sharedPrefs.getString("themeMode", "")
- val layoutId = if (theme.equals("dark")) {
- R.layout.listitem_layout_dark // Define a dark mode layout in your resources
- } else {
- R.layout.listitem_layout
- }
- return layoutId
- }
- fun getListItemLayoutIdForR1(): Int{
- val sharedPrefs = HomeWidgetPlugin.getData(context)
- val theme = sharedPrefs.getString("themeMode", "")
- val layoutId = if (theme.equals("dark")) {
- R.layout.no_tasks_found_li_dark // Define a dark mode layout in your resources
- } else {
- R.layout.no_tasks_found_li
- }
- return layoutId
- }
- fun getDotIdByPriority(p: String) : Int{
- println("PRIORITY: "+p)
- if(p.equals("L")) return R.drawable.low_priority_dot
- if(p.equals("M")) return R.drawable.mid_priority_dot
- if(p.equals("H")) return R.drawable.high_priority_dot
- return R.drawable.no_priority_dot
- }
+ fun getListItemLayoutId(): Int {
+ val sharedPrefs = HomeWidgetPlugin.getData(context)
+ val theme = sharedPrefs.getString("themeMode", "")
+ val layoutId =
+ if (theme.equals("dark")) {
+ R.layout.listitem_layout_dark // Define a dark mode layout in your resources
+ } else {
+ R.layout.listitem_layout
+ }
+ return layoutId
+ }
+ fun getListItemLayoutIdForR1(): Int {
+ val sharedPrefs = HomeWidgetPlugin.getData(context)
+ val theme = sharedPrefs.getString("themeMode", "")
+ val layoutId =
+ if (theme.equals("dark")) {
+ R.layout.no_tasks_found_li_dark // Define a dark mode layout in your resources
+ } else {
+ R.layout.no_tasks_found_li
+ }
+ return layoutId
+ }
+ fun getDotIdByPriority(p: String): Int {
+ if (p.equals("L")) return R.drawable.low_priority_dot
+ if (p.equals("M")) return R.drawable.mid_priority_dot
+ if (p.equals("H")) return R.drawable.high_priority_dot
+ return R.drawable.no_priority_dot
+ }
override fun getViewAt(position: Int): RemoteViews {
+ // Safe guard against Android out-of-bounds scrolling crashes
+ if (position !in tasks.indices) {
+ return RemoteViews(context.packageName, getListItemLayoutIdForR1()).apply {
+ setTextViewText(R.id.tv, "Loading...")
+ }
+ }
+
val task = tasks[position]
- if(task.uuid.equals("NO_TASK"))
- return RemoteViews(context.packageName, getListItemLayoutIdForR1()).apply {
- if(task.priority.equals("1"))
- setTextViewText(R.id.tv, "No tasks added yet")
- if(task.priority.equals("2"))
- setTextViewText(R.id.tv, "Filters applied are hiding all tasks")
- }
- return RemoteViews(context.packageName, getListItemLayoutId()).apply {
- setTextViewText(R.id.todo__title, task.title)
- setImageViewResource(R.id.dot, getDotIdByPriority(task.priority))
- val a = Intent().apply {
-
- Bundle().also { extras ->
- extras.putString("action", "show_task")
- extras.putString("uuid", tasks[position].uuid)
- putExtras(extras)
- }
-
- }
- setOnClickFillInIntent(R.id.list_item_container,a)
- }
-
+ if (task.uuid.equals("NO_TASK"))
+ return RemoteViews(context.packageName, getListItemLayoutIdForR1()).apply {
+ if (task.priority.equals("1")) setTextViewText(R.id.tv, "No tasks added yet")
+ if (task.priority.equals("2"))
+ setTextViewText(R.id.tv, "Filters applied are hiding all tasks")
+ }
+ return RemoteViews(context.packageName, getListItemLayoutId()).apply {
+ setTextViewText(R.id.todo__title, task.title)
+ setImageViewResource(R.id.dot, getDotIdByPriority(task.priority))
+ val fillInIntent =
+ Intent().apply {
+ data = Uri.parse("taskwarrior://cardclicked?uuid=${task.uuid}")
+ }
+ setOnClickFillInIntent(R.id.list_item_container, fillInIntent)
+ }
}
override fun getLoadingView(): RemoteViews? = null
- override fun getViewTypeCount(): Int = 2
+ override fun getViewTypeCount(): Int = 2
override fun getItemId(position: Int): Long = position.toLong()
override fun hasStableIds(): Boolean = true
}
+
class ListViewRemoteViewsService : RemoteViewsService() {
- override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
- val tasksJsonString = intent.getStringExtra("tasksJsonString")
- return ListViewRemoteViewsFactory(applicationContext, tasksJsonString)
- }
+ override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
+ val tasksJsonString = intent.getStringExtra("tasksJsonString")
+ return ListViewRemoteViewsFactory(applicationContext, tasksJsonString)
+ }
+}
+
+data class Task(
+ val title: String,
+ val urgencyLevel: String,
+ val uuid: String,
+ val priority: String
+) {
+ companion object {
+ fun fromJson(json: JSONObject): Task {
+ val title = json.optString("description", "")
+ val urgencyLevel = json.optString("urgency", "")
+ val uuid = json.optString("uuid", "")
+ val priority = json.optString("priority", "")
+ return Task(title, urgencyLevel, uuid, priority)
+ }
+ }
}
-data class Task(val title: String, val urgencyLevel: String,val uuid:String, val priority: String) {
- companion object {
- fun fromJson(json: JSONObject): Task {
- val title = json.optString("description", "")
- val urgencyLevel = json.optString("urgency", "")
- val uuid = json.optString("uuid","")
- val priority = json.optString("priority", "")
- return Task(title, urgencyLevel, uuid, priority)
- }
- }
-}
\ No newline at end of file
diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart
index 053f2bcc..3cf342f8 100644
--- a/lib/app/modules/home/controllers/home_controller.dart
+++ b/lib/app/modules/home/controllers/home_controller.dart
@@ -12,8 +12,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:taskwarrior/app/models/filters.dart';
import 'package:taskwarrior/app/models/json/task.dart';
-import 'package:taskwarrior/app/models/storage.dart';
import 'package:taskwarrior/app/models/storage/client.dart';
+import 'package:taskwarrior/app/models/storage.dart';
import 'package:taskwarrior/app/models/tag_meta_data.dart';
import 'package:taskwarrior/app/modules/home/controllers/widget.controller.dart';
import 'package:taskwarrior/app/modules/splash/controllers/splash_controller.dart';
@@ -70,6 +70,7 @@ class HomeController extends GetxController {
@override
void onInit() {
+ debugPrint("🚀 BOOT: HomeController.onInit()");
super.onInit();
storage = Storage(
Directory(
@@ -78,6 +79,7 @@ class HomeController extends GetxController {
);
serverCertExists = RxBool(storage.guiPemFiles.serverCertExists());
addListenerToScrollController();
+
_profileSet();
loadDelayTask();
initLanguageAndDarkMode();
@@ -127,9 +129,17 @@ class HomeController extends GetxController {
@override
void onReady() {
super.onReady();
- if (Get.isRegistered()) {
- Get.find().consumePendingActions(this);
- }
+ // Replaced 50ms delay with a secure PostFrameCallback
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (isClosed) return;
+
+ final deepLinkService = Get.find();
+ if (deepLinkService.queuedUri != null) {
+ debugPrint(
+ "🚀 TRACE: HomeController.onReady() consuming deferred queue!");
+ deepLinkService.consumePendingActions(this);
+ }
+ });
}
Future> getUniqueProjects() async {
@@ -577,8 +587,7 @@ class HomeController extends GetxController {
await synchronize(context, false);
}
if (context.mounted) {
- final tColors =
- Theme.of(context).extension()!;
+ final tColors = Theme.of(context).extension()!;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
diff --git a/lib/app/modules/splash/controllers/splash_controller.dart b/lib/app/modules/splash/controllers/splash_controller.dart
index 126aaebb..a126ef0e 100644
--- a/lib/app/modules/splash/controllers/splash_controller.dart
+++ b/lib/app/modules/splash/controllers/splash_controller.dart
@@ -13,6 +13,7 @@ import 'package:taskwarrior/app/routes/app_pages.dart';
import 'package:taskwarrior/app/utils/taskchampion/credentials_storage.dart';
import 'package:taskwarrior/app/utils/taskfunctions/profiles.dart';
import 'package:taskwarrior/app/v3/models/task.dart';
+import 'package:taskwarrior/app/services/deep_link_service.dart';
class SplashController extends GetxController {
late Rx baseDirectory = Directory('').obs;
@@ -22,15 +23,29 @@ class SplashController extends GetxController {
Profiles get _profiles => Profiles(baseDirectory.value);
@override
- void onInit() async {
+ void onInit() {
+ debugPrint("🚀 BOOT: SplashController.onInit()");
super.onInit();
+ }
+
+ @override
+ void onReady() async {
+ super.onReady();
+
+ await initBaseDir();
+ _checkProfiles();
+ profilesMap.value = _profiles.profilesMap();
+ currentProfile.value = _profiles.getCurrentProfile()!;
+
+ final deepLinkService = Get.find();
+ if (deepLinkService.queuedUri != null) {
+ debugPrint("🚀 TRACE: Bypassing Splash routing for queued URI");
+ Get.offNamed(Routes.HOME);
+ return;
+ }
+
await checkForUpdate();
- initBaseDir().then((_) {
- _checkProfiles();
- profilesMap.value = _profiles.profilesMap();
- currentProfile.value = _profiles.getCurrentProfile()!;
- sendToNextPage();
- });
+ sendToNextPage();
}
Future initBaseDir() async {
diff --git a/lib/app/services/deep_link_service.dart b/lib/app/services/deep_link_service.dart
index f016c93c..c80df04c 100644
--- a/lib/app/services/deep_link_service.dart
+++ b/lib/app/services/deep_link_service.dart
@@ -1,3 +1,4 @@
+import 'dart:async'; // Add this import at the top
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:app_links/app_links.dart';
@@ -7,35 +8,54 @@ import 'package:taskwarrior/app/routes/app_pages.dart';
class DeepLinkService extends GetxService {
late AppLinks _appLinks;
- Uri? _queuedUri;
+ String? _queuedUri; // Made private
+ String? get queuedUri => _queuedUri; // Added getter
+ StreamSubscription? _linkSubscription; // Added stream subscription
- @override
- void onReady() {
- super.onReady();
- _initDeepLinks();
- }
-
- void _initDeepLinks() {
+ Future init() async {
_appLinks = AppLinks();
- _appLinks.uriLinkStream.listen((uri) {
+
+ try {
+ final initialUri = await _appLinks.getInitialLink();
+ if (initialUri != null) {
+ _queuedUri = initialUri.toString();
+ debugPrint('🔗 INITIAL LINK QUEUED: $_queuedUri');
+ }
+ } catch (e) {
+ debugPrint('Deep link init error: $e');
+ }
+
+ _linkSubscription = _appLinks.uriLinkStream.listen((uri) {
debugPrint('🔗 LINK RECEIVED: $uri');
_handleWidgetUri(uri);
+ }, onError: (err) {
+ debugPrint('🔗 LINK STREAM ERROR: $err');
});
}
+ @override
+ void onClose() {
+ _linkSubscription?.cancel();
+ super.onClose();
+ }
+
void _handleWidgetUri(Uri uri) {
if (Get.isRegistered()) {
_executeAction(uri, Get.find());
} else {
debugPrint("⏳ HomeController not ready. Queuing action.");
- _queuedUri = uri;
+ _queuedUri = uri.toString();
}
}
void consumePendingActions(HomeController controller) {
if (_queuedUri != null) {
debugPrint("🚀 Executing queued action...");
- _executeAction(_queuedUri!, controller);
+ try {
+ _executeAction(Uri.parse(_queuedUri!), controller);
+ } catch (e) {
+ debugPrint("🔗 FAILED TO PARSE URI: $_queuedUri - Error: $e");
+ }
_queuedUri = null;
}
}
@@ -54,15 +74,17 @@ class DeepLinkService extends GetxService {
}
} else if (uri.host == "addclicked") {
if (Get.context != null) {
- Get.dialog(
- Material(
- child: AddTaskBottomSheet(
- homeController: controller,
- forTaskC: isTaskChampion,
- forReplica: isReplica,
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ Get.dialog(
+ Material(
+ child: AddTaskBottomSheet(
+ homeController: controller,
+ forTaskC: isTaskChampion,
+ forReplica: isReplica,
+ ),
),
- ),
- );
+ );
+ });
}
}
}
diff --git a/lib/main.dart b/lib/main.dart
index 09f0268c..16812cf3 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -3,8 +3,6 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
-// 1. Add this import
-import 'package:app_links/app_links.dart';
import 'package:taskwarrior/app/services/deep_link_service.dart';
import 'package:taskwarrior/app/utils/app_settings/app_settings.dart';
@@ -29,6 +27,9 @@ DynamicLibrary loadNativeLibrary() {
}
void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ // Move the logger override ABOVE the first boot print!
debugPrint = (String? message, {int? wrapWidth}) {
if (message != null) {
debugPrintSynchronously(message, wrapWidth: wrapWidth);
@@ -36,19 +37,32 @@ void main() async {
}
};
+ debugPrint("🚀 BOOT: main() started");
+
loadNativeLibrary();
await RustLib.init();
- WidgetsFlutterBinding.ensureInitialized();
await AppSettings.init();
- Get.put(DeepLinkService(), permanent: true);
+ // fix: Actually await the service initialization so the OS intent is caught BEFORE runApp.
+ await Get.putAsync(() async {
+ final service = DeepLinkService();
+ await service.init();
+ return service;
+ }, permanent: true);
runApp(
GetMaterialApp(
darkTheme: darkTheme,
theme: lightTheme,
title: "Application",
initialRoute: AppPages.INITIAL,
+ unknownRoute: AppPages.routes.firstWhere(
+ (page) => page.name == AppPages.INITIAL,
+ orElse: () {
+ debugPrint("⚠️ Unknown route requested, falling back to default");
+ return AppPages.routes.first;
+ },
+ ),
getPages: AppPages.routes,
themeMode: AppSettings.isDarkMode ? ThemeMode.dark : ThemeMode.light,
),