From 5f08c6b53ab4373ff4fcad0381957d07d549ae75 Mon Sep 17 00:00:00 2001 From: Ralf Weinbrecher Date: Tue, 27 Aug 2024 17:46:22 +0200 Subject: [PATCH] feat(ui): Scroll list view to today --- CHANGELOG.md | 7 + README.md | 4 +- example/ios/Runner/AppDelegate.swift | 2 +- example/lib/main.dart | 2 +- .../Flutter/GeneratedPluginRegistrant.swift | 6 - example/macos/Podfile.lock | 20 +-- .../macos/Runner.xcodeproj/project.pbxproj | 18 --- example/macos/Runner/AppDelegate.swift | 2 +- example/pubspec.lock | 74 ++--------- lib/calendar_tile.dart | 1 + lib/flutter_neat_and_clean_calendar.dart | 122 +++++++++++++----- pubspec.lock | 48 ------- pubspec.yaml | 5 +- 13 files changed, 117 insertions(+), 194 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aaa850..38fe92d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.4.10] - 2024-08-27 +* macos_ui removed as dependency +* Issue #84: Events added after initial load are not displayed in the monthly view +* touch target of the today button enlarded +* event list view now scrolls to today (or closest entry to today's date) when today button is hit +* CalendarTiles text doesn't scale with system settings + ## [0.4.9] - 2024-08-14 * Issue #83: Updating the internal data possible again diff --git a/README.md b/README.md index ad9d2d5..8d5241e 100644 --- a/README.md +++ b/README.md @@ -465,10 +465,10 @@ final bool showEvents; ``` This is a `bool`. This parameter allows you to decide, if the calender shows a list of events. Default value is set to `true`, but you can hide the events entirely by setting it to `false`. -#### `showEventListView` +#### `forceEventListView` ```dart -final bool showEventListView; +final bool forceEventListView; ```` With this `bool` parameter you can switch the view to a list of events only. If set to `true` the calendar grid gets hidden. diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 70693e4..b636303 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/example/lib/main.dart b/example/lib/main.dart index f414513..929a173 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -191,7 +191,7 @@ class _CalendarScreenState extends State { datePickerType: DatePickerType.date, dayOfWeekStyle: TextStyle( color: Colors.red, fontWeight: FontWeight.w800, fontSize: 11), - showEventListViewIcon: false, + showEventListViewIcon: true, showEvents: showEvents, ), ), diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index ed687ee..cccf817 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,6 @@ import FlutterMacOS import Foundation -import appkit_ui_element_colors -import macos_ui -import macos_window_utils func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - AppkitUiElementColorsPlugin.register(with: registry.registrar(forPlugin: "AppkitUiElementColorsPlugin")) - MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin")) - MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) } diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 3628437..b52ecd3 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -1,34 +1,16 @@ PODS: - - appkit_ui_element_colors (1.0.0): - - FlutterMacOS - FlutterMacOS (1.0.0) - - macos_ui (0.1.0): - - FlutterMacOS - - macos_window_utils (1.0.0): - - FlutterMacOS DEPENDENCIES: - - appkit_ui_element_colors (from `Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - - macos_ui (from `Flutter/ephemeral/.symlinks/plugins/macos_ui/macos`) - - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) EXTERNAL SOURCES: - appkit_ui_element_colors: - :path: Flutter/ephemeral/.symlinks/plugins/appkit_ui_element_colors/macos FlutterMacOS: :path: Flutter/ephemeral - macos_ui: - :path: Flutter/ephemeral/.symlinks/plugins/macos_ui/macos - macos_window_utils: - :path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos SPEC CHECKSUMS: - appkit_ui_element_colors: 39bb2d80be3f19b152ccf4c70d5bbe6cba43d74a FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - macos_ui: 6229a8922cd97bafb7d9636c8eb8dfb0744183ca - macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 PODFILE CHECKSUM: 505596d150d38022472859d890f709281982e016 -COCOAPODS: 1.13.0 +COCOAPODS: 1.15.2 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 5ee2336..3490c5c 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -184,7 +184,6 @@ 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - E5ED0699756C4EAF38EEC7A3 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -314,23 +313,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - E5ED0699756C4EAF38EEC7A3 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift index d53ef64..8e02df2 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/example/macos/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true diff --git a/example/pubspec.lock b/example/pubspec.lock index d2a608f..e510bcb 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,14 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - appkit_ui_element_colors: - dependency: transitive - description: - name: appkit_ui_element_colors - sha256: c3e50f900aae314d339de489535736238627071457c4a4a2dbbb1545b4f04f22 - url: "https://pub.dev" - source: hosted - version: "1.0.0" async: dependency: transitive description: @@ -57,14 +49,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - equatable: - dependency: transitive - description: - name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 - url: "https://pub.dev" - source: hosted - version: "2.0.5" fake_async: dependency: transitive description: @@ -89,7 +73,7 @@ packages: path: ".." relative: true source: path - version: "0.4.7" + version: "0.4.10" flutter_platform_widgets: dependency: transitive description: @@ -103,14 +87,6 @@ packages: description: flutter source: sdk version: "0.0.0" - gradient_borders: - dependency: transitive - description: - name: gradient_borders - sha256: "69eeaff519d145a4c6c213ada1abae386bcc8981a4970d923e478ce7ba19e309" - url: "https://pub.dev" - source: hosted - version: "1.0.0" intl: dependency: transitive description: @@ -123,18 +99,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -143,22 +119,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" - macos_ui: - dependency: transitive - description: - name: macos_ui - sha256: "91c7f3427f763fd96b65831342b896b18751140e6bf55f8572fcb41f7b30bcab" - url: "https://pub.dev" - source: hosted - version: "2.0.7" - macos_window_utils: - dependency: transitive - description: - name: macos_window_utils - sha256: "230be594d26f6dee92c5a1544f4242d25138a5bfb9f185b27f14de3949ef0be8" - url: "https://pub.dev" - source: hosted - version: "1.5.0" matcher: dependency: transitive description: @@ -171,18 +131,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -191,14 +151,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" sky_engine: dependency: transitive description: flutter @@ -248,10 +200,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" vector_math: dependency: transitive description: @@ -264,10 +216,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" sdks: dart: ">=3.3.0 <4.0.0" flutter: ">=3.19.0" diff --git a/lib/calendar_tile.dart b/lib/calendar_tile.dart index 850c699..e7d5826 100644 --- a/lib/calendar_tile.dart +++ b/lib/calendar_tile.dart @@ -154,6 +154,7 @@ class NeatCleanCalendarTile extends StatelessWidget { ? defaultOutOfMonthDayColor : Colors.grey), ), + textScaler: TextScaler.noScaling, // Grey color for previous or next months dates ), // Dots for the events diff --git a/lib/flutter_neat_and_clean_calendar.dart b/lib/flutter_neat_and_clean_calendar.dart index 91a58ce..ce01739 100644 --- a/lib/flutter_neat_and_clean_calendar.dart +++ b/lib/flutter_neat_and_clean_calendar.dart @@ -146,7 +146,7 @@ class Calendar extends StatefulWidget { final DatePickerConfig? datePickerConfig; final double? eventTileHeight; final bool showEvents; - final bool showEventListView; + final bool forceEventListView; final bool showEventListViewIcon; /// Configures the date picker if enabled @@ -195,7 +195,7 @@ class Calendar extends StatefulWidget { this.displayMonthTextStyle, this.datePickerConfig, this.eventTileHeight, - this.showEventListView = false, + this.forceEventListView = false, this.showEventListViewIcon = true, this.showEvents = true}); @@ -212,11 +212,13 @@ class _CalendarState extends State { DateTime _selectedDate = DateTime.now(); String? currentMonth; late bool isExpanded; - late bool showEventListView; + late bool forceEventListView; String displayMonth = ''; DateTime get selectedDate => _selectedDate; List? _selectedEvents; bool isDarkMode = false; + late ScrollController _scrollController; + late List itemList = []; @override void initState() { @@ -224,11 +226,7 @@ class _CalendarState extends State { isExpanded = widget.isExpanded; - if (widget.showEventListViewIcon == true) { - showEventListView = widget.showEventListView; - } else { - showEventListView = false; - } + forceEventListView = widget.forceEventListView; _selectedDate = widget.initialDate ?? DateTime.now(); initializeDateFormatting(widget.locale, null).then((_) => setState(() { @@ -244,7 +242,7 @@ class _CalendarState extends State { // to the calendar, the eventsmap must be created from the list. This is done in the // _updateEventsMap method. _updateEventsMap(); - + _scrollController = ScrollController(); } @override @@ -261,6 +259,57 @@ class _CalendarState extends State { } } + void scrollToTop() { + // Only scroll to top if the event list view is shown + if (forceEventListView == false) return; + _scrollController.animateTo( + 0.0, // Die Scroll-Position (0.0 ist ganz oben) + duration: Duration(milliseconds: 300), // Dauer der Animation + curve: Curves.easeInOut, // Animationseffekt + ); + } + + void scrollToIndex(int index) { + // Only scroll to top if the event list view is shown + if (forceEventListView == false) return; + double position = + index * 60.0; // Annahme: Höhe eines Listenelements = 60.0 Pixel + + _scrollController.animateTo( + position, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + + int findClosestIndex(DateTime currentDate) { + int closestIndex = -1; + int minDifference = double.maxFinite.toInt(); // Eine sehr große Zahl + + for (int i = 0; i < itemList.length; i++) { + if (itemList[i] is DateTime) { + DateTime eventDate = itemList[i] as DateTime; + + // Berechne die Differenz in Tagen zwischen dem Event-Datum und dem aktuellen Datum + int difference = (eventDate.difference(currentDate).inDays).abs(); + + if (difference < minDifference) { + minDifference = difference; + closestIndex = i; + } + + // Wenn das Event genau am aktuellen Datum stattfindet, direkt zurückgeben + if (eventDate.year == currentDate.year && + eventDate.month == currentDate.month && + eventDate.day == currentDate.day) { + return i; + } + } + } + + return closestIndex; + } + /// The method [_updateEventsMap] has the purpose to update the eventsMap, when the calendar widget /// is initiated. When this method executes, it fills the eventsMap with the contents of the /// given eventsList. This can be used to update the events shown by the calendar. @@ -393,11 +442,8 @@ class _CalendarState extends State { } if (!widget.hideTodayIcon) { - todayIcon = GestureDetector( - child: Text(widget.todayButtonText, - style: widget.displayMonthTextStyle ?? null), - onTap: resetToToday, - ); + todayIcon = Text(widget.todayButtonText, + style: widget.displayMonthTextStyle ?? null); } else { todayIcon = Container(); } @@ -504,14 +550,14 @@ class _CalendarState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - showEventListView ? Container() : leftArrow ?? Container(), + forceEventListView ? Container() : leftArrow ?? Container(), widget.showEventListViewIcon ? PlatformIconButton( onPressed: () { setState(() { - showEventListView = !showEventListView; + forceEventListView = !forceEventListView; if (widget.onListViewStateChanged != null) { - widget.onListViewStateChanged!(showEventListView); + widget.onListViewStateChanged!(forceEventListView); } }); }, @@ -524,21 +570,24 @@ class _CalendarState extends State { Expanded( child: Container()), // Platzhalter, damit die Row ausgeglichen ist - showEventListView ? Container() : jumpDateIcon ?? Container(), - showEventListView ? Container() : rightArrow ?? Container(), + forceEventListView ? Container() : jumpDateIcon ?? Container(), + forceEventListView ? Container() : rightArrow ?? Container(), ], ), // Zentralisiertes Stack-Widget - Column(children: [ - if (todayIcon != null) todayIcon!, - Text( - displayMonth, - style: widget.displayMonthTextStyle ?? - TextStyle( - fontSize: 20.0, - ), - ), - ]), + GestureDetector( + child: Column(children: [ + if (todayIcon != null) todayIcon!, + Text( + displayMonth, + style: widget.displayMonthTextStyle ?? + TextStyle( + fontSize: 20.0, + ), + ), + ]), + onTap: resetToToday, + ), ], ); } @@ -814,7 +863,7 @@ class _CalendarState extends State { mainAxisSize: MainAxisSize.min, children: [ nameAndIconRow, - if (showEventListView) + if (forceEventListView) eventlistView else ...[ ExpansionCrossFade( @@ -845,14 +894,14 @@ class _CalendarState extends State { // If the list view is active, show the full list of events. Otherwise only the // events for the selected day are shown. List? _listEvents = - showEventListView ? widget.eventsList : _selectedEvents; + forceEventListView ? widget.eventsList : _selectedEvents; // If eventListBuilder is provided, use it to build the list of events to show. // Otherwise use the default list of events. if (widget.eventListBuilder == null) { - if (showEventListView == true) { + if (forceEventListView == true) { // If the list view is active a different kind of list is shown. - final List itemList = []; + itemList = []; eventsMap!.forEach((date, events) { itemList.add(date); // Füge das Datum hinzu @@ -861,6 +910,7 @@ class _CalendarState extends State { return Expanded( child: ListView.builder( + controller: _scrollController, itemCount: itemList.length, itemBuilder: (BuildContext context, int index) { final item = itemList[index]; @@ -1018,6 +1068,12 @@ class _CalendarState extends State { /// position of the screen. It re-caclulates the range of dates, so that the /// month view or week view changes to a range containing the current day. void resetToToday() { + int index = findClosestIndex(DateTime.now()); + if (index != -1) { + scrollToIndex(index); + } else { + scrollToTop(); + } onJumpToDateSelected(DateTime.now()); } diff --git a/pubspec.lock b/pubspec.lock index 26b5891..d0f728a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,14 +1,6 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - appkit_ui_element_colors: - dependency: transitive - description: - name: appkit_ui_element_colors - sha256: c3e50f900aae314d339de489535736238627071457c4a4a2dbbb1545b4f04f22 - url: "https://pub.dev" - source: hosted - version: "1.0.0" async: dependency: transitive description: @@ -57,14 +49,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - equatable: - dependency: transitive - description: - name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 - url: "https://pub.dev" - source: hosted - version: "2.0.5" fake_async: dependency: transitive description: @@ -91,14 +75,6 @@ packages: description: flutter source: sdk version: "0.0.0" - gradient_borders: - dependency: transitive - description: - name: gradient_borders - sha256: "69eeaff519d145a4c6c213ada1abae386bcc8981a4970d923e478ce7ba19e309" - url: "https://pub.dev" - source: hosted - version: "1.0.0" intl: dependency: "direct main" description: @@ -131,22 +107,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" - macos_ui: - dependency: "direct main" - description: - name: macos_ui - sha256: "91c7f3427f763fd96b65831342b896b18751140e6bf55f8572fcb41f7b30bcab" - url: "https://pub.dev" - source: hosted - version: "2.0.7" - macos_window_utils: - dependency: transitive - description: - name: macos_window_utils - sha256: "230be594d26f6dee92c5a1544f4242d25138a5bfb9f185b27f14de3949ef0be8" - url: "https://pub.dev" - source: hosted - version: "1.5.0" matcher: dependency: transitive description: @@ -179,14 +139,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index b46a149..b71fc81 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: >- Simple and clean flutter calendar with ability to slide up/down to show weekly/monthly calendar. Fork of https://pub.dev/packages/flutter_clean_calendar -version: 0.4.9 +version: 0.4.10 homepage: https://github.com/rwbr/flutter_neat_and_clean_calendar environment: @@ -14,14 +14,11 @@ dependencies: sdk: flutter flutter_platform_widgets: ^7.0.0 intl: ^0.19.0 - macos_ui: ^2.0.7 dev_dependencies: flutter_test: sdk: flutter -flutter: null - platforms: ios: android: