Skip to content

Update dependencies and improve notification functionality - #5

Merged
Merack merged 3 commits into
mainfrom
dev
Aug 3, 2026
Merged

Update dependencies and improve notification functionality#5
Merack merged 3 commits into
mainfrom
dev

Conversation

@Merack

@Merack Merack commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added event reminder notifications with sound and vibration support.
    • Added per-event notification controls and a dedicated notification settings page.
    • Added notification permission status, authorization prompts, and links to system settings.
    • Event notifications now accompany configured timer audio alerts.
  • Improvements
    • Combined sound and notification options into one settings section.
    • Improved permission displays for storage and battery optimization, including guidance for battery settings.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The application adds Android event reminder notifications with persisted per-event settings. It adds notification permission handling, a dedicated settings page, route wiring, shared permission widgets, and timer event integration.

Changes

Event Reminder Notifications

Layer / File(s) Summary
Notification contracts and service
lib/config/storage_keys.dart, lib/service/notification_service.dart, lib/main.dart, pubspec.yaml, .gitignore
Notification channels, event metadata, preference keys, service initialization, delivery guards, and the updated notification dependency are added.
Timer event notification delivery
lib/page/home/controller.dart
Timer event handlers continue audio playback and asynchronously send enabled event notifications.
Notification settings and permission UI
lib/page/notification_settings/*, lib/page/setting/view.dart, lib/page/setting/widgets/*, lib/route/*
A notification settings page, permission actions, event toggles, route registration, and shared permission rendering are added. The existing permission page now focuses on storage and battery permissions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

I’m a rabbit watching timers glow,
With tiny alerts that hop and show.
Channels ring and switches sway,
Permissions guide the way.
Audio stays, notifications play.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the dependency update and the main notification functionality changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
lib/service/notification_service.dart (1)

26-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider guarding init with a platform check.

_createEventsChannel and showEventNotification both return early on non-Android platforms, but initialize runs everywhere. InitializationSettings supplies only android, and the plugin raises an error when the settings for the target platform are missing. The catch absorbs that error, so the failure surfaces only in the log. An explicit guard makes the Android-only scope of this service consistent.

♻️ Proposed platform guard
   Future<NotificationService> init() async {
     _storage = Get.find<AppStorageService>().mmkv;
 
+    if (!Platform.isAndroid) {
+      Get.log('NotificationService: 非 Android 平台, 跳过初始化');
+      return this;
+    }
+
     try {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/service/notification_service.dart` around lines 26 - 44, Update
NotificationService.init to return early when the current platform is not
Android, before creating InitializationSettings or calling _plugin.initialize.
Keep the existing Android initialization flow, event-channel setup, and success
state unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/service/notification_service.dart`:
- Around line 85-99: Wrap the entire body of showEventNotification, including
the permission check and StorageKeys.notificationIds lookup, in the existing try
block so all failures are contained and skipped silently. Keep the current
early-return behavior for uninitialized, non-Android, disabled,
ungranted-permission, and unknown-event cases.

In `@pubspec.yaml`:
- Line 40: Update the project SDK constraints around the
flutter_local_notifications dependency to require Flutter >=3.27.0, and ensure
workflow Flutter versions satisfy that floor; alternatively, pin all workflow
Flutter versions to a compatible release. Keep the 22.1.0 dependency unchanged.

---

Nitpick comments:
In `@lib/service/notification_service.dart`:
- Around line 26-44: Update NotificationService.init to return early when the
current platform is not Android, before creating InitializationSettings or
calling _plugin.initialize. Keep the existing Android initialization flow,
event-channel setup, and success state unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ce21e97a-e100-4e99-847a-47d226bd9f57

📥 Commits

Reviewing files that changed from the base of the PR and between 71baefd and b18a39d.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .gitignore
  • lib/config/storage_keys.dart
  • lib/main.dart
  • lib/page/home/controller.dart
  • lib/page/notification_settings/controller.dart
  • lib/page/notification_settings/state.dart
  • lib/page/notification_settings/view.dart
  • lib/page/setting/view.dart
  • lib/page/setting/widgets/permission_row.dart
  • lib/page/setting/widgets/permission_settings.dart
  • lib/page/setting/widgets/widgets.dart
  • lib/route/route_name.dart
  • lib/route/route_page.dart
  • lib/service/notification_service.dart
  • pubspec.yaml

Comment on lines +85 to +99
Future<void> showEventNotification(String eventId) async {
if (!_initialized || !Platform.isAndroid) return;
if (!isEventEnabled(eventId)) return;

// 用户可能在设置里开了开关但系统层面没给权限, 这里兜一层
if (!await Permission.notification.isGranted) {
Get.log('通知权限未授予, 跳过事件通知: $eventId');
return;
}

final id = StorageKeys.notificationIds[eventId];
if (id == null) {
Get.log('未知的通知事件: $eventId');
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Move the permission check inside the try block.

The doc comment states that failures are skipped silently. Line 90 awaits Permission.notification.isGranted outside the try, and the map lookups also sit outside it. The caller in lib/page/home/controller.dart at line 520 does not await this future and attaches no error handler. If the permission check throws, the result is an unhandled async error rather than a silent skip. Wrap the whole body so every failure path is contained.

🛡️ Proposed fix
   Future<void> showEventNotification(String eventId) async {
     if (!_initialized || !Platform.isAndroid) return;
     if (!isEventEnabled(eventId)) return;
 
-    // 用户可能在设置里开了开关但系统层面没给权限, 这里兜一层
-    if (!await Permission.notification.isGranted) {
-      Get.log('通知权限未授予, 跳过事件通知: $eventId');
-      return;
-    }
-
-    final id = StorageKeys.notificationIds[eventId];
-    if (id == null) {
-      Get.log('未知的通知事件: $eventId');
-      return;
-    }
-
     const androidDetails = AndroidNotificationDetails(
       StorageKeys.notificationChannelEvents,
       StorageKeys.notificationChannelEventsName,
       channelDescription: StorageKeys.notificationChannelEventsDesc,
       importance: Importance.high,
       priority: Priority.high,
       // 点击后自动消失, 用户不需要手动清
       autoCancel: true,
     );
 
     try {
+      // 用户可能在设置里开了开关但系统层面没给权限, 这里兜一层
+      if (!await Permission.notification.isGranted) {
+        Get.log('通知权限未授予, 跳过事件通知: $eventId');
+        return;
+      }
+
+      final id = StorageKeys.notificationIds[eventId];
+      if (id == null) {
+        Get.log('未知的通知事件: $eventId');
+        return;
+      }
+
       await _plugin.show(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/service/notification_service.dart` around lines 85 - 99, Wrap the entire
body of showEventNotification, including the permission check and
StorageKeys.notificationIds lookup, in the existing try block so all failures
are contained and skipped silently. Keep the current early-return behavior for
uninitialized, non-Android, disabled, ungranted-permission, and unknown-event
cases.

Comment thread pubspec.yaml
path: ^1.9.1
flutter_background_service: ^5.1.0
flutter_local_notifications: ^21.0.0
flutter_local_notifications: ^22.1.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Declared SDK constraints and the resolved plugin version.
fd -t f 'pubspec.yaml' -x rg -n 'sdk:|flutter:|flutter_local_notifications' {}
fd -t f 'pubspec.lock' -x rg -n -A3 'flutter_local_notifications' {}

# Android compileSdk / minSdk settings.
fd -t f -e gradle -e kts . android 2>/dev/null | while IFS= read -r f; do
  rg -n 'compileSdk|minSdk|targetSdk' "$f" || true
done

# Published versions of the plugin.
curl -s https://pub.dev/api/packages/flutter_local_notifications | jq -r '.latest.version, (.versions[-8:][].version)'

Repository: Merack/time_machine

Length of output: 1999


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## pubspec.yaml relevant sections"
sed -n '1,55p' pubspec.yaml

echo
echo "## pubspec.lock flutter_local_notifications full block"
python3 - <<'PY'
from pathlib import Path
text=Path('pubspec.lock').read_text()
start=text.index('flutter_local_notifications:')
end=text.index('\n  flutter_local_notifications_linux:', start)+1
print(path)
PY
sed -n '285,298p' pubspec.lock

echo
echo "## android build.gradle relevant sections"
fd -t f -e gradle -e kts . android 2>/dev/null | while IFS= read -r f; do
  echo "--- $f"
  rg -n -C 3 'compileSdk|minSdk|targetSdk|compileOptions|kotlinOptions|compileSdkVersion|minSdkVersion|targetSdkVersion' "$f" || true
done

echo
echo "## CI Flutter SDK/config references"
git ls-files | rg '(^\.github/workflows/.*\.ya?ml$|(^|/)(pubspec.yaml|analysis_options.yaml)$)' | while IFS= read -r f; do
  echo "--- $f"
  rg -n 'flutter:|sdk:|uses:.*flutter|fvm|flutter-build|flutter test|flutter pub|environment\.sdk|flutter_local_notifications' "$f" || true
done

Repository: Merack/time_machine

Length of output: 2692


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## pubspec.lock flutter_local_notifications lock block"
sed -n '285,298p' pubspec.lock

echo
echo "## android gradle files compile/min/target settings"
for f in android/app/build.gradle android/build.gradle $(fd -t f -e gradle . android); do
  [ -f "$f" ] || continue
  echo "--- $f"
  rg -n -C 3 'compileSdk|minSdk|targetSdk|compileSdkVersion|minSdkVersion|targetSdkVersion|compileOptions|kotlinOptions' "$f" || true
done

echo
echo "## CI/flutter config references"
for f in $(git ls-files | rg '(^(\.github/workflows/.*\.ya?ml|pubspec.yaml|analysis_options.yaml)$)'); do
  echo "--- $f"
  rg -n 'flutter:|sdk:|uses:.*flutter|fvm|pubspec.yaml|flutter test|flutter build|environment\.sdk|flutter_local_notifications' "$f" || true
done

echo
echo "## package manifest floor for resolved version from pub"
python3 - <<'PY'
import json, urllib.request
url='https://pub.flutter-io.cn/api/packages/flutter_local_notifications/versions/22.1.0'
try:
    with urllib.request.urlopen(url, timeout=20) as r:
        data=json.load(r)
except Exception as e:
    print("FETCH_ERROR", repr(e))
    raise SystemExit
print("version:", data.get('version'))
print("sdk:", data.get('environment', {}).get('sdk'))
print("flutter:", data.get('environment', {}).get('flutter'))
print("dependency_platforms:", data.get('dependency', {}).get('dependency_platforms'))
PY

Repository: Merack/time_machine

Length of output: 1471


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## android/build.gradle"
sed -n '1,120p' android/build.gradle

echo
echo "## android/app/build.gradle"
sed -n '1,140p' android/app/build.gradle

echo
echo "## workflow versions"
sed -n '1,90p' .github/workflows/dev.yml
sed -n '1,90p' .github/workflows/release.yml

echo
echo "## try fetching platform manifest with SSL bypass"
python3 - <<'PY'
import json, urllib.request, ssl
url='https://pub.dev/api/packages/flutter_local_notifications/versions/22.1.0'
ctx=ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None
req=urllib.request.Request(url, headers={'User-Agent': 'CodeRabbit-verifier'})
try:
    with urllib.request.urlopen(req, timeout=20, context=ctx) as r:
        data=json.load(r)
    print('version:', data.get('version'))
    print('sdk:', data.get('environment', {}).get('sdk'))
    print('flutter:', data.get('environment', {}).get('flutter'))
except Exception as e:
    print('FETCH_ERROR', repr(e))
PY

echo
echo "## check lock hash against dev API"
python3 - <<'PY'
import json, urllib.request
sha='40c6a69189a622bda89ddcf50a139f4ba0f0eb9c0fef6718845b1f8b95452ed6'
url=f'https://pub.dev/api/packages/flutter_local_notifications/versions/22.1.0'
for ctx in [urllib.request.SSLContext(urllib.request.SSLContext.PROTOCOL_TLS), None]:
    try:
        req=urllib.request.Request(url, headers={'User-Agent': 'CodeRabbit-verifier'})
        with urllib.request.urlopen(req, timeout=20, context=ctx or None) as r:
            data=json.load(r)
        print("fetch_ok", data.get('version'), data.get('sha256'))
        print("matched", data.get('sha256') == sha)
        break
    except Exception as e:
        print("try_error", type(e).__name__, e)
PY

Repository: Merack/time_machine

Length of output: 245


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## locate gradle files"
find android -maxdepth 4 -type f \( -name '*.gradle' -o -name '*.gradle.kts' \) -print | sort

echo
echo "## read all gradle files"
while IFS= read -r f; do
  echo "--- $f"
  sed -n '1,160p' "$f"
done < <(find android -maxdepth 4 -type f \( -name '*.gradle' -o -name '*.gradle.kts' \) -print | sort)

echo
echo "## workflow versions"
for f in .github/workflows/dev.yml .github/workflows/release.yml; do
  [ -f "$f" ] || continue
  echo "--- $f"
  sed -n '1,90p' "$f"
done

echo
echo "## fetch 22.1.0 platform metadata"
python3 - <<'PY'
import json, ssl, urllib.request
url='https://pub.dev/api/packages/flutter_local_notifications/versions/22.1.0'
headers={'User-Agent': 'CodeRabbit-verifier'}
ctx=ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None
for retry_ctx in [ctx, None]:
    try:
        req=urllib.request.Request(url, headers=headers, method='GET')
        with urllib.request.urlopen(req, timeout=20, context=retry_ctx) as r:
            data=json.load(r)
        print('version:', data.get('version'))
        print('sha256:', data.get('sha256'))
        print('android compileSdk:', data.get('dependency', {}).get('dependency_platforms', {}).get('android', {}).get('min_sdk', {}).get('compile_sdk'))
        print('environment:', data.get('environment', {}))
        break
    except Exception as e:
        print('try:', type(e).__name__, str(e))
PY

Repository: Merack/time_machine

Length of output: 341


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## android gradle files"
for f in android/build.gradle.kts android/app/build.gradle.kts android/settings.gradle.kts; do
  [ -f "$f" ] || continue
  echo "--- $f"
  sed -n '1,180p' "$f"
done

echo
echo "## workflow versions"
for f in .github/workflows/dev.yml .github/workflows/release.yml; do
  [ -f "$f" ] || continue
  echo "--- $f"
  sed -n '1,90p' "$f"
done

echo
echo "## fetch 22.1.0 platform metadata"
python3 - <<'PY'
import json, ssl, urllib.request
url='https://pub.dev/api/packages/flutter_local_notifications/versions/22.1.0'
headers={'User-Agent': 'CodeRabbit-verifier'}
ctx=ssl._create_unverified_context() if hasattr(ssl, '_create_unverified_context') else None
for retry_ctx in [ctx, None]:
    try:
        req=urllib.request.Request(url, headers=headers, method='GET')
        with urllib.request.urlopen(req, timeout=20, context=retry_ctx) as r:
            data=json.load(r)
        print('version:', data.get('version'))
        print('sha256:', data.get('sha256'))
        print('android compileSdk:', data.get('dependency', {}).get('dependency_platforms', {}).get('android', {}).get('min_sdk', {}).get('compile_sdk'))
        print('environment:', data.get('environment', {}))
        break
    except Exception as e:
        print('try:', type(e).__name__, str(e))
PY

Repository: Merack/time_machine

Length of output: 10258


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## try pub.dev package summary without custom context"
python3 - <<'PY'
import json, urllib.request
url='https://pub.dev/api/packages/flutter_local_notifications'
req=urllib.request.Request(url, headers={'User-Agent': 'CodeRabbit-verifier'})
with urllib.request.urlopen(req, timeout=20) as r:
    data=json.load(r)
print("latest", data.get("latest", {}).get("version"))
print("latest_sha256", data.get("latest", {}).get("sha256"))
versions=data.get("versions", [])
for v in versions[-6:]:
    pv=v.get("version")
    try:
        uv=urllib.request.urlopen(f'https://pub.dev/api/packages/flutter_local_notifications/versions/{pv}', timeout=20).read().decode()
        info=json.loads(uv)
        print(pv, "sha256=", info.get("sha256"), "environment=", info.get("environment"))
    except Exception as e:
        print(pv, "error", type(e).__name__, e)
PY

echo
echo "## local lock version/hash"
sed -n '289,295p' pubspec.lock

echo
echo "## workflow sdk pins"
rg -n "flutter-version|channel|java-version|subosito/flutter-action" .github/workflows/*.yml

Repository: Merack/time_machine

Length of output: 2497


Confirm the Flutter SDK floor before using flutter_local_notifications 22.x.

pubspec.lock resolves to the published 22.1.0 stable release, and Android uses Flutter-managed SDK values. However, pubspec.yaml has no Dart SDK floor, and the workflows only specify flutter-version: '3.x', so require an explicit Flutter >=3.27.0 constraint or pin the workflow Flutter version to satisfy the 22.x platform requirements.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pubspec.yaml` at line 40, Update the project SDK constraints around the
flutter_local_notifications dependency to require Flutter >=3.27.0, and ensure
workflow Flutter versions satisfy that floor; alternatively, pin all workflow
Flutter versions to a compatible release. Keep the 22.1.0 dependency unchanged.

@Merack
Merack merged commit 9b64221 into main Aug 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant