Skip to content

Commit 8ee37ec

Browse files
committed
handling clicks on the widget
1 parent 6ee234e commit 8ee37ec

File tree

6 files changed

+102
-73
lines changed

6 files changed

+102
-73
lines changed

ios/Runner/AppDelegate.swift

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import UIKit
22
import Flutter
3-
import WidgetKit
43

54
@main
65
@objc class AppDelegate: FlutterAppDelegate {
@@ -9,72 +8,6 @@ import WidgetKit
98
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
109
) -> Bool {
1110
GeneratedPluginRegistrant.register(with: self)
12-
13-
// Register our widget UserDefaults plugin
14-
let controller = window?.rootViewController as! FlutterViewController
15-
let widgetChannel = FlutterMethodChannel(
16-
name: "com.ccextractor.taskwarriorflutter/widget",
17-
binaryMessenger: controller.binaryMessenger)
18-
19-
widgetChannel.setMethodCallHandler { (call, result) in
20-
if call.method == "saveToUserDefaults" {
21-
guard let args = call.arguments as? [String: Any],
22-
let key = args["key"] as? String,
23-
let groupId = args["groupId"] as? String else {
24-
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Missing required arguments", details: nil))
25-
return
26-
}
27-
28-
guard let sharedDefaults = UserDefaults(suiteName: groupId) else {
29-
result(FlutterError(code: "USERDEFAULTS_ERROR", message: "Could not access shared UserDefaults with groupId: \(groupId)", details: nil))
30-
return
31-
}
32-
33-
let isData = args["isData"] as? Bool ?? false
34-
35-
if isData, let value = args["value"] as? FlutterStandardTypedData {
36-
// Save binary data
37-
sharedDefaults.set(value.data, forKey: key)
38-
print("Successfully saved binary data for key: \(key) to group: \(groupId)")
39-
} else if let value = args["value"] as? String {
40-
// Save string value
41-
sharedDefaults.set(value, forKey: key)
42-
print("Successfully saved string for key: \(key) to group: \(groupId)")
43-
} else {
44-
result(FlutterError(code: "INVALID_VALUE", message: "Value must be either String or binary data", details: nil))
45-
return
46-
}
47-
48-
// Force synchronize to ensure data is written immediately
49-
sharedDefaults.synchronize()
50-
result(true)
51-
} else if call.method == "updateWidget" {
52-
#if os(iOS)
53-
if #available(iOS 14.0, *) {
54-
// Get the widget name to update
55-
let args = call.arguments as? [String: Any]
56-
let widgetName = args?["widgetName"] as? String
57-
58-
// Request widget update
59-
if widgetName != nil {
60-
WidgetCenter.shared.reloadTimelines(ofKind: widgetName!)
61-
print("iOS widget timeline reloaded for: \(widgetName!)")
62-
} else {
63-
WidgetCenter.shared.reloadAllTimelines()
64-
print("iOS widget timelines reloaded")
65-
}
66-
result(true)
67-
} else {
68-
result(FlutterError(code: "VERSION_ERROR", message: "Widgets are only available on iOS 14.0 and above", details: nil))
69-
}
70-
#else
71-
result(FlutterError(code: "PLATFORM_ERROR", message: "Not available on this platform", details: nil))
72-
#endif
73-
} else {
74-
result(FlutterMethodNotImplemented)
75-
}
76-
}
77-
7811
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
7912
}
80-
}
13+
}

ios/Runner/Info.plist

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5+
<key>CFBundleURLTypes</key>
6+
<array>
7+
<dict>
8+
<key>CFBundleTypeRole</key>
9+
<string>Editor</string>
10+
<key>CFBundleURLName</key>
11+
<string>taskwarrior</string>
12+
<key>CFBundleURLSchemes</key>
13+
<array>
14+
<string>taskwarrior</string>
15+
</array>
16+
</dict>
17+
</array>
518
<key>CADisableMinimumFrameDurationOnPhone</key>
619
<true/>
720
<key>CFBundleDevelopmentRegion</key>

lib/app/modules/home/controllers/home_controller.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import 'package:taskwarrior/app/modules/home/controllers/widget.controller.dart'
2020
import 'package:taskwarrior/app/modules/home/views/add_task_bottom_sheet_new.dart';
2121
import 'package:taskwarrior/app/modules/splash/controllers/splash_controller.dart';
2222
import 'package:taskwarrior/app/routes/app_pages.dart';
23+
import 'package:taskwarrior/app/services/deep_link_service.dart';
2324
import 'package:taskwarrior/app/services/tag_filter.dart';
2425
import 'package:taskwarrior/app/tour/filter_drawer_tour.dart';
2526
import 'package:taskwarrior/app/tour/home_page_tour.dart';
@@ -87,9 +88,6 @@ class HomeController extends GetxController {
8788
taskdb.open();
8889
getUniqueProjects();
8990
_loadTaskChampion();
90-
if (Platform.isAndroid || Platform.isIOS) {
91-
handleHomeWidgetClicked();
92-
}
9391
fetchTasksFromDB();
9492
ever(taskReplica, (_) {
9593
if (taskReplica.value) refreshReplicaTaskList();
@@ -107,7 +105,6 @@ class HomeController extends GetxController {
107105
if (Platform.isAndroid || Platform.isIOS) {
108106
WidgetController widgetController = Get.put(WidgetController());
109107
widgetController.fetchAllData();
110-
111108
widgetController.update();
112109
}
113110
});
@@ -124,6 +121,14 @@ class HomeController extends GetxController {
124121
});
125122
}
126123

124+
@override
125+
void onReady() {
126+
super.onReady();
127+
if (Get.isRegistered<DeepLinkService>()) {
128+
Get.find<DeepLinkService>().consumePendingActions(this);
129+
}
130+
}
131+
127132
Future<List<String>> getUniqueProjects() async {
128133
if (taskReplica.value) {
129134
return tasksFromReplica
@@ -807,7 +812,9 @@ class HomeController extends GetxController {
807812
void showAddDialogAfterWidgetClick() {
808813
Widget showDialog = Material(
809814
child: AddTaskBottomSheet(
810-
homeController: this, forTaskC: taskchampion.value));
815+
homeController: this,
816+
forTaskC: taskchampion.value,
817+
forReplica: taskReplica.value));
811818
Get.dialog(showDialog);
812819
}
813820
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:get/get.dart';
3+
import 'package:app_links/app_links.dart';
4+
import 'package:taskwarrior/app/modules/home/views/add_task_bottom_sheet_new.dart';
5+
import 'package:taskwarrior/app/modules/home/controllers/home_controller.dart';
6+
import 'package:taskwarrior/app/routes/app_pages.dart';
7+
8+
class DeepLinkService extends GetxService {
9+
late AppLinks _appLinks;
10+
Uri? _queuedUri;
11+
12+
@override
13+
void onReady() {
14+
super.onReady();
15+
_initDeepLinks();
16+
}
17+
18+
void _initDeepLinks() {
19+
_appLinks = AppLinks();
20+
_appLinks.uriLinkStream.listen((uri) {
21+
debugPrint('🔗 LINK RECEIVED: $uri');
22+
_handleWidgetUri(uri);
23+
});
24+
}
25+
26+
void _handleWidgetUri(Uri uri) {
27+
if (Get.isRegistered<HomeController>()) {
28+
_executeAction(uri, Get.find<HomeController>());
29+
} else {
30+
debugPrint("⏳ HomeController not ready. Queuing action.");
31+
_queuedUri = uri;
32+
}
33+
}
34+
35+
void consumePendingActions(HomeController controller) {
36+
if (_queuedUri != null) {
37+
debugPrint("🚀 Executing queued action...");
38+
_executeAction(_queuedUri!, controller);
39+
_queuedUri = null;
40+
}
41+
}
42+
43+
void _executeAction(Uri uri, HomeController controller) {
44+
final bool isTaskChampion = controller.taskchampion.value;
45+
final bool isReplica = controller.taskReplica.value;
46+
47+
if (uri.host == "cardclicked") {
48+
if (uri.queryParameters["uuid"] != null &&
49+
uri.queryParameters["uuid"] != "NO_TASK" &&
50+
!isTaskChampion &&
51+
!isReplica) {
52+
String uuid = uri.queryParameters["uuid"] as String;
53+
Get.toNamed(Routes.DETAIL_ROUTE, arguments: ["uuid", uuid]);
54+
}
55+
} else if (uri.host == "addclicked") {
56+
if (Get.context != null) {
57+
Get.dialog(
58+
Material(
59+
child: AddTaskBottomSheet(
60+
homeController: controller,
61+
forTaskC: isTaskChampion,
62+
forReplica: isReplica,
63+
),
64+
),
65+
);
66+
}
67+
}
68+
}
69+
}

lib/main.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import 'dart:io';
33
import 'package:flutter/foundation.dart';
44
import 'package:flutter/material.dart';
55
import 'package:get/get.dart';
6+
// 1. Add this import
7+
import 'package:app_links/app_links.dart';
8+
import 'package:taskwarrior/app/services/deep_link_service.dart';
9+
610
import 'package:taskwarrior/app/utils/app_settings/app_settings.dart';
711
import 'package:taskwarrior/app/utils/debug_logger/log_databse_helper.dart';
812
import 'package:taskwarrior/app/utils/themes/dark_theme.dart';
@@ -31,12 +35,14 @@ void main() async {
3135
_logDatabaseHelper.insertLog(message);
3236
}
3337
};
38+
3439
loadNativeLibrary();
3540
await RustLib.init();
3641

3742
WidgetsFlutterBinding.ensureInitialized();
3843
await AppSettings.init();
3944

45+
Get.put<DeepLinkService>(DeepLinkService(), permanent: true);
4046
runApp(
4147
GetMaterialApp(
4248
darkTheme: darkTheme,

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ dependencies:
6868
path_provider: ^2.1.5
6969
flutter_rust_bridge: ^2.11.1
7070
ffi: any # Required for FFI
71+
app_links: ^6.4.1
7172

7273
dev_dependencies:
7374
build_runner: null

0 commit comments

Comments
 (0)